From 10bc426bd34ae84b41e489fad0c177b901feeeeb Mon Sep 17 00:00:00 2001 From: John Szakmeister Date: Tue, 17 Jul 2012 12:05:53 -0700 Subject: [PATCH] Introduce end-of-line normalization. --- .gitattributes | 6 + epl-v10.html | 522 +- src/jvm/clojure/asm/AnnotationVisitor.java | 194 +- src/jvm/clojure/asm/AnnotationWriter.java | 714 ++-- src/jvm/clojure/asm/Attribute.java | 506 +- src/jvm/clojure/asm/ByteVector.java | 636 +- src/jvm/clojure/asm/ClassAdapter.java | 230 +- src/jvm/clojure/asm/ClassReader.java | 4448 +++++++------- src/jvm/clojure/asm/ClassVisitor.java | 392 +- src/jvm/clojure/asm/ClassWriter.java | 2830 +++++----- src/jvm/clojure/asm/Edge.java | 150 +- src/jvm/clojure/asm/FieldVisitor.java | 128 +- src/jvm/clojure/asm/FieldWriter.java | 580 +- src/jvm/clojure/asm/Frame.java | 3012 +++++----- src/jvm/clojure/asm/Handler.java | 140 +- src/jvm/clojure/asm/Item.java | 516 +- src/jvm/clojure/asm/Label.java | 874 ++-- src/jvm/clojure/asm/MethodAdapter.java | 372 +- src/jvm/clojure/asm/MethodVisitor.java | 792 ++-- src/jvm/clojure/asm/MethodWriter.java | 6058 ++++++++++---------- src/jvm/clojure/asm/Opcodes.java | 682 ++-- src/jvm/clojure/asm/Type.java | 1744 +++--- src/jvm/clojure/asm/commons/AdviceAdapter.java | 1362 +++--- src/jvm/clojure/asm/commons/AnalyzerAdapter.java | 1876 +++--- src/jvm/clojure/asm/commons/CodeSizeEvaluator.java | 468 +- src/jvm/clojure/asm/commons/EmptyVisitor.java | 442 +- src/jvm/clojure/asm/commons/GeneratorAdapter.java | 3066 +++++----- .../clojure/asm/commons/LocalVariablesSorter.java | 660 ++-- src/jvm/clojure/asm/commons/Method.java | 532 +- .../clojure/asm/commons/SerialVersionUIDAdder.java | 1016 ++-- src/jvm/clojure/asm/commons/StaticInitMerger.java | 204 +- .../clojure/asm/commons/TableSwitchGenerator.java | 110 +- src/jvm/clojure/asm/commons/package.html | 94 +- src/jvm/clojure/asm/package.html | 174 +- src/jvm/clojure/lang/Associative.java | 38 +- src/jvm/clojure/lang/Binding.java | 52 +- src/jvm/clojure/lang/IMapEntry.java | 38 +- src/jvm/clojure/lang/IObj.java | 36 +- src/jvm/clojure/lang/IPersistentCollection.java | 46 +- src/jvm/clojure/lang/IPersistentList.java | 32 +- src/jvm/clojure/lang/IPersistentVector.java | 40 +- src/jvm/clojure/lang/ISeq.java | 56 +- src/jvm/clojure/lang/IndexedSeq.java | 30 +- src/jvm/clojure/lang/IteratorSeq.java | 150 +- .../clojure/lang/LineNumberingPushbackReader.java | 150 +- src/jvm/clojure/lang/MapEntry.java | 80 +- src/jvm/clojure/lang/PersistentQueue.java | 564 +- src/jvm/clojure/lang/Sequential.java | 26 +- 48 files changed, 18437 insertions(+), 18431 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7b89cfa --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +*.txt text +*.clj text +*.html text +*.js text +*.css text +*.java text diff=java diff --git a/epl-v10.html b/epl-v10.html index 813c07d..84ec251 100644 --- a/epl-v10.html +++ b/epl-v10.html @@ -1,261 +1,261 @@ - - - - - - -Eclipse Public License - Version 1.0 - - - - - - -

Eclipse Public License - v 1.0

- -

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE -PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR -DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS -AGREEMENT.

- -

1. DEFINITIONS

- -

"Contribution" means:

- -

a) in the case of the initial Contributor, the initial -code and documentation distributed under this Agreement, and

-

b) in the case of each subsequent Contributor:

-

i) changes to the Program, and

-

ii) additions to the Program;

-

where such changes and/or additions to the Program -originate from and are distributed by that particular Contributor. A -Contribution 'originates' from a Contributor if it was added to the -Program by such Contributor itself or anyone acting on such -Contributor's behalf. Contributions do not include additions to the -Program which: (i) are separate modules of software distributed in -conjunction with the Program under their own license agreement, and (ii) -are not derivative works of the Program.

- -

"Contributor" means any person or entity that distributes -the Program.

- -

"Licensed Patents" mean patent claims licensable by a -Contributor which are necessarily infringed by the use or sale of its -Contribution alone or when combined with the Program.

- -

"Program" means the Contributions distributed in accordance -with this Agreement.

- -

"Recipient" means anyone who receives the Program under -this Agreement, including all Contributors.

- -

2. GRANT OF RIGHTS

- -

a) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free copyright license to reproduce, prepare derivative works -of, publicly display, publicly perform, distribute and sublicense the -Contribution of such Contributor, if any, and such derivative works, in -source code and object code form.

- -

b) Subject to the terms of this Agreement, each -Contributor hereby grants Recipient a non-exclusive, worldwide, -royalty-free patent license under Licensed Patents to make, use, sell, -offer to sell, import and otherwise transfer the Contribution of such -Contributor, if any, in source code and object code form. This patent -license shall apply to the combination of the Contribution and the -Program if, at the time the Contribution is added by the Contributor, -such addition of the Contribution causes such combination to be covered -by the Licensed Patents. The patent license shall not apply to any other -combinations which include the Contribution. No hardware per se is -licensed hereunder.

- -

c) Recipient understands that although each Contributor -grants the licenses to its Contributions set forth herein, no assurances -are provided by any Contributor that the Program does not infringe the -patent or other intellectual property rights of any other entity. Each -Contributor disclaims any liability to Recipient for claims brought by -any other entity based on infringement of intellectual property rights -or otherwise. As a condition to exercising the rights and licenses -granted hereunder, each Recipient hereby assumes sole responsibility to -secure any other intellectual property rights needed, if any. For -example, if a third party patent license is required to allow Recipient -to distribute the Program, it is Recipient's responsibility to acquire -that license before distributing the Program.

- -

d) Each Contributor represents that to its knowledge it -has sufficient copyright rights in its Contribution, if any, to grant -the copyright license set forth in this Agreement.

- -

3. REQUIREMENTS

- -

A Contributor may choose to distribute the Program in object code -form under its own license agreement, provided that:

- -

a) it complies with the terms and conditions of this -Agreement; and

- -

b) its license agreement:

- -

i) effectively disclaims on behalf of all Contributors -all warranties and conditions, express and implied, including warranties -or conditions of title and non-infringement, and implied warranties or -conditions of merchantability and fitness for a particular purpose;

- -

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

- -

iii) states that any provisions which differ from this -Agreement are offered by that Contributor alone and not by any other -party; and

- -

iv) states that source code for the Program is available -from such Contributor, and informs licensees how to obtain it in a -reasonable manner on or through a medium customarily used for software -exchange.

- -

When the Program is made available in source code form:

- -

a) it must be made available under this Agreement; and

- -

b) a copy of this Agreement must be included with each -copy of the Program.

- -

Contributors may not remove or alter any copyright notices contained -within the Program.

- -

Each Contributor must identify itself as the originator of its -Contribution, if any, in a manner that reasonably allows subsequent -Recipients to identify the originator of the Contribution.

- -

4. COMMERCIAL DISTRIBUTION

- -

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

- -

For example, a Contributor might include the Program in a commercial -product offering, Product X. That Contributor is then a Commercial -Contributor. If that Commercial Contributor then makes performance -claims, or offers warranties related to Product X, those performance -claims and warranties are such Commercial Contributor's responsibility -alone. Under this section, the Commercial Contributor would have to -defend claims against the other Contributors related to those -performance claims and warranties, and if a court requires any other -Contributor to pay any damages as a result, the Commercial Contributor -must pay those damages.

- -

5. NO WARRANTY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS -PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS -OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, -ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY -OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely -responsible for determining the appropriateness of using and -distributing the Program and assumes all risks associated with its -exercise of rights under this Agreement , including but not limited to -the risks and costs of program errors, compliance with applicable laws, -damage to or loss of data, programs or equipment, and unavailability or -interruption of operations.

- -

6. DISCLAIMER OF LIABILITY

- -

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT -NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING -WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR -DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

- -

7. GENERAL

- -

If any provision of this Agreement is invalid or unenforceable under -applicable law, it shall not affect the validity or enforceability of -the remainder of the terms of this Agreement, and without further action -by the parties hereto, such provision shall be reformed to the minimum -extent necessary to make such provision valid and enforceable.

- -

If Recipient institutes patent litigation against any entity -(including a cross-claim or counterclaim in a lawsuit) alleging that the -Program itself (excluding combinations of the Program with other -software or hardware) infringes such Recipient's patent(s), then such -Recipient's rights granted under Section 2(b) shall terminate as of the -date such litigation is filed.

- -

All Recipient's rights under this Agreement shall terminate if it -fails to comply with any of the material terms or conditions of this -Agreement and does not cure such failure in a reasonable period of time -after becoming aware of such noncompliance. If all Recipient's rights -under this Agreement terminate, Recipient agrees to cease use and -distribution of the Program as soon as reasonably practicable. However, -Recipient's obligations under this Agreement and any licenses granted by -Recipient relating to the Program shall continue and survive.

- -

Everyone is permitted to copy and distribute copies of this -Agreement, but in order to avoid inconsistency the Agreement is -copyrighted and may only be modified in the following manner. The -Agreement Steward reserves the right to publish new versions (including -revisions) of this Agreement from time to time. No one other than the -Agreement Steward has the right to modify this Agreement. The Eclipse -Foundation is the initial Agreement Steward. The Eclipse Foundation may -assign the responsibility to serve as the Agreement Steward to a -suitable separate entity. Each new version of the Agreement will be -given a distinguishing version number. The Program (including -Contributions) may always be distributed subject to the version of the -Agreement under which it was received. In addition, after a new version -of the Agreement is published, Contributor may elect to distribute the -Program (including its Contributions) under the new version. Except as -expressly stated in Sections 2(a) and 2(b) above, Recipient receives no -rights or licenses to the intellectual property of any Contributor under -this Agreement, whether expressly, by implication, estoppel or -otherwise. All rights in the Program not expressly granted under this -Agreement are reserved.

- -

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

- - - - + + + + + + +Eclipse Public License - Version 1.0 + + + + + + +

Eclipse Public License - v 1.0

+ +

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE +PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR +DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS +AGREEMENT.

+ +

1. DEFINITIONS

+ +

"Contribution" means:

+ +

a) in the case of the initial Contributor, the initial +code and documentation distributed under this Agreement, and

+

b) in the case of each subsequent Contributor:

+

i) changes to the Program, and

+

ii) additions to the Program;

+

where such changes and/or additions to the Program +originate from and are distributed by that particular Contributor. A +Contribution 'originates' from a Contributor if it was added to the +Program by such Contributor itself or anyone acting on such +Contributor's behalf. Contributions do not include additions to the +Program which: (i) are separate modules of software distributed in +conjunction with the Program under their own license agreement, and (ii) +are not derivative works of the Program.

+ +

"Contributor" means any person or entity that distributes +the Program.

+ +

"Licensed Patents" mean patent claims licensable by a +Contributor which are necessarily infringed by the use or sale of its +Contribution alone or when combined with the Program.

+ +

"Program" means the Contributions distributed in accordance +with this Agreement.

+ +

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

+ +

2. GRANT OF RIGHTS

+ +

a) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free copyright license to reproduce, prepare derivative works +of, publicly display, publicly perform, distribute and sublicense the +Contribution of such Contributor, if any, and such derivative works, in +source code and object code form.

+ +

b) Subject to the terms of this Agreement, each +Contributor hereby grants Recipient a non-exclusive, worldwide, +royalty-free patent license under Licensed Patents to make, use, sell, +offer to sell, import and otherwise transfer the Contribution of such +Contributor, if any, in source code and object code form. This patent +license shall apply to the combination of the Contribution and the +Program if, at the time the Contribution is added by the Contributor, +such addition of the Contribution causes such combination to be covered +by the Licensed Patents. The patent license shall not apply to any other +combinations which include the Contribution. No hardware per se is +licensed hereunder.

+ +

c) Recipient understands that although each Contributor +grants the licenses to its Contributions set forth herein, no assurances +are provided by any Contributor that the Program does not infringe the +patent or other intellectual property rights of any other entity. Each +Contributor disclaims any liability to Recipient for claims brought by +any other entity based on infringement of intellectual property rights +or otherwise. As a condition to exercising the rights and licenses +granted hereunder, each Recipient hereby assumes sole responsibility to +secure any other intellectual property rights needed, if any. For +example, if a third party patent license is required to allow Recipient +to distribute the Program, it is Recipient's responsibility to acquire +that license before distributing the Program.

+ +

d) Each Contributor represents that to its knowledge it +has sufficient copyright rights in its Contribution, if any, to grant +the copyright license set forth in this Agreement.

+ +

3. REQUIREMENTS

+ +

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

+ +

a) it complies with the terms and conditions of this +Agreement; and

+ +

b) its license agreement:

+ +

i) effectively disclaims on behalf of all Contributors +all warranties and conditions, express and implied, including warranties +or conditions of title and non-infringement, and implied warranties or +conditions of merchantability and fitness for a particular purpose;

+ +

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

+ +

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

+ +

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

+ +

When the Program is made available in source code form:

+ +

a) it must be made available under this Agreement; and

+ +

b) a copy of this Agreement must be included with each +copy of the Program.

+ +

Contributors may not remove or alter any copyright notices contained +within the Program.

+ +

Each Contributor must identify itself as the originator of its +Contribution, if any, in a manner that reasonably allows subsequent +Recipients to identify the originator of the Contribution.

+ +

4. COMMERCIAL DISTRIBUTION

+ +

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

+ +

For example, a Contributor might include the Program in a commercial +product offering, Product X. That Contributor is then a Commercial +Contributor. If that Commercial Contributor then makes performance +claims, or offers warranties related to Product X, those performance +claims and warranties are such Commercial Contributor's responsibility +alone. Under this section, the Commercial Contributor would have to +defend claims against the other Contributors related to those +performance claims and warranties, and if a court requires any other +Contributor to pay any damages as a result, the Commercial Contributor +must pay those damages.

+ +

5. NO WARRANTY

+ +

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS +PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS +OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, +ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY +OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely +responsible for determining the appropriateness of using and +distributing the Program and assumes all risks associated with its +exercise of rights under this Agreement , including but not limited to +the risks and costs of program errors, compliance with applicable laws, +damage to or loss of data, programs or equipment, and unavailability or +interruption of operations.

+ +

6. DISCLAIMER OF LIABILITY

+ +

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

+ +

7. GENERAL

+ +

If any provision of this Agreement is invalid or unenforceable under +applicable law, it shall not affect the validity or enforceability of +the remainder of the terms of this Agreement, and without further action +by the parties hereto, such provision shall be reformed to the minimum +extent necessary to make such provision valid and enforceable.

+ +

If Recipient institutes patent litigation against any entity +(including a cross-claim or counterclaim in a lawsuit) alleging that the +Program itself (excluding combinations of the Program with other +software or hardware) infringes such Recipient's patent(s), then such +Recipient's rights granted under Section 2(b) shall terminate as of the +date such litigation is filed.

+ +

All Recipient's rights under this Agreement shall terminate if it +fails to comply with any of the material terms or conditions of this +Agreement and does not cure such failure in a reasonable period of time +after becoming aware of such noncompliance. If all Recipient's rights +under this Agreement terminate, Recipient agrees to cease use and +distribution of the Program as soon as reasonably practicable. However, +Recipient's obligations under this Agreement and any licenses granted by +Recipient relating to the Program shall continue and survive.

+ +

Everyone is permitted to copy and distribute copies of this +Agreement, but in order to avoid inconsistency the Agreement is +copyrighted and may only be modified in the following manner. The +Agreement Steward reserves the right to publish new versions (including +revisions) of this Agreement from time to time. No one other than the +Agreement Steward has the right to modify this Agreement. The Eclipse +Foundation is the initial Agreement Steward. The Eclipse Foundation may +assign the responsibility to serve as the Agreement Steward to a +suitable separate entity. Each new version of the Agreement will be +given a distinguishing version number. The Program (including +Contributions) may always be distributed subject to the version of the +Agreement under which it was received. In addition, after a new version +of the Agreement is published, Contributor may elect to distribute the +Program (including its Contributions) under the new version. Except as +expressly stated in Sections 2(a) and 2(b) above, Recipient receives no +rights or licenses to the intellectual property of any Contributor under +this Agreement, whether expressly, by implication, estoppel or +otherwise. All rights in the Program not expressly granted under this +Agreement are reserved.

+ +

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

+ + + + diff --git a/src/jvm/clojure/asm/AnnotationVisitor.java b/src/jvm/clojure/asm/AnnotationVisitor.java index 2b0d2d6..8ce5cc6 100644 --- a/src/jvm/clojure/asm/AnnotationVisitor.java +++ b/src/jvm/clojure/asm/AnnotationVisitor.java @@ -1,97 +1,97 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * A visitor to visit a Java annotation. The methods of this interface must be - * called in the following order: (visit | visitEnum | - * visitAnnotation | visitArray)* visitEnd. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -public interface AnnotationVisitor{ - -/** - * Visits a primitive value of the annotation. - * - * @param name the value name. - * @param value the actual value, whose type must be {@link Byte}, - * {@link Boolean}, {@link Character}, {@link Short}, - * {@link Integer}, {@link Long}, {@link Float}, {@link Double}, - * {@link String} or {@link Type}. This value can also be an array - * of byte, boolean, short, char, int, long, float or double values - * (this is equivalent to using {@link #visitArray visitArray} and - * visiting each array element in turn, but is more convenient). - */ -void visit(String name, Object value); - -/** - * Visits an enumeration value of the annotation. - * - * @param name the value name. - * @param desc the class descriptor of the enumeration class. - * @param value the actual enumeration value. - */ -void visitEnum(String name, String desc, String value); - -/** - * Visits a nested annotation value of the annotation. - * - * @param name the value name. - * @param desc the class descriptor of the nested annotation class. - * @return a visitor to visit the actual nested annotation value, or - * null if this visitor is not interested in visiting - * this nested annotation. The nested annotation value must be - * fully visited before calling other methods on this annotation - * visitor. - */ -AnnotationVisitor visitAnnotation(String name, String desc); - -/** - * Visits an array value of the annotation. Note that arrays of primitive - * types (such as byte, boolean, short, char, int, long, float or double) - * can be passed as value to {@link #visit visit}. This is what - * {@link ClassReader} does. - * - * @param name the value name. - * @return a visitor to visit the actual array value elements, or - * null if this visitor is not interested in visiting - * these values. The 'name' parameters passed to the methods of this - * visitor are ignored. All the array values must be visited - * before calling other methods on this annotation visitor. - */ -AnnotationVisitor visitArray(String name); - -/** - * Visits the end of the annotation. - */ -void visitEnd(); -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * A visitor to visit a Java annotation. The methods of this interface must be + * called in the following order: (visit | visitEnum | + * visitAnnotation | visitArray)* visitEnd. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public interface AnnotationVisitor{ + +/** + * Visits a primitive value of the annotation. + * + * @param name the value name. + * @param value the actual value, whose type must be {@link Byte}, + * {@link Boolean}, {@link Character}, {@link Short}, + * {@link Integer}, {@link Long}, {@link Float}, {@link Double}, + * {@link String} or {@link Type}. This value can also be an array + * of byte, boolean, short, char, int, long, float or double values + * (this is equivalent to using {@link #visitArray visitArray} and + * visiting each array element in turn, but is more convenient). + */ +void visit(String name, Object value); + +/** + * Visits an enumeration value of the annotation. + * + * @param name the value name. + * @param desc the class descriptor of the enumeration class. + * @param value the actual enumeration value. + */ +void visitEnum(String name, String desc, String value); + +/** + * Visits a nested annotation value of the annotation. + * + * @param name the value name. + * @param desc the class descriptor of the nested annotation class. + * @return a visitor to visit the actual nested annotation value, or + * null if this visitor is not interested in visiting + * this nested annotation. The nested annotation value must be + * fully visited before calling other methods on this annotation + * visitor. + */ +AnnotationVisitor visitAnnotation(String name, String desc); + +/** + * Visits an array value of the annotation. Note that arrays of primitive + * types (such as byte, boolean, short, char, int, long, float or double) + * can be passed as value to {@link #visit visit}. This is what + * {@link ClassReader} does. + * + * @param name the value name. + * @return a visitor to visit the actual array value elements, or + * null if this visitor is not interested in visiting + * these values. The 'name' parameters passed to the methods of this + * visitor are ignored. All the array values must be visited + * before calling other methods on this annotation visitor. + */ +AnnotationVisitor visitArray(String name); + +/** + * Visits the end of the annotation. + */ +void visitEnd(); +} diff --git a/src/jvm/clojure/asm/AnnotationWriter.java b/src/jvm/clojure/asm/AnnotationWriter.java index 3097ac5..afd59fd 100644 --- a/src/jvm/clojure/asm/AnnotationWriter.java +++ b/src/jvm/clojure/asm/AnnotationWriter.java @@ -1,357 +1,357 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * An {@link AnnotationVisitor} that generates annotations in bytecode form. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -final class AnnotationWriter implements AnnotationVisitor{ - -/** - * The class writer to which this annotation must be added. - */ -private final ClassWriter cw; - -/** - * The number of values in this annotation. - */ -private int size; - -/** - * true if values are named, false otherwise. Annotation - * writers used for annotation default and annotation arrays use unnamed - * values. - */ -private final boolean named; - -/** - * The annotation values in bytecode form. This byte vector only contains - * the values themselves, i.e. the number of values must be stored as a - * unsigned short just before these bytes. - */ -private final ByteVector bv; - -/** - * The byte vector to be used to store the number of values of this - * annotation. See {@link #bv}. - */ -private final ByteVector parent; - -/** - * Where the number of values of this annotation must be stored in - * {@link #parent}. - */ -private final int offset; - -/** - * Next annotation writer. This field is used to store annotation lists. - */ -AnnotationWriter next; - -/** - * Previous annotation writer. This field is used to store annotation lists. - */ -AnnotationWriter prev; - -// ------------------------------------------------------------------------ -// Constructor -// ------------------------------------------------------------------------ - -/** - * Constructs a new {@link AnnotationWriter}. - * - * @param cw the class writer to which this annotation must be added. - * @param named true if values are named, false otherwise. - * @param bv where the annotation values must be stored. - * @param parent where the number of annotation values must be stored. - * @param offset where in parent the number of annotation values must - * be stored. - */ -AnnotationWriter( - final ClassWriter cw, - final boolean named, - final ByteVector bv, - final ByteVector parent, - final int offset){ - this.cw = cw; - this.named = named; - this.bv = bv; - this.parent = parent; - this.offset = offset; -} - -// ------------------------------------------------------------------------ -// Implementation of the AnnotationVisitor interface -// ------------------------------------------------------------------------ - -public void visit(final String name, final Object value){ - ++size; - if(named) - { - bv.putShort(cw.newUTF8(name)); - } - if(value instanceof String) - { - bv.put12('s', cw.newUTF8((String) value)); - } - else if(value instanceof Byte) - { - bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index); - } - else if(value instanceof Boolean) - { - int v = ((Boolean) value).booleanValue() ? 1 : 0; - bv.put12('Z', cw.newInteger(v).index); - } - else if(value instanceof Character) - { - bv.put12('C', cw.newInteger(((Character) value).charValue()).index); - } - else if(value instanceof Short) - { - bv.put12('S', cw.newInteger(((Short) value).shortValue()).index); - } - else if(value instanceof Type) - { - bv.put12('c', cw.newUTF8(((Type) value).getDescriptor())); - } - else if(value instanceof byte[]) - { - byte[] v = (byte[]) value; - bv.put12('[', v.length); - for(int i = 0; i < v.length; i++) - { - bv.put12('B', cw.newInteger(v[i]).index); - } - } - else if(value instanceof boolean[]) - { - boolean[] v = (boolean[]) value; - bv.put12('[', v.length); - for(int i = 0; i < v.length; i++) - { - bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index); - } - } - else if(value instanceof short[]) - { - short[] v = (short[]) value; - bv.put12('[', v.length); - for(int i = 0; i < v.length; i++) - { - bv.put12('S', cw.newInteger(v[i]).index); - } - } - else if(value instanceof char[]) - { - char[] v = (char[]) value; - bv.put12('[', v.length); - for(int i = 0; i < v.length; i++) - { - bv.put12('C', cw.newInteger(v[i]).index); - } - } - else if(value instanceof int[]) - { - int[] v = (int[]) value; - bv.put12('[', v.length); - for(int i = 0; i < v.length; i++) - { - bv.put12('I', cw.newInteger(v[i]).index); - } - } - else if(value instanceof long[]) - { - long[] v = (long[]) value; - bv.put12('[', v.length); - for(int i = 0; i < v.length; i++) - { - bv.put12('J', cw.newLong(v[i]).index); - } - } - else if(value instanceof float[]) - { - float[] v = (float[]) value; - bv.put12('[', v.length); - for(int i = 0; i < v.length; i++) - { - bv.put12('F', cw.newFloat(v[i]).index); - } - } - else if(value instanceof double[]) - { - double[] v = (double[]) value; - bv.put12('[', v.length); - for(int i = 0; i < v.length; i++) - { - bv.put12('D', cw.newDouble(v[i]).index); - } - } - else - { - Item i = cw.newConstItem(value); - bv.put12(".s.IFJDCS".charAt(i.type), i.index); - } -} - -public void visitEnum( - final String name, - final String desc, - final String value){ - ++size; - if(named) - { - bv.putShort(cw.newUTF8(name)); - } - bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value)); -} - -public AnnotationVisitor visitAnnotation( - final String name, - final String desc){ - ++size; - if(named) - { - bv.putShort(cw.newUTF8(name)); - } - // write tag and type, and reserve space for values count - bv.put12('@', cw.newUTF8(desc)).putShort(0); - return new AnnotationWriter(cw, true, bv, bv, bv.length - 2); -} - -public AnnotationVisitor visitArray(final String name){ - ++size; - if(named) - { - bv.putShort(cw.newUTF8(name)); - } - // write tag, and reserve space for array size - bv.put12('[', 0); - return new AnnotationWriter(cw, false, bv, bv, bv.length - 2); -} - -public void visitEnd(){ - if(parent != null) - { - byte[] data = parent.data; - data[offset] = (byte) (size >>> 8); - data[offset + 1] = (byte) size; - } -} - -// ------------------------------------------------------------------------ -// Utility methods -// ------------------------------------------------------------------------ - -/** - * Returns the size of this annotation writer list. - * - * @return the size of this annotation writer list. - */ -int getSize(){ - int size = 0; - AnnotationWriter aw = this; - while(aw != null) - { - size += aw.bv.length; - aw = aw.next; - } - return size; -} - -/** - * Puts the annotations of this annotation writer list into the given byte - * vector. - * - * @param out where the annotations must be put. - */ -void put(final ByteVector out){ - int n = 0; - int size = 2; - AnnotationWriter aw = this; - AnnotationWriter last = null; - while(aw != null) - { - ++n; - size += aw.bv.length; - aw.visitEnd(); // in case user forgot to call visitEnd - aw.prev = last; - last = aw; - aw = aw.next; - } - out.putInt(size); - out.putShort(n); - aw = last; - while(aw != null) - { - out.putByteArray(aw.bv.data, 0, aw.bv.length); - aw = aw.prev; - } -} - -/** - * Puts the given annotation lists into the given byte vector. - * - * @param panns an array of annotation writer lists. - * @param out where the annotations must be put. - */ -static void put(final AnnotationWriter[] panns, final ByteVector out){ - int size = 1 + 2 * panns.length; - for(int i = 0; i < panns.length; ++i) - { - size += panns[i] == null ? 0 : panns[i].getSize(); - } - out.putInt(size).putByte(panns.length); - for(int i = 0; i < panns.length; ++i) - { - AnnotationWriter aw = panns[i]; - AnnotationWriter last = null; - int n = 0; - while(aw != null) - { - ++n; - aw.visitEnd(); // in case user forgot to call visitEnd - aw.prev = last; - last = aw; - aw = aw.next; - } - out.putShort(n); - aw = last; - while(aw != null) - { - out.putByteArray(aw.bv.data, 0, aw.bv.length); - aw = aw.prev; - } - } -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * An {@link AnnotationVisitor} that generates annotations in bytecode form. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +final class AnnotationWriter implements AnnotationVisitor{ + +/** + * The class writer to which this annotation must be added. + */ +private final ClassWriter cw; + +/** + * The number of values in this annotation. + */ +private int size; + +/** + * true if values are named, false otherwise. Annotation + * writers used for annotation default and annotation arrays use unnamed + * values. + */ +private final boolean named; + +/** + * The annotation values in bytecode form. This byte vector only contains + * the values themselves, i.e. the number of values must be stored as a + * unsigned short just before these bytes. + */ +private final ByteVector bv; + +/** + * The byte vector to be used to store the number of values of this + * annotation. See {@link #bv}. + */ +private final ByteVector parent; + +/** + * Where the number of values of this annotation must be stored in + * {@link #parent}. + */ +private final int offset; + +/** + * Next annotation writer. This field is used to store annotation lists. + */ +AnnotationWriter next; + +/** + * Previous annotation writer. This field is used to store annotation lists. + */ +AnnotationWriter prev; + +// ------------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------------ + +/** + * Constructs a new {@link AnnotationWriter}. + * + * @param cw the class writer to which this annotation must be added. + * @param named true if values are named, false otherwise. + * @param bv where the annotation values must be stored. + * @param parent where the number of annotation values must be stored. + * @param offset where in parent the number of annotation values must + * be stored. + */ +AnnotationWriter( + final ClassWriter cw, + final boolean named, + final ByteVector bv, + final ByteVector parent, + final int offset){ + this.cw = cw; + this.named = named; + this.bv = bv; + this.parent = parent; + this.offset = offset; +} + +// ------------------------------------------------------------------------ +// Implementation of the AnnotationVisitor interface +// ------------------------------------------------------------------------ + +public void visit(final String name, final Object value){ + ++size; + if(named) + { + bv.putShort(cw.newUTF8(name)); + } + if(value instanceof String) + { + bv.put12('s', cw.newUTF8((String) value)); + } + else if(value instanceof Byte) + { + bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index); + } + else if(value instanceof Boolean) + { + int v = ((Boolean) value).booleanValue() ? 1 : 0; + bv.put12('Z', cw.newInteger(v).index); + } + else if(value instanceof Character) + { + bv.put12('C', cw.newInteger(((Character) value).charValue()).index); + } + else if(value instanceof Short) + { + bv.put12('S', cw.newInteger(((Short) value).shortValue()).index); + } + else if(value instanceof Type) + { + bv.put12('c', cw.newUTF8(((Type) value).getDescriptor())); + } + else if(value instanceof byte[]) + { + byte[] v = (byte[]) value; + bv.put12('[', v.length); + for(int i = 0; i < v.length; i++) + { + bv.put12('B', cw.newInteger(v[i]).index); + } + } + else if(value instanceof boolean[]) + { + boolean[] v = (boolean[]) value; + bv.put12('[', v.length); + for(int i = 0; i < v.length; i++) + { + bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index); + } + } + else if(value instanceof short[]) + { + short[] v = (short[]) value; + bv.put12('[', v.length); + for(int i = 0; i < v.length; i++) + { + bv.put12('S', cw.newInteger(v[i]).index); + } + } + else if(value instanceof char[]) + { + char[] v = (char[]) value; + bv.put12('[', v.length); + for(int i = 0; i < v.length; i++) + { + bv.put12('C', cw.newInteger(v[i]).index); + } + } + else if(value instanceof int[]) + { + int[] v = (int[]) value; + bv.put12('[', v.length); + for(int i = 0; i < v.length; i++) + { + bv.put12('I', cw.newInteger(v[i]).index); + } + } + else if(value instanceof long[]) + { + long[] v = (long[]) value; + bv.put12('[', v.length); + for(int i = 0; i < v.length; i++) + { + bv.put12('J', cw.newLong(v[i]).index); + } + } + else if(value instanceof float[]) + { + float[] v = (float[]) value; + bv.put12('[', v.length); + for(int i = 0; i < v.length; i++) + { + bv.put12('F', cw.newFloat(v[i]).index); + } + } + else if(value instanceof double[]) + { + double[] v = (double[]) value; + bv.put12('[', v.length); + for(int i = 0; i < v.length; i++) + { + bv.put12('D', cw.newDouble(v[i]).index); + } + } + else + { + Item i = cw.newConstItem(value); + bv.put12(".s.IFJDCS".charAt(i.type), i.index); + } +} + +public void visitEnum( + final String name, + final String desc, + final String value){ + ++size; + if(named) + { + bv.putShort(cw.newUTF8(name)); + } + bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value)); +} + +public AnnotationVisitor visitAnnotation( + final String name, + final String desc){ + ++size; + if(named) + { + bv.putShort(cw.newUTF8(name)); + } + // write tag and type, and reserve space for values count + bv.put12('@', cw.newUTF8(desc)).putShort(0); + return new AnnotationWriter(cw, true, bv, bv, bv.length - 2); +} + +public AnnotationVisitor visitArray(final String name){ + ++size; + if(named) + { + bv.putShort(cw.newUTF8(name)); + } + // write tag, and reserve space for array size + bv.put12('[', 0); + return new AnnotationWriter(cw, false, bv, bv, bv.length - 2); +} + +public void visitEnd(){ + if(parent != null) + { + byte[] data = parent.data; + data[offset] = (byte) (size >>> 8); + data[offset + 1] = (byte) size; + } +} + +// ------------------------------------------------------------------------ +// Utility methods +// ------------------------------------------------------------------------ + +/** + * Returns the size of this annotation writer list. + * + * @return the size of this annotation writer list. + */ +int getSize(){ + int size = 0; + AnnotationWriter aw = this; + while(aw != null) + { + size += aw.bv.length; + aw = aw.next; + } + return size; +} + +/** + * Puts the annotations of this annotation writer list into the given byte + * vector. + * + * @param out where the annotations must be put. + */ +void put(final ByteVector out){ + int n = 0; + int size = 2; + AnnotationWriter aw = this; + AnnotationWriter last = null; + while(aw != null) + { + ++n; + size += aw.bv.length; + aw.visitEnd(); // in case user forgot to call visitEnd + aw.prev = last; + last = aw; + aw = aw.next; + } + out.putInt(size); + out.putShort(n); + aw = last; + while(aw != null) + { + out.putByteArray(aw.bv.data, 0, aw.bv.length); + aw = aw.prev; + } +} + +/** + * Puts the given annotation lists into the given byte vector. + * + * @param panns an array of annotation writer lists. + * @param out where the annotations must be put. + */ +static void put(final AnnotationWriter[] panns, final ByteVector out){ + int size = 1 + 2 * panns.length; + for(int i = 0; i < panns.length; ++i) + { + size += panns[i] == null ? 0 : panns[i].getSize(); + } + out.putInt(size).putByte(panns.length); + for(int i = 0; i < panns.length; ++i) + { + AnnotationWriter aw = panns[i]; + AnnotationWriter last = null; + int n = 0; + while(aw != null) + { + ++n; + aw.visitEnd(); // in case user forgot to call visitEnd + aw.prev = last; + last = aw; + aw = aw.next; + } + out.putShort(n); + aw = last; + while(aw != null) + { + out.putByteArray(aw.bv.data, 0, aw.bv.length); + aw = aw.prev; + } + } +} +} diff --git a/src/jvm/clojure/asm/Attribute.java b/src/jvm/clojure/asm/Attribute.java index d08762f..0780f5b 100644 --- a/src/jvm/clojure/asm/Attribute.java +++ b/src/jvm/clojure/asm/Attribute.java @@ -1,253 +1,253 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * A non standard class, field, method or code attribute. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -public class Attribute{ - -/** - * The type of this attribute. - */ -public final String type; - -/** - * The raw value of this attribute, used only for unknown attributes. - */ -byte[] value; - -/** - * The next attribute in this attribute list. May be null. - */ -Attribute next; - -/** - * Constructs a new empty attribute. - * - * @param type the type of the attribute. - */ -protected Attribute(final String type){ - this.type = type; -} - -/** - * Returns true if this type of attribute is unknown. The default - * implementation of this method always returns true. - * - * @return true if this type of attribute is unknown. - */ -public boolean isUnknown(){ - return true; -} - -/** - * Returns true if this type of attribute is a code attribute. - * - * @return true if this type of attribute is a code attribute. - */ -public boolean isCodeAttribute(){ - return false; -} - -/** - * Returns the labels corresponding to this attribute. - * - * @return the labels corresponding to this attribute, or null if - * this attribute is not a code attribute that contains labels. - */ -protected Label[] getLabels(){ - return null; -} - -/** - * Reads a {@link #type type} attribute. This method must return a new - * {@link Attribute} object, of type {@link #type type}, corresponding to - * the len bytes starting at the given offset, in the given class - * reader. - * - * @param cr the class that contains the attribute to be read. - * @param off index of the first byte of the attribute's content in {@link - * ClassReader#b cr.b}. The 6 attribute header bytes, containing the - * type and the length of the attribute, are not taken into account - * here. - * @param len the length of the attribute's content. - * @param buf buffer to be used to call - * {@link ClassReader#readUTF8 readUTF8}, - * {@link ClassReader#readClass(int,char[]) readClass} or - * {@link ClassReader#readConst readConst}. - * @param codeOff index of the first byte of code's attribute content in - * {@link ClassReader#b cr.b}, or -1 if the attribute to be read is - * not a code attribute. The 6 attribute header bytes, containing the - * type and the length of the attribute, are not taken into account - * here. - * @param labels the labels of the method's code, or null if the - * attribute to be read is not a code attribute. - * @return a new {@link Attribute} object corresponding to the given - * bytes. - */ -protected Attribute read( - final ClassReader cr, - final int off, - final int len, - final char[] buf, - final int codeOff, - final Label[] labels){ - Attribute attr = new Attribute(type); - attr.value = new byte[len]; - System.arraycopy(cr.b, off, attr.value, 0, len); - return attr; -} - -/** - * Returns the byte array form of this attribute. - * - * @param cw the class to which this attribute must be added. This parameter - * can be used to add to the constant pool of this class the items - * that corresponds to this attribute. - * @param code the bytecode of the method corresponding to this code - * attribute, or null if this attribute is not a code - * attributes. - * @param len the length of the bytecode of the method corresponding to this - * code attribute, or null if this attribute is not a code - * attribute. - * @param maxStack the maximum stack size of the method corresponding to - * this code attribute, or -1 if this attribute is not a code - * attribute. - * @param maxLocals the maximum number of local variables of the method - * corresponding to this code attribute, or -1 if this attribute is - * not a code attribute. - * @return the byte array form of this attribute. - */ -protected ByteVector write( - final ClassWriter cw, - final byte[] code, - final int len, - final int maxStack, - final int maxLocals){ - ByteVector v = new ByteVector(); - v.data = value; - v.length = value.length; - return v; -} - -/** - * Returns the length of the attribute list that begins with this attribute. - * - * @return the length of the attribute list that begins with this attribute. - */ -final int getCount(){ - int count = 0; - Attribute attr = this; - while(attr != null) - { - count += 1; - attr = attr.next; - } - return count; -} - -/** - * Returns the size of all the attributes in this attribute list. - * - * @param cw the class writer to be used to convert the attributes into byte - * arrays, with the {@link #write write} method. - * @param code the bytecode of the method corresponding to these code - * attributes, or null if these attributes are not code - * attributes. - * @param len the length of the bytecode of the method corresponding to - * these code attributes, or null if these attributes are - * not code attributes. - * @param maxStack the maximum stack size of the method corresponding to - * these code attributes, or -1 if these attributes are not code - * attributes. - * @param maxLocals the maximum number of local variables of the method - * corresponding to these code attributes, or -1 if these attributes - * are not code attributes. - * @return the size of all the attributes in this attribute list. This size - * includes the size of the attribute headers. - */ -final int getSize( - final ClassWriter cw, - final byte[] code, - final int len, - final int maxStack, - final int maxLocals){ - Attribute attr = this; - int size = 0; - while(attr != null) - { - cw.newUTF8(attr.type); - size += attr.write(cw, code, len, maxStack, maxLocals).length + 6; - attr = attr.next; - } - return size; -} - -/** - * Writes all the attributes of this attribute list in the given byte - * vector. - * - * @param cw the class writer to be used to convert the attributes into byte - * arrays, with the {@link #write write} method. - * @param code the bytecode of the method corresponding to these code - * attributes, or null if these attributes are not code - * attributes. - * @param len the length of the bytecode of the method corresponding to - * these code attributes, or null if these attributes are - * not code attributes. - * @param maxStack the maximum stack size of the method corresponding to - * these code attributes, or -1 if these attributes are not code - * attributes. - * @param maxLocals the maximum number of local variables of the method - * corresponding to these code attributes, or -1 if these attributes - * are not code attributes. - * @param out where the attributes must be written. - */ -final void put( - final ClassWriter cw, - final byte[] code, - final int len, - final int maxStack, - final int maxLocals, - final ByteVector out){ - Attribute attr = this; - while(attr != null) - { - ByteVector b = attr.write(cw, code, len, maxStack, maxLocals); - out.putShort(cw.newUTF8(attr.type)).putInt(b.length); - out.putByteArray(b.data, 0, b.length); - attr = attr.next; - } -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * A non standard class, field, method or code attribute. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class Attribute{ + +/** + * The type of this attribute. + */ +public final String type; + +/** + * The raw value of this attribute, used only for unknown attributes. + */ +byte[] value; + +/** + * The next attribute in this attribute list. May be null. + */ +Attribute next; + +/** + * Constructs a new empty attribute. + * + * @param type the type of the attribute. + */ +protected Attribute(final String type){ + this.type = type; +} + +/** + * Returns true if this type of attribute is unknown. The default + * implementation of this method always returns true. + * + * @return true if this type of attribute is unknown. + */ +public boolean isUnknown(){ + return true; +} + +/** + * Returns true if this type of attribute is a code attribute. + * + * @return true if this type of attribute is a code attribute. + */ +public boolean isCodeAttribute(){ + return false; +} + +/** + * Returns the labels corresponding to this attribute. + * + * @return the labels corresponding to this attribute, or null if + * this attribute is not a code attribute that contains labels. + */ +protected Label[] getLabels(){ + return null; +} + +/** + * Reads a {@link #type type} attribute. This method must return a new + * {@link Attribute} object, of type {@link #type type}, corresponding to + * the len bytes starting at the given offset, in the given class + * reader. + * + * @param cr the class that contains the attribute to be read. + * @param off index of the first byte of the attribute's content in {@link + * ClassReader#b cr.b}. The 6 attribute header bytes, containing the + * type and the length of the attribute, are not taken into account + * here. + * @param len the length of the attribute's content. + * @param buf buffer to be used to call + * {@link ClassReader#readUTF8 readUTF8}, + * {@link ClassReader#readClass(int,char[]) readClass} or + * {@link ClassReader#readConst readConst}. + * @param codeOff index of the first byte of code's attribute content in + * {@link ClassReader#b cr.b}, or -1 if the attribute to be read is + * not a code attribute. The 6 attribute header bytes, containing the + * type and the length of the attribute, are not taken into account + * here. + * @param labels the labels of the method's code, or null if the + * attribute to be read is not a code attribute. + * @return a new {@link Attribute} object corresponding to the given + * bytes. + */ +protected Attribute read( + final ClassReader cr, + final int off, + final int len, + final char[] buf, + final int codeOff, + final Label[] labels){ + Attribute attr = new Attribute(type); + attr.value = new byte[len]; + System.arraycopy(cr.b, off, attr.value, 0, len); + return attr; +} + +/** + * Returns the byte array form of this attribute. + * + * @param cw the class to which this attribute must be added. This parameter + * can be used to add to the constant pool of this class the items + * that corresponds to this attribute. + * @param code the bytecode of the method corresponding to this code + * attribute, or null if this attribute is not a code + * attributes. + * @param len the length of the bytecode of the method corresponding to this + * code attribute, or null if this attribute is not a code + * attribute. + * @param maxStack the maximum stack size of the method corresponding to + * this code attribute, or -1 if this attribute is not a code + * attribute. + * @param maxLocals the maximum number of local variables of the method + * corresponding to this code attribute, or -1 if this attribute is + * not a code attribute. + * @return the byte array form of this attribute. + */ +protected ByteVector write( + final ClassWriter cw, + final byte[] code, + final int len, + final int maxStack, + final int maxLocals){ + ByteVector v = new ByteVector(); + v.data = value; + v.length = value.length; + return v; +} + +/** + * Returns the length of the attribute list that begins with this attribute. + * + * @return the length of the attribute list that begins with this attribute. + */ +final int getCount(){ + int count = 0; + Attribute attr = this; + while(attr != null) + { + count += 1; + attr = attr.next; + } + return count; +} + +/** + * Returns the size of all the attributes in this attribute list. + * + * @param cw the class writer to be used to convert the attributes into byte + * arrays, with the {@link #write write} method. + * @param code the bytecode of the method corresponding to these code + * attributes, or null if these attributes are not code + * attributes. + * @param len the length of the bytecode of the method corresponding to + * these code attributes, or null if these attributes are + * not code attributes. + * @param maxStack the maximum stack size of the method corresponding to + * these code attributes, or -1 if these attributes are not code + * attributes. + * @param maxLocals the maximum number of local variables of the method + * corresponding to these code attributes, or -1 if these attributes + * are not code attributes. + * @return the size of all the attributes in this attribute list. This size + * includes the size of the attribute headers. + */ +final int getSize( + final ClassWriter cw, + final byte[] code, + final int len, + final int maxStack, + final int maxLocals){ + Attribute attr = this; + int size = 0; + while(attr != null) + { + cw.newUTF8(attr.type); + size += attr.write(cw, code, len, maxStack, maxLocals).length + 6; + attr = attr.next; + } + return size; +} + +/** + * Writes all the attributes of this attribute list in the given byte + * vector. + * + * @param cw the class writer to be used to convert the attributes into byte + * arrays, with the {@link #write write} method. + * @param code the bytecode of the method corresponding to these code + * attributes, or null if these attributes are not code + * attributes. + * @param len the length of the bytecode of the method corresponding to + * these code attributes, or null if these attributes are + * not code attributes. + * @param maxStack the maximum stack size of the method corresponding to + * these code attributes, or -1 if these attributes are not code + * attributes. + * @param maxLocals the maximum number of local variables of the method + * corresponding to these code attributes, or -1 if these attributes + * are not code attributes. + * @param out where the attributes must be written. + */ +final void put( + final ClassWriter cw, + final byte[] code, + final int len, + final int maxStack, + final int maxLocals, + final ByteVector out){ + Attribute attr = this; + while(attr != null) + { + ByteVector b = attr.write(cw, code, len, maxStack, maxLocals); + out.putShort(cw.newUTF8(attr.type)).putInt(b.length); + out.putByteArray(b.data, 0, b.length); + attr = attr.next; + } +} +} diff --git a/src/jvm/clojure/asm/ByteVector.java b/src/jvm/clojure/asm/ByteVector.java index 5181e7b..ed71305 100644 --- a/src/jvm/clojure/asm/ByteVector.java +++ b/src/jvm/clojure/asm/ByteVector.java @@ -1,318 +1,318 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * A dynamically extensible vector of bytes. This class is roughly equivalent to - * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient. - * - * @author Eric Bruneton - */ -public class ByteVector{ - -/** - * The content of this vector. - */ -byte[] data; - -/** - * Actual number of bytes in this vector. - */ -int length; - -/** - * Constructs a new {@link ByteVector ByteVector} with a default initial - * size. - */ -public ByteVector(){ - data = new byte[64]; -} - -/** - * Constructs a new {@link ByteVector ByteVector} with the given initial - * size. - * - * @param initialSize the initial size of the byte vector to be constructed. - */ -public ByteVector(final int initialSize){ - data = new byte[initialSize]; -} - -/** - * Puts a byte into this byte vector. The byte vector is automatically - * enlarged if necessary. - * - * @param b a byte. - * @return this byte vector. - */ -public ByteVector putByte(final int b){ - int length = this.length; - if(length + 1 > data.length) - { - enlarge(1); - } - data[length++] = (byte) b; - this.length = length; - return this; -} - -/** - * Puts two bytes into this byte vector. The byte vector is automatically - * enlarged if necessary. - * - * @param b1 a byte. - * @param b2 another byte. - * @return this byte vector. - */ -ByteVector put11(final int b1, final int b2){ - int length = this.length; - if(length + 2 > data.length) - { - enlarge(2); - } - byte[] data = this.data; - data[length++] = (byte) b1; - data[length++] = (byte) b2; - this.length = length; - return this; -} - -/** - * Puts a short into this byte vector. The byte vector is automatically - * enlarged if necessary. - * - * @param s a short. - * @return this byte vector. - */ -public ByteVector putShort(final int s){ - int length = this.length; - if(length + 2 > data.length) - { - enlarge(2); - } - byte[] data = this.data; - data[length++] = (byte) (s >>> 8); - data[length++] = (byte) s; - this.length = length; - return this; -} - -/** - * Puts a byte and a short into this byte vector. The byte vector is - * automatically enlarged if necessary. - * - * @param b a byte. - * @param s a short. - * @return this byte vector. - */ -ByteVector put12(final int b, final int s){ - int length = this.length; - if(length + 3 > data.length) - { - enlarge(3); - } - byte[] data = this.data; - data[length++] = (byte) b; - data[length++] = (byte) (s >>> 8); - data[length++] = (byte) s; - this.length = length; - return this; -} - -/** - * Puts an int into this byte vector. The byte vector is automatically - * enlarged if necessary. - * - * @param i an int. - * @return this byte vector. - */ -public ByteVector putInt(final int i){ - int length = this.length; - if(length + 4 > data.length) - { - enlarge(4); - } - byte[] data = this.data; - data[length++] = (byte) (i >>> 24); - data[length++] = (byte) (i >>> 16); - data[length++] = (byte) (i >>> 8); - data[length++] = (byte) i; - this.length = length; - return this; -} - -/** - * Puts a long into this byte vector. The byte vector is automatically - * enlarged if necessary. - * - * @param l a long. - * @return this byte vector. - */ -public ByteVector putLong(final long l){ - int length = this.length; - if(length + 8 > data.length) - { - enlarge(8); - } - byte[] data = this.data; - int i = (int) (l >>> 32); - data[length++] = (byte) (i >>> 24); - data[length++] = (byte) (i >>> 16); - data[length++] = (byte) (i >>> 8); - data[length++] = (byte) i; - i = (int) l; - data[length++] = (byte) (i >>> 24); - data[length++] = (byte) (i >>> 16); - data[length++] = (byte) (i >>> 8); - data[length++] = (byte) i; - this.length = length; - return this; -} - -/** - * Puts an UTF8 string into this byte vector. The byte vector is - * automatically enlarged if necessary. - * - * @param s a String. - * @return this byte vector. - */ -public ByteVector putUTF8(final String s){ - int charLength = s.length(); - if(length + 2 + charLength > data.length) - { - enlarge(2 + charLength); - } - int len = length; - byte[] data = this.data; - // optimistic algorithm: instead of computing the byte length and then - // serializing the string (which requires two loops), we assume the byte - // length is equal to char length (which is the most frequent case), and - // we start serializing the string right away. During the serialization, - // if we find that this assumption is wrong, we continue with the - // general method. - data[len++] = (byte) (charLength >>> 8); - data[len++] = (byte) charLength; - for(int i = 0; i < charLength; ++i) - { - char c = s.charAt(i); - if(c >= '\001' && c <= '\177') - { - data[len++] = (byte) c; - } - else - { - int byteLength = i; - for(int j = i; j < charLength; ++j) - { - c = s.charAt(j); - if(c >= '\001' && c <= '\177') - { - byteLength++; - } - else if(c > '\u07FF') - { - byteLength += 3; - } - else - { - byteLength += 2; - } - } - data[length] = (byte) (byteLength >>> 8); - data[length + 1] = (byte) byteLength; - if(length + 2 + byteLength > data.length) - { - length = len; - enlarge(2 + byteLength); - data = this.data; - } - for(int j = i; j < charLength; ++j) - { - c = s.charAt(j); - if(c >= '\001' && c <= '\177') - { - data[len++] = (byte) c; - } - else if(c > '\u07FF') - { - data[len++] = (byte) (0xE0 | c >> 12 & 0xF); - data[len++] = (byte) (0x80 | c >> 6 & 0x3F); - data[len++] = (byte) (0x80 | c & 0x3F); - } - else - { - data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); - data[len++] = (byte) (0x80 | c & 0x3F); - } - } - break; - } - } - length = len; - return this; -} - -/** - * Puts an array of bytes into this byte vector. The byte vector is - * automatically enlarged if necessary. - * - * @param b an array of bytes. May be null to put len - * null bytes into this byte vector. - * @param off index of the fist byte of b that must be copied. - * @param len number of bytes of b that must be copied. - * @return this byte vector. - */ -public ByteVector putByteArray(final byte[] b, final int off, final int len){ - if(length + len > data.length) - { - enlarge(len); - } - if(b != null) - { - System.arraycopy(b, off, data, length, len); - } - length += len; - return this; -} - -/** - * Enlarge this byte vector so that it can receive n more bytes. - * - * @param size number of additional bytes that this byte vector should be - * able to receive. - */ -private void enlarge(final int size){ - int length1 = 2 * data.length; - int length2 = length + size; - byte[] newData = new byte[length1 > length2 ? length1 : length2]; - System.arraycopy(data, 0, newData, 0, length); - data = newData; -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * A dynamically extensible vector of bytes. This class is roughly equivalent to + * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient. + * + * @author Eric Bruneton + */ +public class ByteVector{ + +/** + * The content of this vector. + */ +byte[] data; + +/** + * Actual number of bytes in this vector. + */ +int length; + +/** + * Constructs a new {@link ByteVector ByteVector} with a default initial + * size. + */ +public ByteVector(){ + data = new byte[64]; +} + +/** + * Constructs a new {@link ByteVector ByteVector} with the given initial + * size. + * + * @param initialSize the initial size of the byte vector to be constructed. + */ +public ByteVector(final int initialSize){ + data = new byte[initialSize]; +} + +/** + * Puts a byte into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param b a byte. + * @return this byte vector. + */ +public ByteVector putByte(final int b){ + int length = this.length; + if(length + 1 > data.length) + { + enlarge(1); + } + data[length++] = (byte) b; + this.length = length; + return this; +} + +/** + * Puts two bytes into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param b1 a byte. + * @param b2 another byte. + * @return this byte vector. + */ +ByteVector put11(final int b1, final int b2){ + int length = this.length; + if(length + 2 > data.length) + { + enlarge(2); + } + byte[] data = this.data; + data[length++] = (byte) b1; + data[length++] = (byte) b2; + this.length = length; + return this; +} + +/** + * Puts a short into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param s a short. + * @return this byte vector. + */ +public ByteVector putShort(final int s){ + int length = this.length; + if(length + 2 > data.length) + { + enlarge(2); + } + byte[] data = this.data; + data[length++] = (byte) (s >>> 8); + data[length++] = (byte) s; + this.length = length; + return this; +} + +/** + * Puts a byte and a short into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param b a byte. + * @param s a short. + * @return this byte vector. + */ +ByteVector put12(final int b, final int s){ + int length = this.length; + if(length + 3 > data.length) + { + enlarge(3); + } + byte[] data = this.data; + data[length++] = (byte) b; + data[length++] = (byte) (s >>> 8); + data[length++] = (byte) s; + this.length = length; + return this; +} + +/** + * Puts an int into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param i an int. + * @return this byte vector. + */ +public ByteVector putInt(final int i){ + int length = this.length; + if(length + 4 > data.length) + { + enlarge(4); + } + byte[] data = this.data; + data[length++] = (byte) (i >>> 24); + data[length++] = (byte) (i >>> 16); + data[length++] = (byte) (i >>> 8); + data[length++] = (byte) i; + this.length = length; + return this; +} + +/** + * Puts a long into this byte vector. The byte vector is automatically + * enlarged if necessary. + * + * @param l a long. + * @return this byte vector. + */ +public ByteVector putLong(final long l){ + int length = this.length; + if(length + 8 > data.length) + { + enlarge(8); + } + byte[] data = this.data; + int i = (int) (l >>> 32); + data[length++] = (byte) (i >>> 24); + data[length++] = (byte) (i >>> 16); + data[length++] = (byte) (i >>> 8); + data[length++] = (byte) i; + i = (int) l; + data[length++] = (byte) (i >>> 24); + data[length++] = (byte) (i >>> 16); + data[length++] = (byte) (i >>> 8); + data[length++] = (byte) i; + this.length = length; + return this; +} + +/** + * Puts an UTF8 string into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param s a String. + * @return this byte vector. + */ +public ByteVector putUTF8(final String s){ + int charLength = s.length(); + if(length + 2 + charLength > data.length) + { + enlarge(2 + charLength); + } + int len = length; + byte[] data = this.data; + // optimistic algorithm: instead of computing the byte length and then + // serializing the string (which requires two loops), we assume the byte + // length is equal to char length (which is the most frequent case), and + // we start serializing the string right away. During the serialization, + // if we find that this assumption is wrong, we continue with the + // general method. + data[len++] = (byte) (charLength >>> 8); + data[len++] = (byte) charLength; + for(int i = 0; i < charLength; ++i) + { + char c = s.charAt(i); + if(c >= '\001' && c <= '\177') + { + data[len++] = (byte) c; + } + else + { + int byteLength = i; + for(int j = i; j < charLength; ++j) + { + c = s.charAt(j); + if(c >= '\001' && c <= '\177') + { + byteLength++; + } + else if(c > '\u07FF') + { + byteLength += 3; + } + else + { + byteLength += 2; + } + } + data[length] = (byte) (byteLength >>> 8); + data[length + 1] = (byte) byteLength; + if(length + 2 + byteLength > data.length) + { + length = len; + enlarge(2 + byteLength); + data = this.data; + } + for(int j = i; j < charLength; ++j) + { + c = s.charAt(j); + if(c >= '\001' && c <= '\177') + { + data[len++] = (byte) c; + } + else if(c > '\u07FF') + { + data[len++] = (byte) (0xE0 | c >> 12 & 0xF); + data[len++] = (byte) (0x80 | c >> 6 & 0x3F); + data[len++] = (byte) (0x80 | c & 0x3F); + } + else + { + data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); + data[len++] = (byte) (0x80 | c & 0x3F); + } + } + break; + } + } + length = len; + return this; +} + +/** + * Puts an array of bytes into this byte vector. The byte vector is + * automatically enlarged if necessary. + * + * @param b an array of bytes. May be null to put len + * null bytes into this byte vector. + * @param off index of the fist byte of b that must be copied. + * @param len number of bytes of b that must be copied. + * @return this byte vector. + */ +public ByteVector putByteArray(final byte[] b, final int off, final int len){ + if(length + len > data.length) + { + enlarge(len); + } + if(b != null) + { + System.arraycopy(b, off, data, length, len); + } + length += len; + return this; +} + +/** + * Enlarge this byte vector so that it can receive n more bytes. + * + * @param size number of additional bytes that this byte vector should be + * able to receive. + */ +private void enlarge(final int size){ + int length1 = 2 * data.length; + int length2 = length + size; + byte[] newData = new byte[length1 > length2 ? length1 : length2]; + System.arraycopy(data, 0, newData, 0, length); + data = newData; +} +} diff --git a/src/jvm/clojure/asm/ClassAdapter.java b/src/jvm/clojure/asm/ClassAdapter.java index d9b0d78..9de0979 100644 --- a/src/jvm/clojure/asm/ClassAdapter.java +++ b/src/jvm/clojure/asm/ClassAdapter.java @@ -1,115 +1,115 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * An empty {@link ClassVisitor} that delegates to another {@link ClassVisitor}. - * This class can be used as a super class to quickly implement usefull class - * adapter classes, just by overriding the necessary methods. - * - * @author Eric Bruneton - */ -public class ClassAdapter implements ClassVisitor{ - -/** - * The {@link ClassVisitor} to which this adapter delegates calls. - */ -protected ClassVisitor cv; - -/** - * Constructs a new {@link ClassAdapter} object. - * - * @param cv the class visitor to which this adapter must delegate calls. - */ -public ClassAdapter(final ClassVisitor cv){ - this.cv = cv; -} - -public void visit( - final int version, - final int access, - final String name, - final String signature, - final String superName, - final String[] interfaces){ - cv.visit(version, access, name, signature, superName, interfaces); -} - -public void visitSource(final String source, final String debug){ - cv.visitSource(source, debug); -} - -public void visitOuterClass( - final String owner, - final String name, - final String desc){ - cv.visitOuterClass(owner, name, desc); -} - -public AnnotationVisitor visitAnnotation( - final String desc, - final boolean visible){ - return cv.visitAnnotation(desc, visible); -} - -public void visitAttribute(final Attribute attr){ - cv.visitAttribute(attr); -} - -public void visitInnerClass( - final String name, - final String outerName, - final String innerName, - final int access){ - cv.visitInnerClass(name, outerName, innerName, access); -} - -public FieldVisitor visitField( - final int access, - final String name, - final String desc, - final String signature, - final Object value){ - return cv.visitField(access, name, desc, signature, value); -} - -public MethodVisitor visitMethod( - final int access, - final String name, - final String desc, - final String signature, - final String[] exceptions){ - return cv.visitMethod(access, name, desc, signature, exceptions); -} - -public void visitEnd(){ - cv.visitEnd(); -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * An empty {@link ClassVisitor} that delegates to another {@link ClassVisitor}. + * This class can be used as a super class to quickly implement usefull class + * adapter classes, just by overriding the necessary methods. + * + * @author Eric Bruneton + */ +public class ClassAdapter implements ClassVisitor{ + +/** + * The {@link ClassVisitor} to which this adapter delegates calls. + */ +protected ClassVisitor cv; + +/** + * Constructs a new {@link ClassAdapter} object. + * + * @param cv the class visitor to which this adapter must delegate calls. + */ +public ClassAdapter(final ClassVisitor cv){ + this.cv = cv; +} + +public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces){ + cv.visit(version, access, name, signature, superName, interfaces); +} + +public void visitSource(final String source, final String debug){ + cv.visitSource(source, debug); +} + +public void visitOuterClass( + final String owner, + final String name, + final String desc){ + cv.visitOuterClass(owner, name, desc); +} + +public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible){ + return cv.visitAnnotation(desc, visible); +} + +public void visitAttribute(final Attribute attr){ + cv.visitAttribute(attr); +} + +public void visitInnerClass( + final String name, + final String outerName, + final String innerName, + final int access){ + cv.visitInnerClass(name, outerName, innerName, access); +} + +public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value){ + return cv.visitField(access, name, desc, signature, value); +} + +public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions){ + return cv.visitMethod(access, name, desc, signature, exceptions); +} + +public void visitEnd(){ + cv.visitEnd(); +} +} diff --git a/src/jvm/clojure/asm/ClassReader.java b/src/jvm/clojure/asm/ClassReader.java index c9bec91..b216f70 100644 --- a/src/jvm/clojure/asm/ClassReader.java +++ b/src/jvm/clojure/asm/ClassReader.java @@ -1,2224 +1,2224 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -import java.io.InputStream; -import java.io.IOException; - -/** - * A Java class parser to make a {@link ClassVisitor} visit an existing class. - * This class parses a byte array conforming to the Java class file format and - * calls the appropriate visit methods of a given class visitor for each field, - * method and bytecode instruction encountered. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -public class ClassReader{ - -/** - * Flag to skip method code. If this class is set CODE - * attribute won't be visited. This can be used, for example, to retrieve - * annotations for methods and method parameters. - */ -public final static int SKIP_CODE = 1; - -/** - * Flag to skip the debug information in the class. If this flag is set the - * debug information of the class is not visited, i.e. the - * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and - * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be - * called. - */ -public final static int SKIP_DEBUG = 2; - -/** - * Flag to skip the stack map frames in the class. If this flag is set the - * stack map frames of the class is not visited, i.e. the - * {@link MethodVisitor#visitFrame visitFrame} method will not be called. - * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is - * used: it avoids visiting frames that will be ignored and recomputed from - * scratch in the class writer. - */ -public final static int SKIP_FRAMES = 4; - -/** - * Flag to expand the stack map frames. By default stack map frames are - * visited in their original format (i.e. "expanded" for classes whose - * version is less than V1_6, and "compressed" for the other classes). If - * this flag is set, stack map frames are always visited in expanded format - * (this option adds a decompression/recompression step in ClassReader and - * ClassWriter which degrades performances quite a lot). - */ -public final static int EXPAND_FRAMES = 8; - -/** - * The class to be parsed. The content of this array must not be - * modified. This field is intended for {@link Attribute} sub classes, and - * is normally not needed by class generators or adapters. - */ -public final byte[] b; - -/** - * The start index of each constant pool item in {@link #b b}, plus one. - * The one byte offset skips the constant pool item tag that indicates its - * type. - */ -private final int[] items; - -/** - * The String objects corresponding to the CONSTANT_Utf8 items. This cache - * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, - * which GREATLY improves performances (by a factor 2 to 3). This caching - * strategy could be extended to all constant pool items, but its benefit - * would not be so great for these items (because they are much less - * expensive to parse than CONSTANT_Utf8 items). - */ -private final String[] strings; - -/** - * Maximum length of the strings contained in the constant pool of the - * class. - */ -private final int maxStringLength; - -/** - * Start index of the class header information (access, name...) in - * {@link #b b}. - */ -public final int header; - -// ------------------------------------------------------------------------ -// Constructors -// ------------------------------------------------------------------------ - -/** - * Constructs a new {@link ClassReader} object. - * - * @param b the bytecode of the class to be read. - */ -public ClassReader(final byte[] b){ - this(b, 0, b.length); -} - -/** - * Constructs a new {@link ClassReader} object. - * - * @param b the bytecode of the class to be read. - * @param off the start offset of the class data. - * @param len the length of the class data. - */ -public ClassReader(final byte[] b, final int off, final int len){ - this.b = b; - // parses the constant pool - items = new int[readUnsignedShort(off + 8)]; - int n = items.length; - strings = new String[n]; - int max = 0; - int index = off + 10; - for(int i = 1; i < n; ++i) - { - items[i] = index + 1; - int size; - switch(b[index]) - { - case ClassWriter.FIELD: - case ClassWriter.METH: - case ClassWriter.IMETH: - case ClassWriter.INT: - case ClassWriter.FLOAT: - case ClassWriter.NAME_TYPE: - size = 5; - break; - case ClassWriter.LONG: - case ClassWriter.DOUBLE: - size = 9; - ++i; - break; - case ClassWriter.UTF8: - size = 3 + readUnsignedShort(index + 1); - if(size > max) - { - max = size; - } - break; - // case ClassWriter.CLASS: - // case ClassWriter.STR: - default: - size = 3; - break; - } - index += size; - } - maxStringLength = max; - // the class header information starts just after the constant pool - header = index; -} - -/** - * Returns the class's access flags (see {@link Opcodes}). This value may - * not reflect Deprecated and Synthetic flags when bytecode is before 1.5 - * and those flags are represented by attributes. - * - * @return the class access flags - * @see ClassVisitor#visit(int,int,String,String,String,String[]) - */ -public int getAccess(){ - return readUnsignedShort(header); -} - -/** - * Returns the internal name of the class (see - * {@link Type#getInternalName() getInternalName}). - * - * @return the internal class name - * @see ClassVisitor#visit(int,int,String,String,String,String[]) - */ -public String getClassName(){ - return readClass(header + 2, new char[maxStringLength]); -} - -/** - * Returns the internal of name of the super class (see - * {@link Type#getInternalName() getInternalName}). For interfaces, the - * super class is {@link Object}. - * - * @return the internal name of super class, or null for - * {@link Object} class. - * @see ClassVisitor#visit(int,int,String,String,String,String[]) - */ -public String getSuperName(){ - int n = items[readUnsignedShort(header + 4)]; - return n == 0 ? null : readUTF8(n, new char[maxStringLength]); -} - -/** - * Returns the internal names of the class's interfaces (see - * {@link Type#getInternalName() getInternalName}). - * - * @return the array of internal names for all implemented interfaces or - * null. - * @see ClassVisitor#visit(int,int,String,String,String,String[]) - */ -public String[] getInterfaces(){ - int index = header + 6; - int n = readUnsignedShort(index); - String[] interfaces = new String[n]; - if(n > 0) - { - char[] buf = new char[maxStringLength]; - for(int i = 0; i < n; ++i) - { - index += 2; - interfaces[i] = readClass(index, buf); - } - } - return interfaces; -} - -/** - * Copies the constant pool data into the given {@link ClassWriter}. Should - * be called before the {@link #accept(ClassVisitor,int)} method. - * - * @param classWriter the {@link ClassWriter} to copy constant pool into. - */ -void copyPool(final ClassWriter classWriter){ - char[] buf = new char[maxStringLength]; - int ll = items.length; - Item[] items2 = new Item[ll]; - for(int i = 1; i < ll; i++) - { - int index = items[i]; - int tag = b[index - 1]; - Item item = new Item(i); - int nameType; - switch(tag) - { - case ClassWriter.FIELD: - case ClassWriter.METH: - case ClassWriter.IMETH: - nameType = items[readUnsignedShort(index + 2)]; - item.set(tag, - readClass(index, buf), - readUTF8(nameType, buf), - readUTF8(nameType + 2, buf)); - break; - - case ClassWriter.INT: - item.set(readInt(index)); - break; - - case ClassWriter.FLOAT: - item.set(Float.intBitsToFloat(readInt(index))); - break; - - case ClassWriter.NAME_TYPE: - item.set(tag, - readUTF8(index, buf), - readUTF8(index + 2, buf), - null); - break; - - case ClassWriter.LONG: - item.set(readLong(index)); - ++i; - break; - - case ClassWriter.DOUBLE: - item.set(Double.longBitsToDouble(readLong(index))); - ++i; - break; - - case ClassWriter.UTF8: - { - String s = strings[i]; - if(s == null) - { - index = items[i]; - s = strings[i] = readUTF(index + 2, - readUnsignedShort(index), - buf); - } - item.set(tag, s, null, null); - } - break; - - // case ClassWriter.STR: - // case ClassWriter.CLASS: - default: - item.set(tag, readUTF8(index, buf), null, null); - break; - } - - int index2 = item.hashCode % items2.length; - item.next = items2[index2]; - items2[index2] = item; - } - - int off = items[1] - 1; - classWriter.pool.putByteArray(b, off, header - off); - classWriter.items = items2; - classWriter.threshold = (int) (0.75d * ll); - classWriter.index = ll; -} - -/** - * Constructs a new {@link ClassReader} object. - * - * @param is an input stream from which to read the class. - * @throws IOException if a problem occurs during reading. - */ -public ClassReader(final InputStream is) throws IOException{ - this(readClass(is)); -} - -/** - * Constructs a new {@link ClassReader} object. - * - * @param name the fully qualified name of the class to be read. - * @throws IOException if an exception occurs during reading. - */ -public ClassReader(final String name) throws IOException{ - this(ClassLoader.getSystemResourceAsStream(name.replace('.', '/') - + ".class")); -} - -/** - * Reads the bytecode of a class. - * - * @param is an input stream from which to read the class. - * @return the bytecode read from the given input stream. - * @throws IOException if a problem occurs during reading. - */ -private static byte[] readClass(final InputStream is) throws IOException{ - if(is == null) - { - throw new IOException("Class not found"); - } - byte[] b = new byte[is.available()]; - int len = 0; - while(true) - { - int n = is.read(b, len, b.length - len); - if(n == -1) - { - if(len < b.length) - { - byte[] c = new byte[len]; - System.arraycopy(b, 0, c, 0, len); - b = c; - } - return b; - } - len += n; - if(len == b.length) - { - byte[] c = new byte[b.length + 1000]; - System.arraycopy(b, 0, c, 0, len); - b = c; - } - } -} - -// ------------------------------------------------------------------------ -// Public methods -// ------------------------------------------------------------------------ - -/** - * Makes the given visitor visit the Java class of this {@link ClassReader}. - * This class is the one specified in the constructor (see - * {@link #ClassReader(byte[]) ClassReader}). - * - * @param classVisitor the visitor that must visit this class. - * @param flags option flags that can be used to modify the default behavior - * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}. - */ -public void accept(final ClassVisitor classVisitor, final int flags){ - accept(classVisitor, new Attribute[0], flags); -} - -/** - * Makes the given visitor visit the Java class of this {@link ClassReader}. - * This class is the one specified in the constructor (see - * {@link #ClassReader(byte[]) ClassReader}). - * - * @param classVisitor the visitor that must visit this class. - * @param attrs prototypes of the attributes that must be parsed during the - * visit of the class. Any attribute whose type is not equal to the - * type of one the prototypes will not be parsed: its byte array - * value will be passed unchanged to the ClassWriter. This may - * corrupt it if this value contains references to the constant pool, - * or has syntactic or semantic links with a class element that has - * been transformed by a class adapter between the reader and the - * writer. - * @param flags option flags that can be used to modify the default behavior - * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}. - */ -public void accept( - final ClassVisitor classVisitor, - final Attribute[] attrs, - final int flags){ - byte[] b = this.b; // the bytecode array - char[] c = new char[maxStringLength]; // buffer used to read strings - int i, j, k; // loop variables - int u, v, w; // indexes in b - Attribute attr; - - int access; - String name; - String desc; - String attrName; - String signature; - int anns = 0; - int ianns = 0; - Attribute cattrs = null; - - // visits the header - u = header; - access = readUnsignedShort(u); - name = readClass(u + 2, c); - v = items[readUnsignedShort(u + 4)]; - String superClassName = v == 0 ? null : readUTF8(v, c); - String[] implementedItfs = new String[readUnsignedShort(u + 6)]; - w = 0; - u += 8; - for(i = 0; i < implementedItfs.length; ++i) - { - implementedItfs[i] = readClass(u, c); - u += 2; - } - - boolean skipCode = (flags & SKIP_CODE) != 0; - boolean skipDebug = (flags & SKIP_DEBUG) != 0; - boolean unzip = (flags & EXPAND_FRAMES) != 0; - - // skips fields and methods - v = u; - i = readUnsignedShort(v); - v += 2; - for(; i > 0; --i) - { - j = readUnsignedShort(v + 6); - v += 8; - for(; j > 0; --j) - { - v += 6 + readInt(v + 2); - } - } - i = readUnsignedShort(v); - v += 2; - for(; i > 0; --i) - { - j = readUnsignedShort(v + 6); - v += 8; - for(; j > 0; --j) - { - v += 6 + readInt(v + 2); - } - } - // reads the class's attributes - signature = null; - String sourceFile = null; - String sourceDebug = null; - String enclosingOwner = null; - String enclosingName = null; - String enclosingDesc = null; - - i = readUnsignedShort(v); - v += 2; - for(; i > 0; --i) - { - attrName = readUTF8(v, c); - // tests are sorted in decreasing frequency order - // (based on frequencies observed on typical classes) - if(attrName.equals("SourceFile")) - { - sourceFile = readUTF8(v + 6, c); - } - else if(attrName.equals("InnerClasses")) - { - w = v + 6; - } - else if(attrName.equals("EnclosingMethod")) - { - enclosingOwner = readClass(v + 6, c); - int item = readUnsignedShort(v + 8); - if(item != 0) - { - enclosingName = readUTF8(items[item], c); - enclosingDesc = readUTF8(items[item] + 2, c); - } - } - else if(attrName.equals("Signature")) - { - signature = readUTF8(v + 6, c); - } - else if(attrName.equals("RuntimeVisibleAnnotations")) - { - anns = v + 6; - } - else if(attrName.equals("Deprecated")) - { - access |= Opcodes.ACC_DEPRECATED; - } - else if(attrName.equals("Synthetic")) - { - access |= Opcodes.ACC_SYNTHETIC; - } - else if(attrName.equals("SourceDebugExtension")) - { - int len = readInt(v + 2); - sourceDebug = readUTF(v + 6, len, new char[len]); - } - else if(attrName.equals("RuntimeInvisibleAnnotations")) - { - ianns = v + 6; - } - else - { - attr = readAttribute(attrs, - attrName, - v + 6, - readInt(v + 2), - c, - -1, - null); - if(attr != null) - { - attr.next = cattrs; - cattrs = attr; - } - } - v += 6 + readInt(v + 2); - } - // calls the visit method - classVisitor.visit(readInt(4), - access, - name, - signature, - superClassName, - implementedItfs); - - // calls the visitSource method - if(!skipDebug && (sourceFile != null || sourceDebug != null)) - { - classVisitor.visitSource(sourceFile, sourceDebug); - } - - // calls the visitOuterClass method - if(enclosingOwner != null) - { - classVisitor.visitOuterClass(enclosingOwner, - enclosingName, - enclosingDesc); - } - - // visits the class annotations - for(i = 1; i >= 0; --i) - { - v = i == 0 ? ianns : anns; - if(v != 0) - { - j = readUnsignedShort(v); - v += 2; - for(; j > 0; --j) - { - v = readAnnotationValues(v + 2, - c, - true, - classVisitor.visitAnnotation(readUTF8(v, c), i != 0)); - } - } - } - - // visits the class attributes - while(cattrs != null) - { - attr = cattrs.next; - cattrs.next = null; - classVisitor.visitAttribute(cattrs); - cattrs = attr; - } - - // calls the visitInnerClass method - if(w != 0) - { - i = readUnsignedShort(w); - w += 2; - for(; i > 0; --i) - { - classVisitor.visitInnerClass(readUnsignedShort(w) == 0 - ? null - : readClass(w, c), readUnsignedShort(w + 2) == 0 - ? null - : readClass(w + 2, c), readUnsignedShort(w + 4) == 0 - ? null - : readUTF8(w + 4, c), - readUnsignedShort(w + 6)); - w += 8; - } - } - - // visits the fields - i = readUnsignedShort(u); - u += 2; - for(; i > 0; --i) - { - access = readUnsignedShort(u); - name = readUTF8(u + 2, c); - desc = readUTF8(u + 4, c); - // visits the field's attributes and looks for a ConstantValue - // attribute - int fieldValueItem = 0; - signature = null; - anns = 0; - ianns = 0; - cattrs = null; - - j = readUnsignedShort(u + 6); - u += 8; - for(; j > 0; --j) - { - attrName = readUTF8(u, c); - // tests are sorted in decreasing frequency order - // (based on frequencies observed on typical classes) - if(attrName.equals("ConstantValue")) - { - fieldValueItem = readUnsignedShort(u + 6); - } - else if(attrName.equals("Signature")) - { - signature = readUTF8(u + 6, c); - } - else if(attrName.equals("Deprecated")) - { - access |= Opcodes.ACC_DEPRECATED; - } - else if(attrName.equals("Synthetic")) - { - access |= Opcodes.ACC_SYNTHETIC; - } - else if(attrName.equals("RuntimeVisibleAnnotations")) - { - anns = u + 6; - } - else if(attrName.equals("RuntimeInvisibleAnnotations")) - { - ianns = u + 6; - } - else - { - attr = readAttribute(attrs, - attrName, - u + 6, - readInt(u + 2), - c, - -1, - null); - if(attr != null) - { - attr.next = cattrs; - cattrs = attr; - } - } - u += 6 + readInt(u + 2); - } - // visits the field - FieldVisitor fv = classVisitor.visitField(access, - name, - desc, - signature, - fieldValueItem == 0 ? null : readConst(fieldValueItem, c)); - // visits the field annotations and attributes - if(fv != null) - { - for(j = 1; j >= 0; --j) - { - v = j == 0 ? ianns : anns; - if(v != 0) - { - k = readUnsignedShort(v); - v += 2; - for(; k > 0; --k) - { - v = readAnnotationValues(v + 2, - c, - true, - fv.visitAnnotation(readUTF8(v, c), j != 0)); - } - } - } - while(cattrs != null) - { - attr = cattrs.next; - cattrs.next = null; - fv.visitAttribute(cattrs); - cattrs = attr; - } - fv.visitEnd(); - } - } - - // visits the methods - i = readUnsignedShort(u); - u += 2; - for(; i > 0; --i) - { - int u0 = u + 6; - access = readUnsignedShort(u); - name = readUTF8(u + 2, c); - desc = readUTF8(u + 4, c); - signature = null; - anns = 0; - ianns = 0; - int dann = 0; - int mpanns = 0; - int impanns = 0; - cattrs = null; - v = 0; - w = 0; - - // looks for Code and Exceptions attributes - j = readUnsignedShort(u + 6); - u += 8; - for(; j > 0; --j) - { - attrName = readUTF8(u, c); - int attrSize = readInt(u + 2); - u += 6; - // tests are sorted in decreasing frequency order - // (based on frequencies observed on typical classes) - if(attrName.equals("Code")) - { - if(!skipCode) - { - v = u; - } - } - else if(attrName.equals("Exceptions")) - { - w = u; - } - else if(attrName.equals("Signature")) - { - signature = readUTF8(u, c); - } - else if(attrName.equals("Deprecated")) - { - access |= Opcodes.ACC_DEPRECATED; - } - else if(attrName.equals("RuntimeVisibleAnnotations")) - { - anns = u; - } - else if(attrName.equals("AnnotationDefault")) - { - dann = u; - } - else if(attrName.equals("Synthetic")) - { - access |= Opcodes.ACC_SYNTHETIC; - } - else if(attrName.equals("RuntimeInvisibleAnnotations")) - { - ianns = u; - } - else if(attrName.equals("RuntimeVisibleParameterAnnotations")) - { - mpanns = u; - } - else if(attrName.equals("RuntimeInvisibleParameterAnnotations")) - { - impanns = u; - } - else - { - attr = readAttribute(attrs, - attrName, - u, - attrSize, - c, - -1, - null); - if(attr != null) - { - attr.next = cattrs; - cattrs = attr; - } - } - u += attrSize; - } - // reads declared exceptions - String[] exceptions; - if(w == 0) - { - exceptions = null; - } - else - { - exceptions = new String[readUnsignedShort(w)]; - w += 2; - for(j = 0; j < exceptions.length; ++j) - { - exceptions[j] = readClass(w, c); - w += 2; - } - } - - // visits the method's code, if any - MethodVisitor mv = classVisitor.visitMethod(access, - name, - desc, - signature, - exceptions); - - if(mv != null) - { - /* - * if the returned MethodVisitor is in fact a MethodWriter, it - * means there is no method adapter between the reader and the - * writer. If, in addition, the writer's constant pool was - * copied from this reader (mw.cw.cr == this), and the signature - * and exceptions of the method have not been changed, then it - * is possible to skip all visit events and just copy the - * original code of the method to the writer (the access, name - * and descriptor can have been changed, this is not important - * since they are not copied as is from the reader). - */ - if(mv instanceof MethodWriter) - { - MethodWriter mw = (MethodWriter) mv; - if(mw.cw.cr == this) - { - if(signature == mw.signature) - { - boolean sameExceptions = false; - if(exceptions == null) - { - sameExceptions = mw.exceptionCount == 0; - } - else - { - if(exceptions.length == mw.exceptionCount) - { - sameExceptions = true; - for(j = exceptions.length - 1; j >= 0; --j) - { - w -= 2; - if(mw.exceptions[j] != readUnsignedShort(w)) - { - sameExceptions = false; - break; - } - } - } - } - if(sameExceptions) - { - /* - * we do not copy directly the code into - * MethodWriter to save a byte array copy - * operation. The real copy will be done in - * ClassWriter.toByteArray(). - */ - mw.classReaderOffset = u0; - mw.classReaderLength = u - u0; - continue; - } - } - } - } - - if(dann != 0) - { - AnnotationVisitor dv = mv.visitAnnotationDefault(); - readAnnotationValue(dann, c, null, dv); - if(dv != null) - { - dv.visitEnd(); - } - } - for(j = 1; j >= 0; --j) - { - w = j == 0 ? ianns : anns; - if(w != 0) - { - k = readUnsignedShort(w); - w += 2; - for(; k > 0; --k) - { - w = readAnnotationValues(w + 2, - c, - true, - mv.visitAnnotation(readUTF8(w, c), j != 0)); - } - } - } - if(mpanns != 0) - { - readParameterAnnotations(mpanns, c, true, mv); - } - if(impanns != 0) - { - readParameterAnnotations(impanns, c, false, mv); - } - while(cattrs != null) - { - attr = cattrs.next; - cattrs.next = null; - mv.visitAttribute(cattrs); - cattrs = attr; - } - } - - if(mv != null && v != 0) - { - int maxStack = readUnsignedShort(v); - int maxLocals = readUnsignedShort(v + 2); - int codeLength = readInt(v + 4); - v += 8; - - int codeStart = v; - int codeEnd = v + codeLength; - - mv.visitCode(); - - // 1st phase: finds the labels - int label; - Label[] labels = new Label[codeLength + 1]; - while(v < codeEnd) - { - int opcode = b[v] & 0xFF; - switch(ClassWriter.TYPE[opcode]) - { - case ClassWriter.NOARG_INSN: - case ClassWriter.IMPLVAR_INSN: - v += 1; - break; - case ClassWriter.LABEL_INSN: - label = v - codeStart + readShort(v + 1); - if(labels[label] == null) - { - labels[label] = new Label(); - } - v += 3; - break; - case ClassWriter.LABELW_INSN: - label = v - codeStart + readInt(v + 1); - if(labels[label] == null) - { - labels[label] = new Label(); - } - v += 5; - break; - case ClassWriter.WIDE_INSN: - opcode = b[v + 1] & 0xFF; - if(opcode == Opcodes.IINC) - { - v += 6; - } - else - { - v += 4; - } - break; - case ClassWriter.TABL_INSN: - // skips 0 to 3 padding bytes - w = v - codeStart; - v = v + 4 - (w & 3); - // reads instruction - label = w + readInt(v); - if(labels[label] == null) - { - labels[label] = new Label(); - } - j = readInt(v + 8) - readInt(v + 4) + 1; - v += 12; - for(; j > 0; --j) - { - label = w + readInt(v); - v += 4; - if(labels[label] == null) - { - labels[label] = new Label(); - } - } - break; - case ClassWriter.LOOK_INSN: - // skips 0 to 3 padding bytes - w = v - codeStart; - v = v + 4 - (w & 3); - // reads instruction - label = w + readInt(v); - if(labels[label] == null) - { - labels[label] = new Label(); - } - j = readInt(v + 4); - v += 8; - for(; j > 0; --j) - { - label = w + readInt(v + 4); - v += 8; - if(labels[label] == null) - { - labels[label] = new Label(); - } - } - break; - case ClassWriter.VAR_INSN: - case ClassWriter.SBYTE_INSN: - case ClassWriter.LDC_INSN: - v += 2; - break; - case ClassWriter.SHORT_INSN: - case ClassWriter.LDCW_INSN: - case ClassWriter.FIELDORMETH_INSN: - case ClassWriter.TYPE_INSN: - case ClassWriter.IINC_INSN: - v += 3; - break; - case ClassWriter.ITFMETH_INSN: - v += 5; - break; - // case MANA_INSN: - default: - v += 4; - break; - } - } - // parses the try catch entries - j = readUnsignedShort(v); - v += 2; - for(; j > 0; --j) - { - label = readUnsignedShort(v); - Label start = labels[label]; - if(start == null) - { - labels[label] = start = new Label(); - } - label = readUnsignedShort(v + 2); - Label end = labels[label]; - if(end == null) - { - labels[label] = end = new Label(); - } - label = readUnsignedShort(v + 4); - Label handler = labels[label]; - if(handler == null) - { - labels[label] = handler = new Label(); - } - int type = readUnsignedShort(v + 6); - if(type == 0) - { - mv.visitTryCatchBlock(start, end, handler, null); - } - else - { - mv.visitTryCatchBlock(start, - end, - handler, - readUTF8(items[type], c)); - } - v += 8; - } - // parses the local variable, line number tables, and code - // attributes - int varTable = 0; - int varTypeTable = 0; - int stackMap = 0; - int frameCount = 0; - int frameMode = 0; - int frameOffset = 0; - int frameLocalCount = 0; - int frameLocalDiff = 0; - int frameStackCount = 0; - Object[] frameLocal = null; - Object[] frameStack = null; - boolean zip = true; - cattrs = null; - j = readUnsignedShort(v); - v += 2; - for(; j > 0; --j) - { - attrName = readUTF8(v, c); - if(attrName.equals("LocalVariableTable")) - { - if(!skipDebug) - { - varTable = v + 6; - k = readUnsignedShort(v + 6); - w = v + 8; - for(; k > 0; --k) - { - label = readUnsignedShort(w); - if(labels[label] == null) - { - labels[label] = new Label(true); - } - label += readUnsignedShort(w + 2); - if(labels[label] == null) - { - labels[label] = new Label(true); - } - w += 10; - } - } - } - else if(attrName.equals("LocalVariableTypeTable")) - { - varTypeTable = v + 6; - } - else if(attrName.equals("LineNumberTable")) - { - if(!skipDebug) - { - k = readUnsignedShort(v + 6); - w = v + 8; - for(; k > 0; --k) - { - label = readUnsignedShort(w); - if(labels[label] == null) - { - labels[label] = new Label(true); - } - labels[label].line = readUnsignedShort(w + 2); - w += 4; - } - } - } - else if(attrName.equals("StackMapTable")) - { - if((flags & SKIP_FRAMES) == 0) - { - stackMap = v + 8; - frameCount = readUnsignedShort(v + 6); - } - /* - * here we do not extract the labels corresponding to - * the attribute content. This would require a full - * parsing of the attribute, which would need to be - * repeated in the second phase (see below). Instead the - * content of the attribute is read one frame at a time - * (i.e. after a frame has been visited, the next frame - * is read), and the labels it contains are also - * extracted one frame at a time. Thanks to the ordering - * of frames, having only a "one frame lookahead" is not - * a problem, i.e. it is not possible to see an offset - * smaller than the offset of the current insn and for - * which no Label exist. - */ - // TODO true for frame offsets, - // but for UNINITIALIZED type offsets? - } - else if(attrName.equals("StackMap")) - { - if((flags & SKIP_FRAMES) == 0) - { - stackMap = v + 8; - frameCount = readUnsignedShort(v + 6); - zip = false; - } - /* - * IMPORTANT! here we assume that the frames are - * ordered, as in the StackMapTable attribute, although - * this is not guaranteed by the attribute format. - */ - } - else - { - for(k = 0; k < attrs.length; ++k) - { - if(attrs[k].type.equals(attrName)) - { - attr = attrs[k].read(this, - v + 6, - readInt(v + 2), - c, - codeStart - 8, - labels); - if(attr != null) - { - attr.next = cattrs; - cattrs = attr; - } - } - } - } - v += 6 + readInt(v + 2); - } - - // 2nd phase: visits each instruction - if(stackMap != 0) - { - // creates the very first (implicit) frame from the method - // descriptor - frameLocal = new Object[maxLocals]; - frameStack = new Object[maxStack]; - if(unzip) - { - int local = 0; - if((access & Opcodes.ACC_STATIC) == 0) - { - if(name.equals("")) - { - frameLocal[local++] = Opcodes.UNINITIALIZED_THIS; - } - else - { - frameLocal[local++] = readClass(header + 2, c); - } - } - j = 1; - loop: - while(true) - { - k = j; - switch(desc.charAt(j++)) - { - case'Z': - case'C': - case'B': - case'S': - case'I': - frameLocal[local++] = Opcodes.INTEGER; - break; - case'F': - frameLocal[local++] = Opcodes.FLOAT; - break; - case'J': - frameLocal[local++] = Opcodes.LONG; - break; - case'D': - frameLocal[local++] = Opcodes.DOUBLE; - break; - case'[': - while(desc.charAt(j) == '[') - { - ++j; - } - if(desc.charAt(j) == 'L') - { - ++j; - while(desc.charAt(j) != ';') - { - ++j; - } - } - frameLocal[local++] = desc.substring(k, ++j); - break; - case'L': - while(desc.charAt(j) != ';') - { - ++j; - } - frameLocal[local++] = desc.substring(k + 1, - j++); - break; - default: - break loop; - } - } - frameLocalCount = local; - } - /* - * for the first explicit frame the offset is not - * offset_delta + 1 but only offset_delta; setting the - * implicit frame offset to -1 allow the use of the - * "offset_delta + 1" rule in all cases - */ - frameOffset = -1; - } - v = codeStart; - Label l; - while(v < codeEnd) - { - w = v - codeStart; - - l = labels[w]; - if(l != null) - { - mv.visitLabel(l); - if(!skipDebug && l.line > 0) - { - mv.visitLineNumber(l.line, l); - } - } - - while(frameLocal != null - && (frameOffset == w || frameOffset == -1)) - { - // if there is a frame for this offset, - // makes the visitor visit it, - // and reads the next frame if there is one. - if(!zip || unzip) - { - mv.visitFrame(Opcodes.F_NEW, - frameLocalCount, - frameLocal, - frameStackCount, - frameStack); - } - else if(frameOffset != -1) - { - mv.visitFrame(frameMode, - frameLocalDiff, - frameLocal, - frameStackCount, - frameStack); - } - - if(frameCount > 0) - { - int tag, delta, n; - if(zip) - { - tag = b[stackMap++] & 0xFF; - } - else - { - tag = MethodWriter.FULL_FRAME; - frameOffset = -1; - } - frameLocalDiff = 0; - if(tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) - { - delta = tag; - frameMode = Opcodes.F_SAME; - frameStackCount = 0; - } - else if(tag < MethodWriter.RESERVED) - { - delta = tag - - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME; - stackMap = readFrameType(frameStack, - 0, - stackMap, - c, - labels); - frameMode = Opcodes.F_SAME1; - frameStackCount = 1; - } - else - { - delta = readUnsignedShort(stackMap); - stackMap += 2; - if(tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) - { - stackMap = readFrameType(frameStack, - 0, - stackMap, - c, - labels); - frameMode = Opcodes.F_SAME1; - frameStackCount = 1; - } - else if(tag >= MethodWriter.CHOP_FRAME - && tag < MethodWriter.SAME_FRAME_EXTENDED) - { - frameMode = Opcodes.F_CHOP; - frameLocalDiff = MethodWriter.SAME_FRAME_EXTENDED - - tag; - frameLocalCount -= frameLocalDiff; - frameStackCount = 0; - } - else if(tag == MethodWriter.SAME_FRAME_EXTENDED) - { - frameMode = Opcodes.F_SAME; - frameStackCount = 0; - } - else if(tag < MethodWriter.FULL_FRAME) - { - j = unzip ? frameLocalCount : 0; - for(k = tag - - MethodWriter.SAME_FRAME_EXTENDED; k > 0; k--) - { - stackMap = readFrameType(frameLocal, - j++, - stackMap, - c, - labels); - } - frameMode = Opcodes.F_APPEND; - frameLocalDiff = tag - - MethodWriter.SAME_FRAME_EXTENDED; - frameLocalCount += frameLocalDiff; - frameStackCount = 0; - } - else - { // if (tag == FULL_FRAME) { - frameMode = Opcodes.F_FULL; - n = frameLocalDiff = frameLocalCount = readUnsignedShort(stackMap); - stackMap += 2; - for(j = 0; n > 0; n--) - { - stackMap = readFrameType(frameLocal, - j++, - stackMap, - c, - labels); - } - n = frameStackCount = readUnsignedShort(stackMap); - stackMap += 2; - for(j = 0; n > 0; n--) - { - stackMap = readFrameType(frameStack, - j++, - stackMap, - c, - labels); - } - } - } - frameOffset += delta + 1; - if(labels[frameOffset] == null) - { - labels[frameOffset] = new Label(); - } - - --frameCount; - } - else - { - frameLocal = null; - } - } - - int opcode = b[v] & 0xFF; - switch(ClassWriter.TYPE[opcode]) - { - case ClassWriter.NOARG_INSN: - mv.visitInsn(opcode); - v += 1; - break; - case ClassWriter.IMPLVAR_INSN: - if(opcode > Opcodes.ISTORE) - { - opcode -= 59; // ISTORE_0 - mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), - opcode & 0x3); - } - else - { - opcode -= 26; // ILOAD_0 - mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), - opcode & 0x3); - } - v += 1; - break; - case ClassWriter.LABEL_INSN: - mv.visitJumpInsn(opcode, labels[w - + readShort(v + 1)]); - v += 3; - break; - case ClassWriter.LABELW_INSN: - mv.visitJumpInsn(opcode - 33, labels[w - + readInt(v + 1)]); - v += 5; - break; - case ClassWriter.WIDE_INSN: - opcode = b[v + 1] & 0xFF; - if(opcode == Opcodes.IINC) - { - mv.visitIincInsn(readUnsignedShort(v + 2), - readShort(v + 4)); - v += 6; - } - else - { - mv.visitVarInsn(opcode, - readUnsignedShort(v + 2)); - v += 4; - } - break; - case ClassWriter.TABL_INSN: - // skips 0 to 3 padding bytes - v = v + 4 - (w & 3); - // reads instruction - label = w + readInt(v); - int min = readInt(v + 4); - int max = readInt(v + 8); - v += 12; - Label[] table = new Label[max - min + 1]; - for(j = 0; j < table.length; ++j) - { - table[j] = labels[w + readInt(v)]; - v += 4; - } - mv.visitTableSwitchInsn(min, - max, - labels[label], - table); - break; - case ClassWriter.LOOK_INSN: - // skips 0 to 3 padding bytes - v = v + 4 - (w & 3); - // reads instruction - label = w + readInt(v); - j = readInt(v + 4); - v += 8; - int[] keys = new int[j]; - Label[] values = new Label[j]; - for(j = 0; j < keys.length; ++j) - { - keys[j] = readInt(v); - values[j] = labels[w + readInt(v + 4)]; - v += 8; - } - mv.visitLookupSwitchInsn(labels[label], - keys, - values); - break; - case ClassWriter.VAR_INSN: - mv.visitVarInsn(opcode, b[v + 1] & 0xFF); - v += 2; - break; - case ClassWriter.SBYTE_INSN: - mv.visitIntInsn(opcode, b[v + 1]); - v += 2; - break; - case ClassWriter.SHORT_INSN: - mv.visitIntInsn(opcode, readShort(v + 1)); - v += 3; - break; - case ClassWriter.LDC_INSN: - mv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c)); - v += 2; - break; - case ClassWriter.LDCW_INSN: - mv.visitLdcInsn(readConst(readUnsignedShort(v + 1), - c)); - v += 3; - break; - case ClassWriter.FIELDORMETH_INSN: - case ClassWriter.ITFMETH_INSN: - int cpIndex = items[readUnsignedShort(v + 1)]; - String iowner = readClass(cpIndex, c); - cpIndex = items[readUnsignedShort(cpIndex + 2)]; - String iname = readUTF8(cpIndex, c); - String idesc = readUTF8(cpIndex + 2, c); - if(opcode < Opcodes.INVOKEVIRTUAL) - { - mv.visitFieldInsn(opcode, iowner, iname, idesc); - } - else - { - mv.visitMethodInsn(opcode, iowner, iname, idesc); - } - if(opcode == Opcodes.INVOKEINTERFACE) - { - v += 5; - } - else - { - v += 3; - } - break; - case ClassWriter.TYPE_INSN: - mv.visitTypeInsn(opcode, readClass(v + 1, c)); - v += 3; - break; - case ClassWriter.IINC_INSN: - mv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]); - v += 3; - break; - // case MANA_INSN: - default: - mv.visitMultiANewArrayInsn(readClass(v + 1, c), - b[v + 3] & 0xFF); - v += 4; - break; - } - } - l = labels[codeEnd - codeStart]; - if(l != null) - { - mv.visitLabel(l); - } - // visits the local variable tables - if(!skipDebug && varTable != 0) - { - int[] typeTable = null; - if(varTypeTable != 0) - { - k = readUnsignedShort(varTypeTable) * 3; - w = varTypeTable + 2; - typeTable = new int[k]; - while(k > 0) - { - typeTable[--k] = w + 6; // signature - typeTable[--k] = readUnsignedShort(w + 8); // index - typeTable[--k] = readUnsignedShort(w); // start - w += 10; - } - } - k = readUnsignedShort(varTable); - w = varTable + 2; - for(; k > 0; --k) - { - int start = readUnsignedShort(w); - int length = readUnsignedShort(w + 2); - int index = readUnsignedShort(w + 8); - String vsignature = null; - if(typeTable != null) - { - for(int a = 0; a < typeTable.length; a += 3) - { - if(typeTable[a] == start - && typeTable[a + 1] == index) - { - vsignature = readUTF8(typeTable[a + 2], c); - break; - } - } - } - mv.visitLocalVariable(readUTF8(w + 4, c), - readUTF8(w + 6, c), - vsignature, - labels[start], - labels[start + length], - index); - w += 10; - } - } - // visits the other attributes - while(cattrs != null) - { - attr = cattrs.next; - cattrs.next = null; - mv.visitAttribute(cattrs); - cattrs = attr; - } - // visits the max stack and max locals values - mv.visitMaxs(maxStack, maxLocals); - } - - if(mv != null) - { - mv.visitEnd(); - } - } - - // visits the end of the class - classVisitor.visitEnd(); -} - -/** - * Reads parameter annotations and makes the given visitor visit them. - * - * @param v start offset in {@link #b b} of the annotations to be read. - * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, - * {@link #readClass(int,char[]) readClass} or - * {@link #readConst readConst}. - * @param visible true if the annotations to be read are visible - * at runtime. - * @param mv the visitor that must visit the annotations. - */ -private void readParameterAnnotations( - int v, - final char[] buf, - final boolean visible, - final MethodVisitor mv){ - int n = b[v++] & 0xFF; - for(int i = 0; i < n; ++i) - { - int j = readUnsignedShort(v); - v += 2; - for(; j > 0; --j) - { - v = readAnnotationValues(v + 2, - buf, - true, - mv.visitParameterAnnotation(i, - readUTF8(v, buf), - visible)); - } - } -} - -/** - * Reads the values of an annotation and makes the given visitor visit them. - * - * @param v the start offset in {@link #b b} of the values to be read - * (including the unsigned short that gives the number of values). - * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, - * {@link #readClass(int,char[]) readClass} or - * {@link #readConst readConst}. - * @param named if the annotation values are named or not. - * @param av the visitor that must visit the values. - * @return the end offset of the annotation values. - */ -private int readAnnotationValues( - int v, - final char[] buf, - final boolean named, - final AnnotationVisitor av){ - int i = readUnsignedShort(v); - v += 2; - if(named) - { - for(; i > 0; --i) - { - v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av); - } - } - else - { - for(; i > 0; --i) - { - v = readAnnotationValue(v, buf, null, av); - } - } - if(av != null) - { - av.visitEnd(); - } - return v; -} - -/** - * Reads a value of an annotation and makes the given visitor visit it. - * - * @param v the start offset in {@link #b b} of the value to be read (not - * including the value name constant pool index). - * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, - * {@link #readClass(int,char[]) readClass} or - * {@link #readConst readConst}. - * @param name the name of the value to be read. - * @param av the visitor that must visit the value. - * @return the end offset of the annotation value. - */ -private int readAnnotationValue( - int v, - final char[] buf, - final String name, - final AnnotationVisitor av){ - int i; - if(av == null) - { - switch(b[v] & 0xFF) - { - case'e': // enum_const_value - return v + 5; - case'@': // annotation_value - return readAnnotationValues(v + 3, buf, true, null); - case'[': // array_value - return readAnnotationValues(v + 1, buf, false, null); - default: - return v + 3; - } - } - switch(b[v++] & 0xFF) - { - case'I': // pointer to CONSTANT_Integer - case'J': // pointer to CONSTANT_Long - case'F': // pointer to CONSTANT_Float - case'D': // pointer to CONSTANT_Double - av.visit(name, readConst(readUnsignedShort(v), buf)); - v += 2; - break; - case'B': // pointer to CONSTANT_Byte - av.visit(name, - new Byte((byte) readInt(items[readUnsignedShort(v)]))); - v += 2; - break; - case'Z': // pointer to CONSTANT_Boolean - av.visit(name, readInt(items[readUnsignedShort(v)]) == 0 - ? Boolean.FALSE - : Boolean.TRUE); - v += 2; - break; - case'S': // pointer to CONSTANT_Short - av.visit(name, - new Short((short) readInt(items[readUnsignedShort(v)]))); - v += 2; - break; - case'C': // pointer to CONSTANT_Char - av.visit(name, - new Character((char) readInt(items[readUnsignedShort(v)]))); - v += 2; - break; - case's': // pointer to CONSTANT_Utf8 - av.visit(name, readUTF8(v, buf)); - v += 2; - break; - case'e': // enum_const_value - av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf)); - v += 4; - break; - case'c': // class_info - av.visit(name, Type.getType(readUTF8(v, buf))); - v += 2; - break; - case'@': // annotation_value - v = readAnnotationValues(v + 2, - buf, - true, - av.visitAnnotation(name, readUTF8(v, buf))); - break; - case'[': // array_value - int size = readUnsignedShort(v); - v += 2; - if(size == 0) - { - return readAnnotationValues(v - 2, - buf, - false, - av.visitArray(name)); - } - switch(this.b[v++] & 0xFF) - { - case'B': - byte[] bv = new byte[size]; - for(i = 0; i < size; i++) - { - bv[i] = (byte) readInt(items[readUnsignedShort(v)]); - v += 3; - } - av.visit(name, bv); - --v; - break; - case'Z': - boolean[] zv = new boolean[size]; - for(i = 0; i < size; i++) - { - zv[i] = readInt(items[readUnsignedShort(v)]) != 0; - v += 3; - } - av.visit(name, zv); - --v; - break; - case'S': - short[] sv = new short[size]; - for(i = 0; i < size; i++) - { - sv[i] = (short) readInt(items[readUnsignedShort(v)]); - v += 3; - } - av.visit(name, sv); - --v; - break; - case'C': - char[] cv = new char[size]; - for(i = 0; i < size; i++) - { - cv[i] = (char) readInt(items[readUnsignedShort(v)]); - v += 3; - } - av.visit(name, cv); - --v; - break; - case'I': - int[] iv = new int[size]; - for(i = 0; i < size; i++) - { - iv[i] = readInt(items[readUnsignedShort(v)]); - v += 3; - } - av.visit(name, iv); - --v; - break; - case'J': - long[] lv = new long[size]; - for(i = 0; i < size; i++) - { - lv[i] = readLong(items[readUnsignedShort(v)]); - v += 3; - } - av.visit(name, lv); - --v; - break; - case'F': - float[] fv = new float[size]; - for(i = 0; i < size; i++) - { - fv[i] = Float.intBitsToFloat(readInt(items[readUnsignedShort(v)])); - v += 3; - } - av.visit(name, fv); - --v; - break; - case'D': - double[] dv = new double[size]; - for(i = 0; i < size; i++) - { - dv[i] = Double.longBitsToDouble(readLong(items[readUnsignedShort(v)])); - v += 3; - } - av.visit(name, dv); - --v; - break; - default: - v = readAnnotationValues(v - 3, - buf, - false, - av.visitArray(name)); - } - } - return v; -} - -private int readFrameType( - final Object[] frame, - final int index, - int v, - final char[] buf, - final Label[] labels){ - int type = b[v++] & 0xFF; - switch(type) - { - case 0: - frame[index] = Opcodes.TOP; - break; - case 1: - frame[index] = Opcodes.INTEGER; - break; - case 2: - frame[index] = Opcodes.FLOAT; - break; - case 3: - frame[index] = Opcodes.DOUBLE; - break; - case 4: - frame[index] = Opcodes.LONG; - break; - case 5: - frame[index] = Opcodes.NULL; - break; - case 6: - frame[index] = Opcodes.UNINITIALIZED_THIS; - break; - case 7: // Object - frame[index] = readClass(v, buf); - v += 2; - break; - default: // Uninitialized - int offset = readUnsignedShort(v); - if(labels[offset] == null) - { - labels[offset] = new Label(); - } - frame[index] = labels[offset]; - v += 2; - } - return v; -} - -/** - * Reads an attribute in {@link #b b}. - * - * @param attrs prototypes of the attributes that must be parsed during the - * visit of the class. Any attribute whose type is not equal to the - * type of one the prototypes is ignored (i.e. an empty - * {@link Attribute} instance is returned). - * @param type the type of the attribute. - * @param off index of the first byte of the attribute's content in - * {@link #b b}. The 6 attribute header bytes, containing the type - * and the length of the attribute, are not taken into account here - * (they have already been read). - * @param len the length of the attribute's content. - * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, - * {@link #readClass(int,char[]) readClass} or - * {@link #readConst readConst}. - * @param codeOff index of the first byte of code's attribute content in - * {@link #b b}, or -1 if the attribute to be read is not a code - * attribute. The 6 attribute header bytes, containing the type and - * the length of the attribute, are not taken into account here. - * @param labels the labels of the method's code, or null if the - * attribute to be read is not a code attribute. - * @return the attribute that has been read, or null to skip this - * attribute. - */ -private Attribute readAttribute( - final Attribute[] attrs, - final String type, - final int off, - final int len, - final char[] buf, - final int codeOff, - final Label[] labels){ - for(int i = 0; i < attrs.length; ++i) - { - if(attrs[i].type.equals(type)) - { - return attrs[i].read(this, off, len, buf, codeOff, labels); - } - } - return new Attribute(type).read(this, off, len, null, -1, null); -} - -// ------------------------------------------------------------------------ -// Utility methods: low level parsing -// ------------------------------------------------------------------------ - -/** - * Returns the start index of the constant pool item in {@link #b b}, plus - * one. This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param item the index a constant pool item. - * @return the start index of the constant pool item in {@link #b b}, plus - * one. - */ -public int getItem(final int item){ - return items[item]; -} - -/** - * Reads a byte value in {@link #b b}. This method is intended for - * {@link Attribute} sub classes, and is normally not needed by class - * generators or adapters. - * - * @param index the start index of the value to be read in {@link #b b}. - * @return the read value. - */ -public int readByte(final int index){ - return b[index] & 0xFF; -} - -/** - * Reads an unsigned short value in {@link #b b}. This method is - * intended for {@link Attribute} sub classes, and is normally not needed by - * class generators or adapters. - * - * @param index the start index of the value to be read in {@link #b b}. - * @return the read value. - */ -public int readUnsignedShort(final int index){ - byte[] b = this.b; - return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); -} - -/** - * Reads a signed short value in {@link #b b}. This method is intended - * for {@link Attribute} sub classes, and is normally not needed by class - * generators or adapters. - * - * @param index the start index of the value to be read in {@link #b b}. - * @return the read value. - */ -public short readShort(final int index){ - byte[] b = this.b; - return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); -} - -/** - * Reads a signed int value in {@link #b b}. This method is intended for - * {@link Attribute} sub classes, and is normally not needed by class - * generators or adapters. - * - * @param index the start index of the value to be read in {@link #b b}. - * @return the read value. - */ -public int readInt(final int index){ - byte[] b = this.b; - return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) - | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); -} - -/** - * Reads a signed long value in {@link #b b}. This method is intended - * for {@link Attribute} sub classes, and is normally not needed by class - * generators or adapters. - * - * @param index the start index of the value to be read in {@link #b b}. - * @return the read value. - */ -public long readLong(final int index){ - long l1 = readInt(index); - long l0 = readInt(index + 4) & 0xFFFFFFFFL; - return (l1 << 32) | l0; -} - -/** - * Reads an UTF8 string constant pool item in {@link #b b}. This method - * is intended for {@link Attribute} sub classes, and is normally not needed - * by class generators or adapters. - * - * @param index the start index of an unsigned short value in {@link #b b}, - * whose value is the index of an UTF8 constant pool item. - * @param buf buffer to be used to read the item. This buffer must be - * sufficiently large. It is not automatically resized. - * @return the String corresponding to the specified UTF8 item. - */ -public String readUTF8(int index, final char[] buf){ - int item = readUnsignedShort(index); - String s = strings[item]; - if(s != null) - { - return s; - } - index = items[item]; - return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); -} - -/** - * Reads UTF8 string in {@link #b b}. - * - * @param index start offset of the UTF8 string to be read. - * @param utfLen length of the UTF8 string to be read. - * @param buf buffer to be used to read the string. This buffer must be - * sufficiently large. It is not automatically resized. - * @return the String corresponding to the specified UTF8 string. - */ -private String readUTF(int index, final int utfLen, final char[] buf){ - int endIndex = index + utfLen; - byte[] b = this.b; - int strLen = 0; - int c, d, e; - while(index < endIndex) - { - c = b[index++] & 0xFF; - switch(c >> 4) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - // 0xxxxxxx - buf[strLen++] = (char) c; - break; - case 12: - case 13: - // 110x xxxx 10xx xxxx - d = b[index++]; - buf[strLen++] = (char) (((c & 0x1F) << 6) | (d & 0x3F)); - break; - default: - // 1110 xxxx 10xx xxxx 10xx xxxx - d = b[index++]; - e = b[index++]; - buf[strLen++] = (char) (((c & 0x0F) << 12) - | ((d & 0x3F) << 6) | (e & 0x3F)); - break; - } - } - return new String(buf, 0, strLen); -} - -/** - * Reads a class constant pool item in {@link #b b}. This method is - * intended for {@link Attribute} sub classes, and is normally not needed by - * class generators or adapters. - * - * @param index the start index of an unsigned short value in {@link #b b}, - * whose value is the index of a class constant pool item. - * @param buf buffer to be used to read the item. This buffer must be - * sufficiently large. It is not automatically resized. - * @return the String corresponding to the specified class item. - */ -public String readClass(final int index, final char[] buf){ - // computes the start index of the CONSTANT_Class item in b - // and reads the CONSTANT_Utf8 item designated by - // the first two bytes of this CONSTANT_Class item - return readUTF8(items[readUnsignedShort(index)], buf); -} - -/** - * Reads a numeric or string constant pool item in {@link #b b}. This - * method is intended for {@link Attribute} sub classes, and is normally not - * needed by class generators or adapters. - * - * @param item the index of a constant pool item. - * @param buf buffer to be used to read the item. This buffer must be - * sufficiently large. It is not automatically resized. - * @return the {@link Integer}, {@link Float}, {@link Long}, - * {@link Double}, {@link String} or {@link Type} corresponding to - * the given constant pool item. - */ -public Object readConst(final int item, final char[] buf){ - int index = items[item]; - switch(b[index - 1]) - { - case ClassWriter.INT: - return new Integer(readInt(index)); - case ClassWriter.FLOAT: - return new Float(Float.intBitsToFloat(readInt(index))); - case ClassWriter.LONG: - return new Long(readLong(index)); - case ClassWriter.DOUBLE: - return new Double(Double.longBitsToDouble(readLong(index))); - case ClassWriter.CLASS: - String s = readUTF8(index, buf); - return s.charAt(0) == '[' - ? Type.getType(s) - : Type.getObjectType(s); - // case ClassWriter.STR: - default: - return readUTF8(index, buf); - } -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +import java.io.InputStream; +import java.io.IOException; + +/** + * A Java class parser to make a {@link ClassVisitor} visit an existing class. + * This class parses a byte array conforming to the Java class file format and + * calls the appropriate visit methods of a given class visitor for each field, + * method and bytecode instruction encountered. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public class ClassReader{ + +/** + * Flag to skip method code. If this class is set CODE + * attribute won't be visited. This can be used, for example, to retrieve + * annotations for methods and method parameters. + */ +public final static int SKIP_CODE = 1; + +/** + * Flag to skip the debug information in the class. If this flag is set the + * debug information of the class is not visited, i.e. the + * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and + * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be + * called. + */ +public final static int SKIP_DEBUG = 2; + +/** + * Flag to skip the stack map frames in the class. If this flag is set the + * stack map frames of the class is not visited, i.e. the + * {@link MethodVisitor#visitFrame visitFrame} method will not be called. + * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is + * used: it avoids visiting frames that will be ignored and recomputed from + * scratch in the class writer. + */ +public final static int SKIP_FRAMES = 4; + +/** + * Flag to expand the stack map frames. By default stack map frames are + * visited in their original format (i.e. "expanded" for classes whose + * version is less than V1_6, and "compressed" for the other classes). If + * this flag is set, stack map frames are always visited in expanded format + * (this option adds a decompression/recompression step in ClassReader and + * ClassWriter which degrades performances quite a lot). + */ +public final static int EXPAND_FRAMES = 8; + +/** + * The class to be parsed. The content of this array must not be + * modified. This field is intended for {@link Attribute} sub classes, and + * is normally not needed by class generators or adapters. + */ +public final byte[] b; + +/** + * The start index of each constant pool item in {@link #b b}, plus one. + * The one byte offset skips the constant pool item tag that indicates its + * type. + */ +private final int[] items; + +/** + * The String objects corresponding to the CONSTANT_Utf8 items. This cache + * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, + * which GREATLY improves performances (by a factor 2 to 3). This caching + * strategy could be extended to all constant pool items, but its benefit + * would not be so great for these items (because they are much less + * expensive to parse than CONSTANT_Utf8 items). + */ +private final String[] strings; + +/** + * Maximum length of the strings contained in the constant pool of the + * class. + */ +private final int maxStringLength; + +/** + * Start index of the class header information (access, name...) in + * {@link #b b}. + */ +public final int header; + +// ------------------------------------------------------------------------ +// Constructors +// ------------------------------------------------------------------------ + +/** + * Constructs a new {@link ClassReader} object. + * + * @param b the bytecode of the class to be read. + */ +public ClassReader(final byte[] b){ + this(b, 0, b.length); +} + +/** + * Constructs a new {@link ClassReader} object. + * + * @param b the bytecode of the class to be read. + * @param off the start offset of the class data. + * @param len the length of the class data. + */ +public ClassReader(final byte[] b, final int off, final int len){ + this.b = b; + // parses the constant pool + items = new int[readUnsignedShort(off + 8)]; + int n = items.length; + strings = new String[n]; + int max = 0; + int index = off + 10; + for(int i = 1; i < n; ++i) + { + items[i] = index + 1; + int size; + switch(b[index]) + { + case ClassWriter.FIELD: + case ClassWriter.METH: + case ClassWriter.IMETH: + case ClassWriter.INT: + case ClassWriter.FLOAT: + case ClassWriter.NAME_TYPE: + size = 5; + break; + case ClassWriter.LONG: + case ClassWriter.DOUBLE: + size = 9; + ++i; + break; + case ClassWriter.UTF8: + size = 3 + readUnsignedShort(index + 1); + if(size > max) + { + max = size; + } + break; + // case ClassWriter.CLASS: + // case ClassWriter.STR: + default: + size = 3; + break; + } + index += size; + } + maxStringLength = max; + // the class header information starts just after the constant pool + header = index; +} + +/** + * Returns the class's access flags (see {@link Opcodes}). This value may + * not reflect Deprecated and Synthetic flags when bytecode is before 1.5 + * and those flags are represented by attributes. + * + * @return the class access flags + * @see ClassVisitor#visit(int,int,String,String,String,String[]) + */ +public int getAccess(){ + return readUnsignedShort(header); +} + +/** + * Returns the internal name of the class (see + * {@link Type#getInternalName() getInternalName}). + * + * @return the internal class name + * @see ClassVisitor#visit(int,int,String,String,String,String[]) + */ +public String getClassName(){ + return readClass(header + 2, new char[maxStringLength]); +} + +/** + * Returns the internal of name of the super class (see + * {@link Type#getInternalName() getInternalName}). For interfaces, the + * super class is {@link Object}. + * + * @return the internal name of super class, or null for + * {@link Object} class. + * @see ClassVisitor#visit(int,int,String,String,String,String[]) + */ +public String getSuperName(){ + int n = items[readUnsignedShort(header + 4)]; + return n == 0 ? null : readUTF8(n, new char[maxStringLength]); +} + +/** + * Returns the internal names of the class's interfaces (see + * {@link Type#getInternalName() getInternalName}). + * + * @return the array of internal names for all implemented interfaces or + * null. + * @see ClassVisitor#visit(int,int,String,String,String,String[]) + */ +public String[] getInterfaces(){ + int index = header + 6; + int n = readUnsignedShort(index); + String[] interfaces = new String[n]; + if(n > 0) + { + char[] buf = new char[maxStringLength]; + for(int i = 0; i < n; ++i) + { + index += 2; + interfaces[i] = readClass(index, buf); + } + } + return interfaces; +} + +/** + * Copies the constant pool data into the given {@link ClassWriter}. Should + * be called before the {@link #accept(ClassVisitor,int)} method. + * + * @param classWriter the {@link ClassWriter} to copy constant pool into. + */ +void copyPool(final ClassWriter classWriter){ + char[] buf = new char[maxStringLength]; + int ll = items.length; + Item[] items2 = new Item[ll]; + for(int i = 1; i < ll; i++) + { + int index = items[i]; + int tag = b[index - 1]; + Item item = new Item(i); + int nameType; + switch(tag) + { + case ClassWriter.FIELD: + case ClassWriter.METH: + case ClassWriter.IMETH: + nameType = items[readUnsignedShort(index + 2)]; + item.set(tag, + readClass(index, buf), + readUTF8(nameType, buf), + readUTF8(nameType + 2, buf)); + break; + + case ClassWriter.INT: + item.set(readInt(index)); + break; + + case ClassWriter.FLOAT: + item.set(Float.intBitsToFloat(readInt(index))); + break; + + case ClassWriter.NAME_TYPE: + item.set(tag, + readUTF8(index, buf), + readUTF8(index + 2, buf), + null); + break; + + case ClassWriter.LONG: + item.set(readLong(index)); + ++i; + break; + + case ClassWriter.DOUBLE: + item.set(Double.longBitsToDouble(readLong(index))); + ++i; + break; + + case ClassWriter.UTF8: + { + String s = strings[i]; + if(s == null) + { + index = items[i]; + s = strings[i] = readUTF(index + 2, + readUnsignedShort(index), + buf); + } + item.set(tag, s, null, null); + } + break; + + // case ClassWriter.STR: + // case ClassWriter.CLASS: + default: + item.set(tag, readUTF8(index, buf), null, null); + break; + } + + int index2 = item.hashCode % items2.length; + item.next = items2[index2]; + items2[index2] = item; + } + + int off = items[1] - 1; + classWriter.pool.putByteArray(b, off, header - off); + classWriter.items = items2; + classWriter.threshold = (int) (0.75d * ll); + classWriter.index = ll; +} + +/** + * Constructs a new {@link ClassReader} object. + * + * @param is an input stream from which to read the class. + * @throws IOException if a problem occurs during reading. + */ +public ClassReader(final InputStream is) throws IOException{ + this(readClass(is)); +} + +/** + * Constructs a new {@link ClassReader} object. + * + * @param name the fully qualified name of the class to be read. + * @throws IOException if an exception occurs during reading. + */ +public ClassReader(final String name) throws IOException{ + this(ClassLoader.getSystemResourceAsStream(name.replace('.', '/') + + ".class")); +} + +/** + * Reads the bytecode of a class. + * + * @param is an input stream from which to read the class. + * @return the bytecode read from the given input stream. + * @throws IOException if a problem occurs during reading. + */ +private static byte[] readClass(final InputStream is) throws IOException{ + if(is == null) + { + throw new IOException("Class not found"); + } + byte[] b = new byte[is.available()]; + int len = 0; + while(true) + { + int n = is.read(b, len, b.length - len); + if(n == -1) + { + if(len < b.length) + { + byte[] c = new byte[len]; + System.arraycopy(b, 0, c, 0, len); + b = c; + } + return b; + } + len += n; + if(len == b.length) + { + byte[] c = new byte[b.length + 1000]; + System.arraycopy(b, 0, c, 0, len); + b = c; + } + } +} + +// ------------------------------------------------------------------------ +// Public methods +// ------------------------------------------------------------------------ + +/** + * Makes the given visitor visit the Java class of this {@link ClassReader}. + * This class is the one specified in the constructor (see + * {@link #ClassReader(byte[]) ClassReader}). + * + * @param classVisitor the visitor that must visit this class. + * @param flags option flags that can be used to modify the default behavior + * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}. + */ +public void accept(final ClassVisitor classVisitor, final int flags){ + accept(classVisitor, new Attribute[0], flags); +} + +/** + * Makes the given visitor visit the Java class of this {@link ClassReader}. + * This class is the one specified in the constructor (see + * {@link #ClassReader(byte[]) ClassReader}). + * + * @param classVisitor the visitor that must visit this class. + * @param attrs prototypes of the attributes that must be parsed during the + * visit of the class. Any attribute whose type is not equal to the + * type of one the prototypes will not be parsed: its byte array + * value will be passed unchanged to the ClassWriter. This may + * corrupt it if this value contains references to the constant pool, + * or has syntactic or semantic links with a class element that has + * been transformed by a class adapter between the reader and the + * writer. + * @param flags option flags that can be used to modify the default behavior + * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}. + */ +public void accept( + final ClassVisitor classVisitor, + final Attribute[] attrs, + final int flags){ + byte[] b = this.b; // the bytecode array + char[] c = new char[maxStringLength]; // buffer used to read strings + int i, j, k; // loop variables + int u, v, w; // indexes in b + Attribute attr; + + int access; + String name; + String desc; + String attrName; + String signature; + int anns = 0; + int ianns = 0; + Attribute cattrs = null; + + // visits the header + u = header; + access = readUnsignedShort(u); + name = readClass(u + 2, c); + v = items[readUnsignedShort(u + 4)]; + String superClassName = v == 0 ? null : readUTF8(v, c); + String[] implementedItfs = new String[readUnsignedShort(u + 6)]; + w = 0; + u += 8; + for(i = 0; i < implementedItfs.length; ++i) + { + implementedItfs[i] = readClass(u, c); + u += 2; + } + + boolean skipCode = (flags & SKIP_CODE) != 0; + boolean skipDebug = (flags & SKIP_DEBUG) != 0; + boolean unzip = (flags & EXPAND_FRAMES) != 0; + + // skips fields and methods + v = u; + i = readUnsignedShort(v); + v += 2; + for(; i > 0; --i) + { + j = readUnsignedShort(v + 6); + v += 8; + for(; j > 0; --j) + { + v += 6 + readInt(v + 2); + } + } + i = readUnsignedShort(v); + v += 2; + for(; i > 0; --i) + { + j = readUnsignedShort(v + 6); + v += 8; + for(; j > 0; --j) + { + v += 6 + readInt(v + 2); + } + } + // reads the class's attributes + signature = null; + String sourceFile = null; + String sourceDebug = null; + String enclosingOwner = null; + String enclosingName = null; + String enclosingDesc = null; + + i = readUnsignedShort(v); + v += 2; + for(; i > 0; --i) + { + attrName = readUTF8(v, c); + // tests are sorted in decreasing frequency order + // (based on frequencies observed on typical classes) + if(attrName.equals("SourceFile")) + { + sourceFile = readUTF8(v + 6, c); + } + else if(attrName.equals("InnerClasses")) + { + w = v + 6; + } + else if(attrName.equals("EnclosingMethod")) + { + enclosingOwner = readClass(v + 6, c); + int item = readUnsignedShort(v + 8); + if(item != 0) + { + enclosingName = readUTF8(items[item], c); + enclosingDesc = readUTF8(items[item] + 2, c); + } + } + else if(attrName.equals("Signature")) + { + signature = readUTF8(v + 6, c); + } + else if(attrName.equals("RuntimeVisibleAnnotations")) + { + anns = v + 6; + } + else if(attrName.equals("Deprecated")) + { + access |= Opcodes.ACC_DEPRECATED; + } + else if(attrName.equals("Synthetic")) + { + access |= Opcodes.ACC_SYNTHETIC; + } + else if(attrName.equals("SourceDebugExtension")) + { + int len = readInt(v + 2); + sourceDebug = readUTF(v + 6, len, new char[len]); + } + else if(attrName.equals("RuntimeInvisibleAnnotations")) + { + ianns = v + 6; + } + else + { + attr = readAttribute(attrs, + attrName, + v + 6, + readInt(v + 2), + c, + -1, + null); + if(attr != null) + { + attr.next = cattrs; + cattrs = attr; + } + } + v += 6 + readInt(v + 2); + } + // calls the visit method + classVisitor.visit(readInt(4), + access, + name, + signature, + superClassName, + implementedItfs); + + // calls the visitSource method + if(!skipDebug && (sourceFile != null || sourceDebug != null)) + { + classVisitor.visitSource(sourceFile, sourceDebug); + } + + // calls the visitOuterClass method + if(enclosingOwner != null) + { + classVisitor.visitOuterClass(enclosingOwner, + enclosingName, + enclosingDesc); + } + + // visits the class annotations + for(i = 1; i >= 0; --i) + { + v = i == 0 ? ianns : anns; + if(v != 0) + { + j = readUnsignedShort(v); + v += 2; + for(; j > 0; --j) + { + v = readAnnotationValues(v + 2, + c, + true, + classVisitor.visitAnnotation(readUTF8(v, c), i != 0)); + } + } + } + + // visits the class attributes + while(cattrs != null) + { + attr = cattrs.next; + cattrs.next = null; + classVisitor.visitAttribute(cattrs); + cattrs = attr; + } + + // calls the visitInnerClass method + if(w != 0) + { + i = readUnsignedShort(w); + w += 2; + for(; i > 0; --i) + { + classVisitor.visitInnerClass(readUnsignedShort(w) == 0 + ? null + : readClass(w, c), readUnsignedShort(w + 2) == 0 + ? null + : readClass(w + 2, c), readUnsignedShort(w + 4) == 0 + ? null + : readUTF8(w + 4, c), + readUnsignedShort(w + 6)); + w += 8; + } + } + + // visits the fields + i = readUnsignedShort(u); + u += 2; + for(; i > 0; --i) + { + access = readUnsignedShort(u); + name = readUTF8(u + 2, c); + desc = readUTF8(u + 4, c); + // visits the field's attributes and looks for a ConstantValue + // attribute + int fieldValueItem = 0; + signature = null; + anns = 0; + ianns = 0; + cattrs = null; + + j = readUnsignedShort(u + 6); + u += 8; + for(; j > 0; --j) + { + attrName = readUTF8(u, c); + // tests are sorted in decreasing frequency order + // (based on frequencies observed on typical classes) + if(attrName.equals("ConstantValue")) + { + fieldValueItem = readUnsignedShort(u + 6); + } + else if(attrName.equals("Signature")) + { + signature = readUTF8(u + 6, c); + } + else if(attrName.equals("Deprecated")) + { + access |= Opcodes.ACC_DEPRECATED; + } + else if(attrName.equals("Synthetic")) + { + access |= Opcodes.ACC_SYNTHETIC; + } + else if(attrName.equals("RuntimeVisibleAnnotations")) + { + anns = u + 6; + } + else if(attrName.equals("RuntimeInvisibleAnnotations")) + { + ianns = u + 6; + } + else + { + attr = readAttribute(attrs, + attrName, + u + 6, + readInt(u + 2), + c, + -1, + null); + if(attr != null) + { + attr.next = cattrs; + cattrs = attr; + } + } + u += 6 + readInt(u + 2); + } + // visits the field + FieldVisitor fv = classVisitor.visitField(access, + name, + desc, + signature, + fieldValueItem == 0 ? null : readConst(fieldValueItem, c)); + // visits the field annotations and attributes + if(fv != null) + { + for(j = 1; j >= 0; --j) + { + v = j == 0 ? ianns : anns; + if(v != 0) + { + k = readUnsignedShort(v); + v += 2; + for(; k > 0; --k) + { + v = readAnnotationValues(v + 2, + c, + true, + fv.visitAnnotation(readUTF8(v, c), j != 0)); + } + } + } + while(cattrs != null) + { + attr = cattrs.next; + cattrs.next = null; + fv.visitAttribute(cattrs); + cattrs = attr; + } + fv.visitEnd(); + } + } + + // visits the methods + i = readUnsignedShort(u); + u += 2; + for(; i > 0; --i) + { + int u0 = u + 6; + access = readUnsignedShort(u); + name = readUTF8(u + 2, c); + desc = readUTF8(u + 4, c); + signature = null; + anns = 0; + ianns = 0; + int dann = 0; + int mpanns = 0; + int impanns = 0; + cattrs = null; + v = 0; + w = 0; + + // looks for Code and Exceptions attributes + j = readUnsignedShort(u + 6); + u += 8; + for(; j > 0; --j) + { + attrName = readUTF8(u, c); + int attrSize = readInt(u + 2); + u += 6; + // tests are sorted in decreasing frequency order + // (based on frequencies observed on typical classes) + if(attrName.equals("Code")) + { + if(!skipCode) + { + v = u; + } + } + else if(attrName.equals("Exceptions")) + { + w = u; + } + else if(attrName.equals("Signature")) + { + signature = readUTF8(u, c); + } + else if(attrName.equals("Deprecated")) + { + access |= Opcodes.ACC_DEPRECATED; + } + else if(attrName.equals("RuntimeVisibleAnnotations")) + { + anns = u; + } + else if(attrName.equals("AnnotationDefault")) + { + dann = u; + } + else if(attrName.equals("Synthetic")) + { + access |= Opcodes.ACC_SYNTHETIC; + } + else if(attrName.equals("RuntimeInvisibleAnnotations")) + { + ianns = u; + } + else if(attrName.equals("RuntimeVisibleParameterAnnotations")) + { + mpanns = u; + } + else if(attrName.equals("RuntimeInvisibleParameterAnnotations")) + { + impanns = u; + } + else + { + attr = readAttribute(attrs, + attrName, + u, + attrSize, + c, + -1, + null); + if(attr != null) + { + attr.next = cattrs; + cattrs = attr; + } + } + u += attrSize; + } + // reads declared exceptions + String[] exceptions; + if(w == 0) + { + exceptions = null; + } + else + { + exceptions = new String[readUnsignedShort(w)]; + w += 2; + for(j = 0; j < exceptions.length; ++j) + { + exceptions[j] = readClass(w, c); + w += 2; + } + } + + // visits the method's code, if any + MethodVisitor mv = classVisitor.visitMethod(access, + name, + desc, + signature, + exceptions); + + if(mv != null) + { + /* + * if the returned MethodVisitor is in fact a MethodWriter, it + * means there is no method adapter between the reader and the + * writer. If, in addition, the writer's constant pool was + * copied from this reader (mw.cw.cr == this), and the signature + * and exceptions of the method have not been changed, then it + * is possible to skip all visit events and just copy the + * original code of the method to the writer (the access, name + * and descriptor can have been changed, this is not important + * since they are not copied as is from the reader). + */ + if(mv instanceof MethodWriter) + { + MethodWriter mw = (MethodWriter) mv; + if(mw.cw.cr == this) + { + if(signature == mw.signature) + { + boolean sameExceptions = false; + if(exceptions == null) + { + sameExceptions = mw.exceptionCount == 0; + } + else + { + if(exceptions.length == mw.exceptionCount) + { + sameExceptions = true; + for(j = exceptions.length - 1; j >= 0; --j) + { + w -= 2; + if(mw.exceptions[j] != readUnsignedShort(w)) + { + sameExceptions = false; + break; + } + } + } + } + if(sameExceptions) + { + /* + * we do not copy directly the code into + * MethodWriter to save a byte array copy + * operation. The real copy will be done in + * ClassWriter.toByteArray(). + */ + mw.classReaderOffset = u0; + mw.classReaderLength = u - u0; + continue; + } + } + } + } + + if(dann != 0) + { + AnnotationVisitor dv = mv.visitAnnotationDefault(); + readAnnotationValue(dann, c, null, dv); + if(dv != null) + { + dv.visitEnd(); + } + } + for(j = 1; j >= 0; --j) + { + w = j == 0 ? ianns : anns; + if(w != 0) + { + k = readUnsignedShort(w); + w += 2; + for(; k > 0; --k) + { + w = readAnnotationValues(w + 2, + c, + true, + mv.visitAnnotation(readUTF8(w, c), j != 0)); + } + } + } + if(mpanns != 0) + { + readParameterAnnotations(mpanns, c, true, mv); + } + if(impanns != 0) + { + readParameterAnnotations(impanns, c, false, mv); + } + while(cattrs != null) + { + attr = cattrs.next; + cattrs.next = null; + mv.visitAttribute(cattrs); + cattrs = attr; + } + } + + if(mv != null && v != 0) + { + int maxStack = readUnsignedShort(v); + int maxLocals = readUnsignedShort(v + 2); + int codeLength = readInt(v + 4); + v += 8; + + int codeStart = v; + int codeEnd = v + codeLength; + + mv.visitCode(); + + // 1st phase: finds the labels + int label; + Label[] labels = new Label[codeLength + 1]; + while(v < codeEnd) + { + int opcode = b[v] & 0xFF; + switch(ClassWriter.TYPE[opcode]) + { + case ClassWriter.NOARG_INSN: + case ClassWriter.IMPLVAR_INSN: + v += 1; + break; + case ClassWriter.LABEL_INSN: + label = v - codeStart + readShort(v + 1); + if(labels[label] == null) + { + labels[label] = new Label(); + } + v += 3; + break; + case ClassWriter.LABELW_INSN: + label = v - codeStart + readInt(v + 1); + if(labels[label] == null) + { + labels[label] = new Label(); + } + v += 5; + break; + case ClassWriter.WIDE_INSN: + opcode = b[v + 1] & 0xFF; + if(opcode == Opcodes.IINC) + { + v += 6; + } + else + { + v += 4; + } + break; + case ClassWriter.TABL_INSN: + // skips 0 to 3 padding bytes + w = v - codeStart; + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); + if(labels[label] == null) + { + labels[label] = new Label(); + } + j = readInt(v + 8) - readInt(v + 4) + 1; + v += 12; + for(; j > 0; --j) + { + label = w + readInt(v); + v += 4; + if(labels[label] == null) + { + labels[label] = new Label(); + } + } + break; + case ClassWriter.LOOK_INSN: + // skips 0 to 3 padding bytes + w = v - codeStart; + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); + if(labels[label] == null) + { + labels[label] = new Label(); + } + j = readInt(v + 4); + v += 8; + for(; j > 0; --j) + { + label = w + readInt(v + 4); + v += 8; + if(labels[label] == null) + { + labels[label] = new Label(); + } + } + break; + case ClassWriter.VAR_INSN: + case ClassWriter.SBYTE_INSN: + case ClassWriter.LDC_INSN: + v += 2; + break; + case ClassWriter.SHORT_INSN: + case ClassWriter.LDCW_INSN: + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.TYPE_INSN: + case ClassWriter.IINC_INSN: + v += 3; + break; + case ClassWriter.ITFMETH_INSN: + v += 5; + break; + // case MANA_INSN: + default: + v += 4; + break; + } + } + // parses the try catch entries + j = readUnsignedShort(v); + v += 2; + for(; j > 0; --j) + { + label = readUnsignedShort(v); + Label start = labels[label]; + if(start == null) + { + labels[label] = start = new Label(); + } + label = readUnsignedShort(v + 2); + Label end = labels[label]; + if(end == null) + { + labels[label] = end = new Label(); + } + label = readUnsignedShort(v + 4); + Label handler = labels[label]; + if(handler == null) + { + labels[label] = handler = new Label(); + } + int type = readUnsignedShort(v + 6); + if(type == 0) + { + mv.visitTryCatchBlock(start, end, handler, null); + } + else + { + mv.visitTryCatchBlock(start, + end, + handler, + readUTF8(items[type], c)); + } + v += 8; + } + // parses the local variable, line number tables, and code + // attributes + int varTable = 0; + int varTypeTable = 0; + int stackMap = 0; + int frameCount = 0; + int frameMode = 0; + int frameOffset = 0; + int frameLocalCount = 0; + int frameLocalDiff = 0; + int frameStackCount = 0; + Object[] frameLocal = null; + Object[] frameStack = null; + boolean zip = true; + cattrs = null; + j = readUnsignedShort(v); + v += 2; + for(; j > 0; --j) + { + attrName = readUTF8(v, c); + if(attrName.equals("LocalVariableTable")) + { + if(!skipDebug) + { + varTable = v + 6; + k = readUnsignedShort(v + 6); + w = v + 8; + for(; k > 0; --k) + { + label = readUnsignedShort(w); + if(labels[label] == null) + { + labels[label] = new Label(true); + } + label += readUnsignedShort(w + 2); + if(labels[label] == null) + { + labels[label] = new Label(true); + } + w += 10; + } + } + } + else if(attrName.equals("LocalVariableTypeTable")) + { + varTypeTable = v + 6; + } + else if(attrName.equals("LineNumberTable")) + { + if(!skipDebug) + { + k = readUnsignedShort(v + 6); + w = v + 8; + for(; k > 0; --k) + { + label = readUnsignedShort(w); + if(labels[label] == null) + { + labels[label] = new Label(true); + } + labels[label].line = readUnsignedShort(w + 2); + w += 4; + } + } + } + else if(attrName.equals("StackMapTable")) + { + if((flags & SKIP_FRAMES) == 0) + { + stackMap = v + 8; + frameCount = readUnsignedShort(v + 6); + } + /* + * here we do not extract the labels corresponding to + * the attribute content. This would require a full + * parsing of the attribute, which would need to be + * repeated in the second phase (see below). Instead the + * content of the attribute is read one frame at a time + * (i.e. after a frame has been visited, the next frame + * is read), and the labels it contains are also + * extracted one frame at a time. Thanks to the ordering + * of frames, having only a "one frame lookahead" is not + * a problem, i.e. it is not possible to see an offset + * smaller than the offset of the current insn and for + * which no Label exist. + */ + // TODO true for frame offsets, + // but for UNINITIALIZED type offsets? + } + else if(attrName.equals("StackMap")) + { + if((flags & SKIP_FRAMES) == 0) + { + stackMap = v + 8; + frameCount = readUnsignedShort(v + 6); + zip = false; + } + /* + * IMPORTANT! here we assume that the frames are + * ordered, as in the StackMapTable attribute, although + * this is not guaranteed by the attribute format. + */ + } + else + { + for(k = 0; k < attrs.length; ++k) + { + if(attrs[k].type.equals(attrName)) + { + attr = attrs[k].read(this, + v + 6, + readInt(v + 2), + c, + codeStart - 8, + labels); + if(attr != null) + { + attr.next = cattrs; + cattrs = attr; + } + } + } + } + v += 6 + readInt(v + 2); + } + + // 2nd phase: visits each instruction + if(stackMap != 0) + { + // creates the very first (implicit) frame from the method + // descriptor + frameLocal = new Object[maxLocals]; + frameStack = new Object[maxStack]; + if(unzip) + { + int local = 0; + if((access & Opcodes.ACC_STATIC) == 0) + { + if(name.equals("")) + { + frameLocal[local++] = Opcodes.UNINITIALIZED_THIS; + } + else + { + frameLocal[local++] = readClass(header + 2, c); + } + } + j = 1; + loop: + while(true) + { + k = j; + switch(desc.charAt(j++)) + { + case'Z': + case'C': + case'B': + case'S': + case'I': + frameLocal[local++] = Opcodes.INTEGER; + break; + case'F': + frameLocal[local++] = Opcodes.FLOAT; + break; + case'J': + frameLocal[local++] = Opcodes.LONG; + break; + case'D': + frameLocal[local++] = Opcodes.DOUBLE; + break; + case'[': + while(desc.charAt(j) == '[') + { + ++j; + } + if(desc.charAt(j) == 'L') + { + ++j; + while(desc.charAt(j) != ';') + { + ++j; + } + } + frameLocal[local++] = desc.substring(k, ++j); + break; + case'L': + while(desc.charAt(j) != ';') + { + ++j; + } + frameLocal[local++] = desc.substring(k + 1, + j++); + break; + default: + break loop; + } + } + frameLocalCount = local; + } + /* + * for the first explicit frame the offset is not + * offset_delta + 1 but only offset_delta; setting the + * implicit frame offset to -1 allow the use of the + * "offset_delta + 1" rule in all cases + */ + frameOffset = -1; + } + v = codeStart; + Label l; + while(v < codeEnd) + { + w = v - codeStart; + + l = labels[w]; + if(l != null) + { + mv.visitLabel(l); + if(!skipDebug && l.line > 0) + { + mv.visitLineNumber(l.line, l); + } + } + + while(frameLocal != null + && (frameOffset == w || frameOffset == -1)) + { + // if there is a frame for this offset, + // makes the visitor visit it, + // and reads the next frame if there is one. + if(!zip || unzip) + { + mv.visitFrame(Opcodes.F_NEW, + frameLocalCount, + frameLocal, + frameStackCount, + frameStack); + } + else if(frameOffset != -1) + { + mv.visitFrame(frameMode, + frameLocalDiff, + frameLocal, + frameStackCount, + frameStack); + } + + if(frameCount > 0) + { + int tag, delta, n; + if(zip) + { + tag = b[stackMap++] & 0xFF; + } + else + { + tag = MethodWriter.FULL_FRAME; + frameOffset = -1; + } + frameLocalDiff = 0; + if(tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) + { + delta = tag; + frameMode = Opcodes.F_SAME; + frameStackCount = 0; + } + else if(tag < MethodWriter.RESERVED) + { + delta = tag + - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME; + stackMap = readFrameType(frameStack, + 0, + stackMap, + c, + labels); + frameMode = Opcodes.F_SAME1; + frameStackCount = 1; + } + else + { + delta = readUnsignedShort(stackMap); + stackMap += 2; + if(tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) + { + stackMap = readFrameType(frameStack, + 0, + stackMap, + c, + labels); + frameMode = Opcodes.F_SAME1; + frameStackCount = 1; + } + else if(tag >= MethodWriter.CHOP_FRAME + && tag < MethodWriter.SAME_FRAME_EXTENDED) + { + frameMode = Opcodes.F_CHOP; + frameLocalDiff = MethodWriter.SAME_FRAME_EXTENDED + - tag; + frameLocalCount -= frameLocalDiff; + frameStackCount = 0; + } + else if(tag == MethodWriter.SAME_FRAME_EXTENDED) + { + frameMode = Opcodes.F_SAME; + frameStackCount = 0; + } + else if(tag < MethodWriter.FULL_FRAME) + { + j = unzip ? frameLocalCount : 0; + for(k = tag + - MethodWriter.SAME_FRAME_EXTENDED; k > 0; k--) + { + stackMap = readFrameType(frameLocal, + j++, + stackMap, + c, + labels); + } + frameMode = Opcodes.F_APPEND; + frameLocalDiff = tag + - MethodWriter.SAME_FRAME_EXTENDED; + frameLocalCount += frameLocalDiff; + frameStackCount = 0; + } + else + { // if (tag == FULL_FRAME) { + frameMode = Opcodes.F_FULL; + n = frameLocalDiff = frameLocalCount = readUnsignedShort(stackMap); + stackMap += 2; + for(j = 0; n > 0; n--) + { + stackMap = readFrameType(frameLocal, + j++, + stackMap, + c, + labels); + } + n = frameStackCount = readUnsignedShort(stackMap); + stackMap += 2; + for(j = 0; n > 0; n--) + { + stackMap = readFrameType(frameStack, + j++, + stackMap, + c, + labels); + } + } + } + frameOffset += delta + 1; + if(labels[frameOffset] == null) + { + labels[frameOffset] = new Label(); + } + + --frameCount; + } + else + { + frameLocal = null; + } + } + + int opcode = b[v] & 0xFF; + switch(ClassWriter.TYPE[opcode]) + { + case ClassWriter.NOARG_INSN: + mv.visitInsn(opcode); + v += 1; + break; + case ClassWriter.IMPLVAR_INSN: + if(opcode > Opcodes.ISTORE) + { + opcode -= 59; // ISTORE_0 + mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), + opcode & 0x3); + } + else + { + opcode -= 26; // ILOAD_0 + mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), + opcode & 0x3); + } + v += 1; + break; + case ClassWriter.LABEL_INSN: + mv.visitJumpInsn(opcode, labels[w + + readShort(v + 1)]); + v += 3; + break; + case ClassWriter.LABELW_INSN: + mv.visitJumpInsn(opcode - 33, labels[w + + readInt(v + 1)]); + v += 5; + break; + case ClassWriter.WIDE_INSN: + opcode = b[v + 1] & 0xFF; + if(opcode == Opcodes.IINC) + { + mv.visitIincInsn(readUnsignedShort(v + 2), + readShort(v + 4)); + v += 6; + } + else + { + mv.visitVarInsn(opcode, + readUnsignedShort(v + 2)); + v += 4; + } + break; + case ClassWriter.TABL_INSN: + // skips 0 to 3 padding bytes + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); + int min = readInt(v + 4); + int max = readInt(v + 8); + v += 12; + Label[] table = new Label[max - min + 1]; + for(j = 0; j < table.length; ++j) + { + table[j] = labels[w + readInt(v)]; + v += 4; + } + mv.visitTableSwitchInsn(min, + max, + labels[label], + table); + break; + case ClassWriter.LOOK_INSN: + // skips 0 to 3 padding bytes + v = v + 4 - (w & 3); + // reads instruction + label = w + readInt(v); + j = readInt(v + 4); + v += 8; + int[] keys = new int[j]; + Label[] values = new Label[j]; + for(j = 0; j < keys.length; ++j) + { + keys[j] = readInt(v); + values[j] = labels[w + readInt(v + 4)]; + v += 8; + } + mv.visitLookupSwitchInsn(labels[label], + keys, + values); + break; + case ClassWriter.VAR_INSN: + mv.visitVarInsn(opcode, b[v + 1] & 0xFF); + v += 2; + break; + case ClassWriter.SBYTE_INSN: + mv.visitIntInsn(opcode, b[v + 1]); + v += 2; + break; + case ClassWriter.SHORT_INSN: + mv.visitIntInsn(opcode, readShort(v + 1)); + v += 3; + break; + case ClassWriter.LDC_INSN: + mv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c)); + v += 2; + break; + case ClassWriter.LDCW_INSN: + mv.visitLdcInsn(readConst(readUnsignedShort(v + 1), + c)); + v += 3; + break; + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.ITFMETH_INSN: + int cpIndex = items[readUnsignedShort(v + 1)]; + String iowner = readClass(cpIndex, c); + cpIndex = items[readUnsignedShort(cpIndex + 2)]; + String iname = readUTF8(cpIndex, c); + String idesc = readUTF8(cpIndex + 2, c); + if(opcode < Opcodes.INVOKEVIRTUAL) + { + mv.visitFieldInsn(opcode, iowner, iname, idesc); + } + else + { + mv.visitMethodInsn(opcode, iowner, iname, idesc); + } + if(opcode == Opcodes.INVOKEINTERFACE) + { + v += 5; + } + else + { + v += 3; + } + break; + case ClassWriter.TYPE_INSN: + mv.visitTypeInsn(opcode, readClass(v + 1, c)); + v += 3; + break; + case ClassWriter.IINC_INSN: + mv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]); + v += 3; + break; + // case MANA_INSN: + default: + mv.visitMultiANewArrayInsn(readClass(v + 1, c), + b[v + 3] & 0xFF); + v += 4; + break; + } + } + l = labels[codeEnd - codeStart]; + if(l != null) + { + mv.visitLabel(l); + } + // visits the local variable tables + if(!skipDebug && varTable != 0) + { + int[] typeTable = null; + if(varTypeTable != 0) + { + k = readUnsignedShort(varTypeTable) * 3; + w = varTypeTable + 2; + typeTable = new int[k]; + while(k > 0) + { + typeTable[--k] = w + 6; // signature + typeTable[--k] = readUnsignedShort(w + 8); // index + typeTable[--k] = readUnsignedShort(w); // start + w += 10; + } + } + k = readUnsignedShort(varTable); + w = varTable + 2; + for(; k > 0; --k) + { + int start = readUnsignedShort(w); + int length = readUnsignedShort(w + 2); + int index = readUnsignedShort(w + 8); + String vsignature = null; + if(typeTable != null) + { + for(int a = 0; a < typeTable.length; a += 3) + { + if(typeTable[a] == start + && typeTable[a + 1] == index) + { + vsignature = readUTF8(typeTable[a + 2], c); + break; + } + } + } + mv.visitLocalVariable(readUTF8(w + 4, c), + readUTF8(w + 6, c), + vsignature, + labels[start], + labels[start + length], + index); + w += 10; + } + } + // visits the other attributes + while(cattrs != null) + { + attr = cattrs.next; + cattrs.next = null; + mv.visitAttribute(cattrs); + cattrs = attr; + } + // visits the max stack and max locals values + mv.visitMaxs(maxStack, maxLocals); + } + + if(mv != null) + { + mv.visitEnd(); + } + } + + // visits the end of the class + classVisitor.visitEnd(); +} + +/** + * Reads parameter annotations and makes the given visitor visit them. + * + * @param v start offset in {@link #b b} of the annotations to be read. + * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or + * {@link #readConst readConst}. + * @param visible true if the annotations to be read are visible + * at runtime. + * @param mv the visitor that must visit the annotations. + */ +private void readParameterAnnotations( + int v, + final char[] buf, + final boolean visible, + final MethodVisitor mv){ + int n = b[v++] & 0xFF; + for(int i = 0; i < n; ++i) + { + int j = readUnsignedShort(v); + v += 2; + for(; j > 0; --j) + { + v = readAnnotationValues(v + 2, + buf, + true, + mv.visitParameterAnnotation(i, + readUTF8(v, buf), + visible)); + } + } +} + +/** + * Reads the values of an annotation and makes the given visitor visit them. + * + * @param v the start offset in {@link #b b} of the values to be read + * (including the unsigned short that gives the number of values). + * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or + * {@link #readConst readConst}. + * @param named if the annotation values are named or not. + * @param av the visitor that must visit the values. + * @return the end offset of the annotation values. + */ +private int readAnnotationValues( + int v, + final char[] buf, + final boolean named, + final AnnotationVisitor av){ + int i = readUnsignedShort(v); + v += 2; + if(named) + { + for(; i > 0; --i) + { + v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av); + } + } + else + { + for(; i > 0; --i) + { + v = readAnnotationValue(v, buf, null, av); + } + } + if(av != null) + { + av.visitEnd(); + } + return v; +} + +/** + * Reads a value of an annotation and makes the given visitor visit it. + * + * @param v the start offset in {@link #b b} of the value to be read (not + * including the value name constant pool index). + * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or + * {@link #readConst readConst}. + * @param name the name of the value to be read. + * @param av the visitor that must visit the value. + * @return the end offset of the annotation value. + */ +private int readAnnotationValue( + int v, + final char[] buf, + final String name, + final AnnotationVisitor av){ + int i; + if(av == null) + { + switch(b[v] & 0xFF) + { + case'e': // enum_const_value + return v + 5; + case'@': // annotation_value + return readAnnotationValues(v + 3, buf, true, null); + case'[': // array_value + return readAnnotationValues(v + 1, buf, false, null); + default: + return v + 3; + } + } + switch(b[v++] & 0xFF) + { + case'I': // pointer to CONSTANT_Integer + case'J': // pointer to CONSTANT_Long + case'F': // pointer to CONSTANT_Float + case'D': // pointer to CONSTANT_Double + av.visit(name, readConst(readUnsignedShort(v), buf)); + v += 2; + break; + case'B': // pointer to CONSTANT_Byte + av.visit(name, + new Byte((byte) readInt(items[readUnsignedShort(v)]))); + v += 2; + break; + case'Z': // pointer to CONSTANT_Boolean + av.visit(name, readInt(items[readUnsignedShort(v)]) == 0 + ? Boolean.FALSE + : Boolean.TRUE); + v += 2; + break; + case'S': // pointer to CONSTANT_Short + av.visit(name, + new Short((short) readInt(items[readUnsignedShort(v)]))); + v += 2; + break; + case'C': // pointer to CONSTANT_Char + av.visit(name, + new Character((char) readInt(items[readUnsignedShort(v)]))); + v += 2; + break; + case's': // pointer to CONSTANT_Utf8 + av.visit(name, readUTF8(v, buf)); + v += 2; + break; + case'e': // enum_const_value + av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf)); + v += 4; + break; + case'c': // class_info + av.visit(name, Type.getType(readUTF8(v, buf))); + v += 2; + break; + case'@': // annotation_value + v = readAnnotationValues(v + 2, + buf, + true, + av.visitAnnotation(name, readUTF8(v, buf))); + break; + case'[': // array_value + int size = readUnsignedShort(v); + v += 2; + if(size == 0) + { + return readAnnotationValues(v - 2, + buf, + false, + av.visitArray(name)); + } + switch(this.b[v++] & 0xFF) + { + case'B': + byte[] bv = new byte[size]; + for(i = 0; i < size; i++) + { + bv[i] = (byte) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, bv); + --v; + break; + case'Z': + boolean[] zv = new boolean[size]; + for(i = 0; i < size; i++) + { + zv[i] = readInt(items[readUnsignedShort(v)]) != 0; + v += 3; + } + av.visit(name, zv); + --v; + break; + case'S': + short[] sv = new short[size]; + for(i = 0; i < size; i++) + { + sv[i] = (short) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, sv); + --v; + break; + case'C': + char[] cv = new char[size]; + for(i = 0; i < size; i++) + { + cv[i] = (char) readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, cv); + --v; + break; + case'I': + int[] iv = new int[size]; + for(i = 0; i < size; i++) + { + iv[i] = readInt(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, iv); + --v; + break; + case'J': + long[] lv = new long[size]; + for(i = 0; i < size; i++) + { + lv[i] = readLong(items[readUnsignedShort(v)]); + v += 3; + } + av.visit(name, lv); + --v; + break; + case'F': + float[] fv = new float[size]; + for(i = 0; i < size; i++) + { + fv[i] = Float.intBitsToFloat(readInt(items[readUnsignedShort(v)])); + v += 3; + } + av.visit(name, fv); + --v; + break; + case'D': + double[] dv = new double[size]; + for(i = 0; i < size; i++) + { + dv[i] = Double.longBitsToDouble(readLong(items[readUnsignedShort(v)])); + v += 3; + } + av.visit(name, dv); + --v; + break; + default: + v = readAnnotationValues(v - 3, + buf, + false, + av.visitArray(name)); + } + } + return v; +} + +private int readFrameType( + final Object[] frame, + final int index, + int v, + final char[] buf, + final Label[] labels){ + int type = b[v++] & 0xFF; + switch(type) + { + case 0: + frame[index] = Opcodes.TOP; + break; + case 1: + frame[index] = Opcodes.INTEGER; + break; + case 2: + frame[index] = Opcodes.FLOAT; + break; + case 3: + frame[index] = Opcodes.DOUBLE; + break; + case 4: + frame[index] = Opcodes.LONG; + break; + case 5: + frame[index] = Opcodes.NULL; + break; + case 6: + frame[index] = Opcodes.UNINITIALIZED_THIS; + break; + case 7: // Object + frame[index] = readClass(v, buf); + v += 2; + break; + default: // Uninitialized + int offset = readUnsignedShort(v); + if(labels[offset] == null) + { + labels[offset] = new Label(); + } + frame[index] = labels[offset]; + v += 2; + } + return v; +} + +/** + * Reads an attribute in {@link #b b}. + * + * @param attrs prototypes of the attributes that must be parsed during the + * visit of the class. Any attribute whose type is not equal to the + * type of one the prototypes is ignored (i.e. an empty + * {@link Attribute} instance is returned). + * @param type the type of the attribute. + * @param off index of the first byte of the attribute's content in + * {@link #b b}. The 6 attribute header bytes, containing the type + * and the length of the attribute, are not taken into account here + * (they have already been read). + * @param len the length of the attribute's content. + * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, + * {@link #readClass(int,char[]) readClass} or + * {@link #readConst readConst}. + * @param codeOff index of the first byte of code's attribute content in + * {@link #b b}, or -1 if the attribute to be read is not a code + * attribute. The 6 attribute header bytes, containing the type and + * the length of the attribute, are not taken into account here. + * @param labels the labels of the method's code, or null if the + * attribute to be read is not a code attribute. + * @return the attribute that has been read, or null to skip this + * attribute. + */ +private Attribute readAttribute( + final Attribute[] attrs, + final String type, + final int off, + final int len, + final char[] buf, + final int codeOff, + final Label[] labels){ + for(int i = 0; i < attrs.length; ++i) + { + if(attrs[i].type.equals(type)) + { + return attrs[i].read(this, off, len, buf, codeOff, labels); + } + } + return new Attribute(type).read(this, off, len, null, -1, null); +} + +// ------------------------------------------------------------------------ +// Utility methods: low level parsing +// ------------------------------------------------------------------------ + +/** + * Returns the start index of the constant pool item in {@link #b b}, plus + * one. This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param item the index a constant pool item. + * @return the start index of the constant pool item in {@link #b b}, plus + * one. + */ +public int getItem(final int item){ + return items[item]; +} + +/** + * Reads a byte value in {@link #b b}. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ +public int readByte(final int index){ + return b[index] & 0xFF; +} + +/** + * Reads an unsigned short value in {@link #b b}. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ +public int readUnsignedShort(final int index){ + byte[] b = this.b; + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); +} + +/** + * Reads a signed short value in {@link #b b}. This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ +public short readShort(final int index){ + byte[] b = this.b; + return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); +} + +/** + * Reads a signed int value in {@link #b b}. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ +public int readInt(final int index){ + byte[] b = this.b; + return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) + | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); +} + +/** + * Reads a signed long value in {@link #b b}. This method is intended + * for {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @param index the start index of the value to be read in {@link #b b}. + * @return the read value. + */ +public long readLong(final int index){ + long l1 = readInt(index); + long l0 = readInt(index + 4) & 0xFFFFFFFFL; + return (l1 << 32) | l0; +} + +/** + * Reads an UTF8 string constant pool item in {@link #b b}. This method + * is intended for {@link Attribute} sub classes, and is normally not needed + * by class generators or adapters. + * + * @param index the start index of an unsigned short value in {@link #b b}, + * whose value is the index of an UTF8 constant pool item. + * @param buf buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified UTF8 item. + */ +public String readUTF8(int index, final char[] buf){ + int item = readUnsignedShort(index); + String s = strings[item]; + if(s != null) + { + return s; + } + index = items[item]; + return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); +} + +/** + * Reads UTF8 string in {@link #b b}. + * + * @param index start offset of the UTF8 string to be read. + * @param utfLen length of the UTF8 string to be read. + * @param buf buffer to be used to read the string. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified UTF8 string. + */ +private String readUTF(int index, final int utfLen, final char[] buf){ + int endIndex = index + utfLen; + byte[] b = this.b; + int strLen = 0; + int c, d, e; + while(index < endIndex) + { + c = b[index++] & 0xFF; + switch(c >> 4) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + buf[strLen++] = (char) c; + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + d = b[index++]; + buf[strLen++] = (char) (((c & 0x1F) << 6) | (d & 0x3F)); + break; + default: + // 1110 xxxx 10xx xxxx 10xx xxxx + d = b[index++]; + e = b[index++]; + buf[strLen++] = (char) (((c & 0x0F) << 12) + | ((d & 0x3F) << 6) | (e & 0x3F)); + break; + } + } + return new String(buf, 0, strLen); +} + +/** + * Reads a class constant pool item in {@link #b b}. This method is + * intended for {@link Attribute} sub classes, and is normally not needed by + * class generators or adapters. + * + * @param index the start index of an unsigned short value in {@link #b b}, + * whose value is the index of a class constant pool item. + * @param buf buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the String corresponding to the specified class item. + */ +public String readClass(final int index, final char[] buf){ + // computes the start index of the CONSTANT_Class item in b + // and reads the CONSTANT_Utf8 item designated by + // the first two bytes of this CONSTANT_Class item + return readUTF8(items[readUnsignedShort(index)], buf); +} + +/** + * Reads a numeric or string constant pool item in {@link #b b}. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param item the index of a constant pool item. + * @param buf buffer to be used to read the item. This buffer must be + * sufficiently large. It is not automatically resized. + * @return the {@link Integer}, {@link Float}, {@link Long}, + * {@link Double}, {@link String} or {@link Type} corresponding to + * the given constant pool item. + */ +public Object readConst(final int item, final char[] buf){ + int index = items[item]; + switch(b[index - 1]) + { + case ClassWriter.INT: + return new Integer(readInt(index)); + case ClassWriter.FLOAT: + return new Float(Float.intBitsToFloat(readInt(index))); + case ClassWriter.LONG: + return new Long(readLong(index)); + case ClassWriter.DOUBLE: + return new Double(Double.longBitsToDouble(readLong(index))); + case ClassWriter.CLASS: + String s = readUTF8(index, buf); + return s.charAt(0) == '[' + ? Type.getType(s) + : Type.getObjectType(s); + // case ClassWriter.STR: + default: + return readUTF8(index, buf); + } +} +} diff --git a/src/jvm/clojure/asm/ClassVisitor.java b/src/jvm/clojure/asm/ClassVisitor.java index c150e31..2bb300b 100644 --- a/src/jvm/clojure/asm/ClassVisitor.java +++ b/src/jvm/clojure/asm/ClassVisitor.java @@ -1,196 +1,196 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * A visitor to visit a Java class. The methods of this interface must be called - * in the following order: visit [ visitSource ] [ - * visitOuterClass ] ( visitAnnotation | - * visitAttribute )* (visitInnerClass | - * visitField | visitMethod )* visitEnd. - * - * @author Eric Bruneton - */ -public interface ClassVisitor{ - -/** - * Visits the header of the class. - * - * @param version the class version. - * @param access the class's access flags (see {@link Opcodes}). This - * parameter also indicates if the class is deprecated. - * @param name the internal name of the class (see - * {@link Type#getInternalName() getInternalName}). - * @param signature the signature of this class. May be null if - * the class is not a generic one, and does not extend or implement - * generic classes or interfaces. - * @param superName the internal of name of the super class (see - * {@link Type#getInternalName() getInternalName}). For interfaces, - * the super class is {@link Object}. May be null, but - * only for the {@link Object} class. - * @param interfaces the internal names of the class's interfaces (see - * {@link Type#getInternalName() getInternalName}). May be - * null. - */ -void visit( - int version, - int access, - String name, - String signature, - String superName, - String[] interfaces); - -/** - * Visits the source of the class. - * - * @param source the name of the source file from which the class was - * compiled. May be null. - * @param debug additional debug information to compute the correspondance - * between source and compiled elements of the class. May be - * null. - */ -void visitSource(String source, String debug); - -/** - * Visits the enclosing class of the class. This method must be called only - * if the class has an enclosing class. - * - * @param owner internal name of the enclosing class of the class. - * @param name the name of the method that contains the class, or - * null if the class is not enclosed in a method of its - * enclosing class. - * @param desc the descriptor of the method that contains the class, or - * null if the class is not enclosed in a method of its - * enclosing class. - */ -void visitOuterClass(String owner, String name, String desc); - -/** - * Visits an annotation of the class. - * - * @param desc the class descriptor of the annotation class. - * @param visible true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ -AnnotationVisitor visitAnnotation(String desc, boolean visible); - -/** - * Visits a non standard attribute of the class. - * - * @param attr an attribute. - */ -void visitAttribute(Attribute attr); - -/** - * Visits information about an inner class. This inner class is not - * necessarily a member of the class being visited. - * - * @param name the internal name of an inner class (see - * {@link Type#getInternalName() getInternalName}). - * @param outerName the internal name of the class to which the inner class - * belongs (see {@link Type#getInternalName() getInternalName}). May - * be null for not member classes. - * @param innerName the (simple) name of the inner class inside its - * enclosing class. May be null for anonymous inner - * classes. - * @param access the access flags of the inner class as originally declared - * in the enclosing class. - */ -void visitInnerClass( - String name, - String outerName, - String innerName, - int access); - -/** - * Visits a field of the class. - * - * @param access the field's access flags (see {@link Opcodes}). This - * parameter also indicates if the field is synthetic and/or - * deprecated. - * @param name the field's name. - * @param desc the field's descriptor (see {@link Type Type}). - * @param signature the field's signature. May be null if the - * field's type does not use generic types. - * @param value the field's initial value. This parameter, which may be - * null if the field does not have an initial value, must - * be an {@link Integer}, a {@link Float}, a {@link Long}, a - * {@link Double} or a {@link String} (for int, - * float, long or String fields - * respectively). This parameter is only used for static fields. - * Its value is ignored for non static fields, which must be - * initialized through bytecode instructions in constructors or - * methods. - * @return a visitor to visit field annotations and attributes, or - * null if this class visitor is not interested in - * visiting these annotations and attributes. - */ -FieldVisitor visitField( - int access, - String name, - String desc, - String signature, - Object value); - -/** - * Visits a method of the class. This method must return a new - * {@link MethodVisitor} instance (or null) each time it is - * called, i.e., it should not return a previously returned visitor. - * - * @param access the method's access flags (see {@link Opcodes}). This - * parameter also indicates if the method is synthetic and/or - * deprecated. - * @param name the method's name. - * @param desc the method's descriptor (see {@link Type Type}). - * @param signature the method's signature. May be null if the - * method parameters, return type and exceptions do not use generic - * types. - * @param exceptions the internal names of the method's exception classes - * (see {@link Type#getInternalName() getInternalName}). May be - * null. - * @return an object to visit the byte code of the method, or null - * if this class visitor is not interested in visiting the code of - * this method. - */ -MethodVisitor visitMethod( - int access, - String name, - String desc, - String signature, - String[] exceptions); - -/** - * Visits the end of the class. This method, which is the last one to be - * called, is used to inform the visitor that all the fields and methods of - * the class have been visited. - */ -void visitEnd(); -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * A visitor to visit a Java class. The methods of this interface must be called + * in the following order: visit [ visitSource ] [ + * visitOuterClass ] ( visitAnnotation | + * visitAttribute )* (visitInnerClass | + * visitField | visitMethod )* visitEnd. + * + * @author Eric Bruneton + */ +public interface ClassVisitor{ + +/** + * Visits the header of the class. + * + * @param version the class version. + * @param access the class's access flags (see {@link Opcodes}). This + * parameter also indicates if the class is deprecated. + * @param name the internal name of the class (see + * {@link Type#getInternalName() getInternalName}). + * @param signature the signature of this class. May be null if + * the class is not a generic one, and does not extend or implement + * generic classes or interfaces. + * @param superName the internal of name of the super class (see + * {@link Type#getInternalName() getInternalName}). For interfaces, + * the super class is {@link Object}. May be null, but + * only for the {@link Object} class. + * @param interfaces the internal names of the class's interfaces (see + * {@link Type#getInternalName() getInternalName}). May be + * null. + */ +void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces); + +/** + * Visits the source of the class. + * + * @param source the name of the source file from which the class was + * compiled. May be null. + * @param debug additional debug information to compute the correspondance + * between source and compiled elements of the class. May be + * null. + */ +void visitSource(String source, String debug); + +/** + * Visits the enclosing class of the class. This method must be called only + * if the class has an enclosing class. + * + * @param owner internal name of the enclosing class of the class. + * @param name the name of the method that contains the class, or + * null if the class is not enclosed in a method of its + * enclosing class. + * @param desc the descriptor of the method that contains the class, or + * null if the class is not enclosed in a method of its + * enclosing class. + */ +void visitOuterClass(String owner, String name, String desc); + +/** + * Visits an annotation of the class. + * + * @param desc the class descriptor of the annotation class. + * @param visible true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ +AnnotationVisitor visitAnnotation(String desc, boolean visible); + +/** + * Visits a non standard attribute of the class. + * + * @param attr an attribute. + */ +void visitAttribute(Attribute attr); + +/** + * Visits information about an inner class. This inner class is not + * necessarily a member of the class being visited. + * + * @param name the internal name of an inner class (see + * {@link Type#getInternalName() getInternalName}). + * @param outerName the internal name of the class to which the inner class + * belongs (see {@link Type#getInternalName() getInternalName}). May + * be null for not member classes. + * @param innerName the (simple) name of the inner class inside its + * enclosing class. May be null for anonymous inner + * classes. + * @param access the access flags of the inner class as originally declared + * in the enclosing class. + */ +void visitInnerClass( + String name, + String outerName, + String innerName, + int access); + +/** + * Visits a field of the class. + * + * @param access the field's access flags (see {@link Opcodes}). This + * parameter also indicates if the field is synthetic and/or + * deprecated. + * @param name the field's name. + * @param desc the field's descriptor (see {@link Type Type}). + * @param signature the field's signature. May be null if the + * field's type does not use generic types. + * @param value the field's initial value. This parameter, which may be + * null if the field does not have an initial value, must + * be an {@link Integer}, a {@link Float}, a {@link Long}, a + * {@link Double} or a {@link String} (for int, + * float, long or String fields + * respectively). This parameter is only used for static fields. + * Its value is ignored for non static fields, which must be + * initialized through bytecode instructions in constructors or + * methods. + * @return a visitor to visit field annotations and attributes, or + * null if this class visitor is not interested in + * visiting these annotations and attributes. + */ +FieldVisitor visitField( + int access, + String name, + String desc, + String signature, + Object value); + +/** + * Visits a method of the class. This method must return a new + * {@link MethodVisitor} instance (or null) each time it is + * called, i.e., it should not return a previously returned visitor. + * + * @param access the method's access flags (see {@link Opcodes}). This + * parameter also indicates if the method is synthetic and/or + * deprecated. + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + * @param signature the method's signature. May be null if the + * method parameters, return type and exceptions do not use generic + * types. + * @param exceptions the internal names of the method's exception classes + * (see {@link Type#getInternalName() getInternalName}). May be + * null. + * @return an object to visit the byte code of the method, or null + * if this class visitor is not interested in visiting the code of + * this method. + */ +MethodVisitor visitMethod( + int access, + String name, + String desc, + String signature, + String[] exceptions); + +/** + * Visits the end of the class. This method, which is the last one to be + * called, is used to inform the visitor that all the fields and methods of + * the class have been visited. + */ +void visitEnd(); +} diff --git a/src/jvm/clojure/asm/ClassWriter.java b/src/jvm/clojure/asm/ClassWriter.java index 3bc76dc..958f4fe 100644 --- a/src/jvm/clojure/asm/ClassWriter.java +++ b/src/jvm/clojure/asm/ClassWriter.java @@ -1,1415 +1,1415 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * A {@link ClassVisitor} that generates classes in bytecode form. More - * precisely this visitor generates a byte array conforming to the Java class - * file format. It can be used alone, to generate a Java class "from scratch", - * or with one or more {@link ClassReader ClassReader} and adapter class visitor - * to generate a modified class from one or more existing Java classes. - * - * @author Eric Bruneton - */ -public class ClassWriter implements ClassVisitor{ - -/** - * Flag to automatically compute the maximum stack size and the maximum - * number of local variables of methods. If this flag is set, then the - * arguments of the {@link MethodVisitor#visitMaxs visitMaxs} method of the - * {@link MethodVisitor} returned by the {@link #visitMethod visitMethod} - * method will be ignored, and computed automatically from the signature and - * the bytecode of each method. - * - * @see #ClassWriter(int) - */ -public final static int COMPUTE_MAXS = 1; - -/** - * Flag to automatically compute the stack map frames of methods from - * scratch. If this flag is set, then the calls to the - * {@link MethodVisitor#visitFrame} method are ignored, and the stack map - * frames are recomputed from the methods bytecode. The arguments of the - * {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and - * recomputed from the bytecode. In other words, computeFrames implies - * computeMaxs. - * - * @see #ClassWriter(int) - */ -public final static int COMPUTE_FRAMES = 2; - -/** - * The type of instructions without any argument. - */ -final static int NOARG_INSN = 0; - -/** - * The type of instructions with an signed byte argument. - */ -final static int SBYTE_INSN = 1; - -/** - * The type of instructions with an signed short argument. - */ -final static int SHORT_INSN = 2; - -/** - * The type of instructions with a local variable index argument. - */ -final static int VAR_INSN = 3; - -/** - * The type of instructions with an implicit local variable index argument. - */ -final static int IMPLVAR_INSN = 4; - -/** - * The type of instructions with a type descriptor argument. - */ -final static int TYPE_INSN = 5; - -/** - * The type of field and method invocations instructions. - */ -final static int FIELDORMETH_INSN = 6; - -/** - * The type of the INVOKEINTERFACE instruction. - */ -final static int ITFMETH_INSN = 7; - -/** - * The type of instructions with a 2 bytes bytecode offset label. - */ -final static int LABEL_INSN = 8; - -/** - * The type of instructions with a 4 bytes bytecode offset label. - */ -final static int LABELW_INSN = 9; - -/** - * The type of the LDC instruction. - */ -final static int LDC_INSN = 10; - -/** - * The type of the LDC_W and LDC2_W instructions. - */ -final static int LDCW_INSN = 11; - -/** - * The type of the IINC instruction. - */ -final static int IINC_INSN = 12; - -/** - * The type of the TABLESWITCH instruction. - */ -final static int TABL_INSN = 13; - -/** - * The type of the LOOKUPSWITCH instruction. - */ -final static int LOOK_INSN = 14; - -/** - * The type of the MULTIANEWARRAY instruction. - */ -final static int MANA_INSN = 15; - -/** - * The type of the WIDE instruction. - */ -final static int WIDE_INSN = 16; - -/** - * The instruction types of all JVM opcodes. - */ -static byte[] TYPE; - -/** - * The type of CONSTANT_Class constant pool items. - */ -final static int CLASS = 7; - -/** - * The type of CONSTANT_Fieldref constant pool items. - */ -final static int FIELD = 9; - -/** - * The type of CONSTANT_Methodref constant pool items. - */ -final static int METH = 10; - -/** - * The type of CONSTANT_InterfaceMethodref constant pool items. - */ -final static int IMETH = 11; - -/** - * The type of CONSTANT_String constant pool items. - */ -final static int STR = 8; - -/** - * The type of CONSTANT_Integer constant pool items. - */ -final static int INT = 3; - -/** - * The type of CONSTANT_Float constant pool items. - */ -final static int FLOAT = 4; - -/** - * The type of CONSTANT_Long constant pool items. - */ -final static int LONG = 5; - -/** - * The type of CONSTANT_Double constant pool items. - */ -final static int DOUBLE = 6; - -/** - * The type of CONSTANT_NameAndType constant pool items. - */ -final static int NAME_TYPE = 12; - -/** - * The type of CONSTANT_Utf8 constant pool items. - */ -final static int UTF8 = 1; - -/** - * Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable}, - * instead of the constant pool, in order to avoid clashes with normal - * constant pool items in the ClassWriter constant pool's hash table. - */ -final static int TYPE_NORMAL = 13; - -/** - * Uninitialized type Item stored in the ClassWriter - * {@link ClassWriter#typeTable}, instead of the constant pool, in order to - * avoid clashes with normal constant pool items in the ClassWriter constant - * pool's hash table. - */ -final static int TYPE_UNINIT = 14; - -/** - * Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable}, - * instead of the constant pool, in order to avoid clashes with normal - * constant pool items in the ClassWriter constant pool's hash table. - */ -final static int TYPE_MERGED = 15; - -/** - * The class reader from which this class writer was constructed, if any. - */ -ClassReader cr; - -/** - * Minor and major version numbers of the class to be generated. - */ -int version; - -/** - * Index of the next item to be added in the constant pool. - */ -int index; - -/** - * The constant pool of this class. - */ -ByteVector pool; - -/** - * The constant pool's hash table data. - */ -Item[] items; - -/** - * The threshold of the constant pool's hash table. - */ -int threshold; - -/** - * A reusable key used to look for items in the {@link #items} hash table. - */ -Item key; - -/** - * A reusable key used to look for items in the {@link #items} hash table. - */ -Item key2; - -/** - * A reusable key used to look for items in the {@link #items} hash table. - */ -Item key3; - -/** - * A type table used to temporarily store internal names that will not - * necessarily be stored in the constant pool. This type table is used by - * the control flow and data flow analysis algorithm used to compute stack - * map frames from scratch. This array associates to each index i - * the Item whose index is i. All Item objects stored in this - * array are also stored in the {@link #items} hash table. These two arrays - * allow to retrieve an Item from its index or, conversly, to get the index - * of an Item from its value. Each Item stores an internal name in its - * {@link Item#strVal1} field. - */ -Item[] typeTable; - -/** - * Number of elements in the {@link #typeTable} array. - */ -private short typeCount; // TODO int? - -/** - * The access flags of this class. - */ -private int access; - -/** - * The constant pool item that contains the internal name of this class. - */ -private int name; - -/** - * The internal name of this class. - */ -String thisName; - -/** - * The constant pool item that contains the signature of this class. - */ -private int signature; - -/** - * The constant pool item that contains the internal name of the super class - * of this class. - */ -private int superName; - -/** - * Number of interfaces implemented or extended by this class or interface. - */ -private int interfaceCount; - -/** - * The interfaces implemented or extended by this class or interface. More - * precisely, this array contains the indexes of the constant pool items - * that contain the internal names of these interfaces. - */ -private int[] interfaces; - -/** - * The index of the constant pool item that contains the name of the source - * file from which this class was compiled. - */ -private int sourceFile; - -/** - * The SourceDebug attribute of this class. - */ -private ByteVector sourceDebug; - -/** - * The constant pool item that contains the name of the enclosing class of - * this class. - */ -private int enclosingMethodOwner; - -/** - * The constant pool item that contains the name and descriptor of the - * enclosing method of this class. - */ -private int enclosingMethod; - -/** - * The runtime visible annotations of this class. - */ -private AnnotationWriter anns; - -/** - * The runtime invisible annotations of this class. - */ -private AnnotationWriter ianns; - -/** - * The non standard attributes of this class. - */ -private Attribute attrs; - -/** - * The number of entries in the InnerClasses attribute. - */ -private int innerClassesCount; - -/** - * The InnerClasses attribute. - */ -private ByteVector innerClasses; - -/** - * The fields of this class. These fields are stored in a linked list of - * {@link FieldWriter} objects, linked to each other by their - * {@link FieldWriter#next} field. This field stores the first element of - * this list. - */ -FieldWriter firstField; - -/** - * The fields of this class. These fields are stored in a linked list of - * {@link FieldWriter} objects, linked to each other by their - * {@link FieldWriter#next} field. This field stores the last element of - * this list. - */ -FieldWriter lastField; - -/** - * The methods of this class. These methods are stored in a linked list of - * {@link MethodWriter} objects, linked to each other by their - * {@link MethodWriter#next} field. This field stores the first element of - * this list. - */ -MethodWriter firstMethod; - -/** - * The methods of this class. These methods are stored in a linked list of - * {@link MethodWriter} objects, linked to each other by their - * {@link MethodWriter#next} field. This field stores the last element of - * this list. - */ -MethodWriter lastMethod; - -/** - * true if the maximum stack size and number of local variables - * must be automatically computed. - */ -private boolean computeMaxs; - -/** - * true if the stack map frames must be recomputed from scratch. - */ -private boolean computeFrames; - -/** - * true if the stack map tables of this class are invalid. The - * {@link MethodWriter#resizeInstructions} method cannot transform existing - * stack map tables, and so produces potentially invalid classes when it is - * executed. In this case the class is reread and rewritten with the - * {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize - * stack map tables when this option is used). - */ -boolean invalidFrames; - -// ------------------------------------------------------------------------ -// Static initializer -// ------------------------------------------------------------------------ - -/** - * Computes the instruction types of JVM opcodes. - */ -static - { - int i; - byte[] b = new byte[220]; - String s = "AAAAAAAAAAAAAAAABCKLLDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD" - + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - + "AAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAIIIIIIIIIIIIIIIIDNOAA" - + "AAAAGGGGGGGHAFBFAAFFAAQPIIJJIIIIIIIIIIIIIIIIII"; - for(i = 0; i < b.length; ++i) - { - b[i] = (byte) (s.charAt(i) - 'A'); - } - TYPE = b; - - // code to generate the above string - // - // // SBYTE_INSN instructions - // b[Constants.NEWARRAY] = SBYTE_INSN; - // b[Constants.BIPUSH] = SBYTE_INSN; - // - // // SHORT_INSN instructions - // b[Constants.SIPUSH] = SHORT_INSN; - // - // // (IMPL)VAR_INSN instructions - // b[Constants.RET] = VAR_INSN; - // for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) { - // b[i] = VAR_INSN; - // } - // for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) { - // b[i] = VAR_INSN; - // } - // for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3 - // b[i] = IMPLVAR_INSN; - // } - // for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3 - // b[i] = IMPLVAR_INSN; - // } - // - // // TYPE_INSN instructions - // b[Constants.NEW] = TYPE_INSN; - // b[Constants.ANEWARRAY] = TYPE_INSN; - // b[Constants.CHECKCAST] = TYPE_INSN; - // b[Constants.INSTANCEOF] = TYPE_INSN; - // - // // (Set)FIELDORMETH_INSN instructions - // for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) { - // b[i] = FIELDORMETH_INSN; - // } - // b[Constants.INVOKEINTERFACE] = ITFMETH_INSN; - // - // // LABEL(W)_INSN instructions - // for (i = Constants.IFEQ; i <= Constants.JSR; ++i) { - // b[i] = LABEL_INSN; - // } - // b[Constants.IFNULL] = LABEL_INSN; - // b[Constants.IFNONNULL] = LABEL_INSN; - // b[200] = LABELW_INSN; // GOTO_W - // b[201] = LABELW_INSN; // JSR_W - // // temporary opcodes used internally by ASM - see Label and - // MethodWriter - // for (i = 202; i < 220; ++i) { - // b[i] = LABEL_INSN; - // } - // - // // LDC(_W) instructions - // b[Constants.LDC] = LDC_INSN; - // b[19] = LDCW_INSN; // LDC_W - // b[20] = LDCW_INSN; // LDC2_W - // - // // special instructions - // b[Constants.IINC] = IINC_INSN; - // b[Constants.TABLESWITCH] = TABL_INSN; - // b[Constants.LOOKUPSWITCH] = LOOK_INSN; - // b[Constants.MULTIANEWARRAY] = MANA_INSN; - // b[196] = WIDE_INSN; // WIDE - // - // for (i = 0; i < b.length; ++i) { - // System.err.print((char)('A' + b[i])); - // } - // System.err.println(); - } - -// ------------------------------------------------------------------------ -// Constructor -// ------------------------------------------------------------------------ - -/** - * Constructs a new {@link ClassWriter} object. - * - * @param flags option flags that can be used to modify the default behavior - * of this class. See {@link #COMPUTE_MAXS}, {@link #COMPUTE_FRAMES}. - */ -public ClassWriter(final int flags){ - index = 1; - pool = new ByteVector(); - items = new Item[256]; - threshold = (int) (0.75d * items.length); - key = new Item(); - key2 = new Item(); - key3 = new Item(); - this.computeMaxs = (flags & COMPUTE_MAXS) != 0; - this.computeFrames = (flags & COMPUTE_FRAMES) != 0; -} - -/** - * Constructs a new {@link ClassWriter} object and enables optimizations for - * "mostly add" bytecode transformations. These optimizations are the - * following: - *

- *

  • The constant pool from the original class is copied as is in - * the new class, which saves time. New constant pool entries will be added - * at the end if necessary, but unused constant pool entries won't be - * removed.
  • Methods that are not transformed are copied as - * is in the new class, directly from the original class bytecode (i.e. - * without emitting visit events for all the method instructions), which - * saves a lot of time. Untransformed methods are detected by the - * fact that the {@link ClassReader} receives {@link MethodVisitor} objects - * that come from a {@link ClassWriter} (and not from a custom - * {@link ClassAdapter} or any other {@link ClassVisitor} instance).
  • - *
- * - * @param classReader the {@link ClassReader} used to read the original - * class. It will be used to copy the entire constant pool from the - * original class and also to copy other fragments of original - * bytecode where applicable. - * @param flags option flags that can be used to modify the default behavior - * of this class. See {@link #COMPUTE_MAXS}, {@link #COMPUTE_FRAMES}. - */ -public ClassWriter(final ClassReader classReader, final int flags){ - this(flags); - classReader.copyPool(this); - this.cr = classReader; -} - -// ------------------------------------------------------------------------ -// Implementation of the ClassVisitor interface -// ------------------------------------------------------------------------ - -public void visit( - final int version, - final int access, - final String name, - final String signature, - final String superName, - final String[] interfaces){ - this.version = version; - this.access = access; - this.name = newClass(name); - thisName = name; - if(signature != null) - { - this.signature = newUTF8(signature); - } - this.superName = superName == null ? 0 : newClass(superName); - if(interfaces != null && interfaces.length > 0) - { - interfaceCount = interfaces.length; - this.interfaces = new int[interfaceCount]; - for(int i = 0; i < interfaceCount; ++i) - { - this.interfaces[i] = newClass(interfaces[i]); - } - } -} - -public void visitSource(final String file, final String debug){ - if(file != null) - { - sourceFile = newUTF8(file); - } - if(debug != null) - { - sourceDebug = new ByteVector().putUTF8(debug); - } -} - -public void visitOuterClass( - final String owner, - final String name, - final String desc){ - enclosingMethodOwner = newClass(owner); - if(name != null && desc != null) - { - enclosingMethod = newNameType(name, desc); - } -} - -public AnnotationVisitor visitAnnotation( - final String desc, - final boolean visible){ - ByteVector bv = new ByteVector(); - // write type, and reserve space for values count - bv.putShort(newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2); - if(visible) - { - aw.next = anns; - anns = aw; - } - else - { - aw.next = ianns; - ianns = aw; - } - return aw; -} - -public void visitAttribute(final Attribute attr){ - attr.next = attrs; - attrs = attr; -} - -public void visitInnerClass( - final String name, - final String outerName, - final String innerName, - final int access){ - if(innerClasses == null) - { - innerClasses = new ByteVector(); - } - ++innerClassesCount; - innerClasses.putShort(name == null ? 0 : newClass(name)); - innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); - innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); - innerClasses.putShort(access); -} - -public FieldVisitor visitField( - final int access, - final String name, - final String desc, - final String signature, - final Object value){ - return new FieldWriter(this, access, name, desc, signature, value); -} - -public MethodVisitor visitMethod( - final int access, - final String name, - final String desc, - final String signature, - final String[] exceptions){ - return new MethodWriter(this, - access, - name, - desc, - signature, - exceptions, - computeMaxs, - computeFrames); -} - -public void visitEnd(){ -} - -// ------------------------------------------------------------------------ -// Other public methods -// ------------------------------------------------------------------------ - -/** - * Returns the bytecode of the class that was build with this class writer. - * - * @return the bytecode of the class that was build with this class writer. - */ -public byte[] toByteArray(){ - // computes the real size of the bytecode of this class - int size = 24 + 2 * interfaceCount; - int nbFields = 0; - FieldWriter fb = firstField; - while(fb != null) - { - ++nbFields; - size += fb.getSize(); - fb = fb.next; - } - int nbMethods = 0; - MethodWriter mb = firstMethod; - while(mb != null) - { - ++nbMethods; - size += mb.getSize(); - mb = mb.next; - } - int attributeCount = 0; - if(signature != 0) - { - ++attributeCount; - size += 8; - newUTF8("Signature"); - } - if(sourceFile != 0) - { - ++attributeCount; - size += 8; - newUTF8("SourceFile"); - } - if(sourceDebug != null) - { - ++attributeCount; - size += sourceDebug.length + 4; - newUTF8("SourceDebugExtension"); - } - if(enclosingMethodOwner != 0) - { - ++attributeCount; - size += 10; - newUTF8("EnclosingMethod"); - } - if((access & Opcodes.ACC_DEPRECATED) != 0) - { - ++attributeCount; - size += 6; - newUTF8("Deprecated"); - } - if((access & Opcodes.ACC_SYNTHETIC) != 0 - && (version & 0xffff) < Opcodes.V1_5) - { - ++attributeCount; - size += 6; - newUTF8("Synthetic"); - } - if(innerClasses != null) - { - ++attributeCount; - size += 8 + innerClasses.length; - newUTF8("InnerClasses"); - } - if(anns != null) - { - ++attributeCount; - size += 8 + anns.getSize(); - newUTF8("RuntimeVisibleAnnotations"); - } - if(ianns != null) - { - ++attributeCount; - size += 8 + ianns.getSize(); - newUTF8("RuntimeInvisibleAnnotations"); - } - if(attrs != null) - { - attributeCount += attrs.getCount(); - size += attrs.getSize(this, null, 0, -1, -1); - } - size += pool.length; - // allocates a byte vector of this size, in order to avoid unnecessary - // arraycopy operations in the ByteVector.enlarge() method - ByteVector out = new ByteVector(size); - out.putInt(0xCAFEBABE).putInt(version); - out.putShort(index).putByteArray(pool.data, 0, pool.length); - out.putShort(access).putShort(name).putShort(superName); - out.putShort(interfaceCount); - for(int i = 0; i < interfaceCount; ++i) - { - out.putShort(interfaces[i]); - } - out.putShort(nbFields); - fb = firstField; - while(fb != null) - { - fb.put(out); - fb = fb.next; - } - out.putShort(nbMethods); - mb = firstMethod; - while(mb != null) - { - mb.put(out); - mb = mb.next; - } - out.putShort(attributeCount); - if(signature != 0) - { - out.putShort(newUTF8("Signature")).putInt(2).putShort(signature); - } - if(sourceFile != 0) - { - out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); - } - if(sourceDebug != null) - { - int len = sourceDebug.length - 2; - out.putShort(newUTF8("SourceDebugExtension")).putInt(len); - out.putByteArray(sourceDebug.data, 2, len); - } - if(enclosingMethodOwner != 0) - { - out.putShort(newUTF8("EnclosingMethod")).putInt(4); - out.putShort(enclosingMethodOwner).putShort(enclosingMethod); - } - if((access & Opcodes.ACC_DEPRECATED) != 0) - { - out.putShort(newUTF8("Deprecated")).putInt(0); - } - if((access & Opcodes.ACC_SYNTHETIC) != 0 - && (version & 0xffff) < Opcodes.V1_5) - { - out.putShort(newUTF8("Synthetic")).putInt(0); - } - if(innerClasses != null) - { - out.putShort(newUTF8("InnerClasses")); - out.putInt(innerClasses.length + 2).putShort(innerClassesCount); - out.putByteArray(innerClasses.data, 0, innerClasses.length); - } - if(anns != null) - { - out.putShort(newUTF8("RuntimeVisibleAnnotations")); - anns.put(out); - } - if(ianns != null) - { - out.putShort(newUTF8("RuntimeInvisibleAnnotations")); - ianns.put(out); - } - if(attrs != null) - { - attrs.put(this, null, 0, -1, -1, out); - } - if(invalidFrames) - { - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - new ClassReader(out.data).accept(cw, ClassReader.SKIP_FRAMES); - return cw.toByteArray(); - } - return out.data; -} - -// ------------------------------------------------------------------------ -// Utility methods: constant pool management -// ------------------------------------------------------------------------ - -/** - * Adds a number or string constant to the constant pool of the class being - * build. Does nothing if the constant pool already contains a similar item. - * - * @param cst the value of the constant to be added to the constant pool. - * This parameter must be an {@link Integer}, a {@link Float}, a - * {@link Long}, a {@link Double}, a {@link String} or a - * {@link Type}. - * @return a new or already existing constant item with the given value. - */ -Item newConstItem(final Object cst){ - if(cst instanceof Integer) - { - int val = ((Integer) cst).intValue(); - return newInteger(val); - } - else if(cst instanceof Byte) - { - int val = ((Byte) cst).intValue(); - return newInteger(val); - } - else if(cst instanceof Character) - { - int val = ((Character) cst).charValue(); - return newInteger(val); - } - else if(cst instanceof Short) - { - int val = ((Short) cst).intValue(); - return newInteger(val); - } - else if(cst instanceof Boolean) - { - int val = ((Boolean) cst).booleanValue() ? 1 : 0; - return newInteger(val); - } - else if(cst instanceof Float) - { - float val = ((Float) cst).floatValue(); - return newFloat(val); - } - else if(cst instanceof Long) - { - long val = ((Long) cst).longValue(); - return newLong(val); - } - else if(cst instanceof Double) - { - double val = ((Double) cst).doubleValue(); - return newDouble(val); - } - else if(cst instanceof String) - { - return newString((String) cst); - } - else if(cst instanceof Type) - { - Type t = (Type) cst; - return newClassItem(t.getSort() == Type.OBJECT - ? t.getInternalName() - : t.getDescriptor()); - } - else - { - throw new IllegalArgumentException("value " + cst); - } -} - -/** - * Adds a number or string constant to the constant pool of the class being - * build. Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param cst the value of the constant to be added to the constant pool. - * This parameter must be an {@link Integer}, a {@link Float}, a - * {@link Long}, a {@link Double} or a {@link String}. - * @return the index of a new or already existing constant item with the - * given value. - */ -public int newConst(final Object cst){ - return newConstItem(cst).index; -} - -/** - * Adds an UTF8 string to the constant pool of the class being build. Does - * nothing if the constant pool already contains a similar item. This - * method is intended for {@link Attribute} sub classes, and is normally not - * needed by class generators or adapters. - * - * @param value the String value. - * @return the index of a new or already existing UTF8 item. - */ -public int newUTF8(final String value){ - key.set(UTF8, value, null, null); - Item result = get(key); - if(result == null) - { - pool.putByte(UTF8).putUTF8(value); - result = new Item(index++, key); - put(result); - } - return result.index; -} - -/** - * Adds a class reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param value the internal name of the class. - * @return a new or already existing class reference item. - */ -Item newClassItem(final String value){ - key2.set(CLASS, value, null, null); - Item result = get(key2); - if(result == null) - { - pool.put12(CLASS, newUTF8(value)); - result = new Item(index++, key2); - put(result); - } - return result; -} - -/** - * Adds a class reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param value the internal name of the class. - * @return the index of a new or already existing class reference item. - */ -public int newClass(final String value){ - return newClassItem(value).index; -} - -/** - * Adds a field reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * - * @param owner the internal name of the field's owner class. - * @param name the field's name. - * @param desc the field's descriptor. - * @return a new or already existing field reference item. - */ -Item newFieldItem(final String owner, final String name, final String desc){ - key3.set(FIELD, owner, name, desc); - Item result = get(key3); - if(result == null) - { - put122(FIELD, newClass(owner), newNameType(name, desc)); - result = new Item(index++, key3); - put(result); - } - return result; -} - -/** - * Adds a field reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param owner the internal name of the field's owner class. - * @param name the field's name. - * @param desc the field's descriptor. - * @return the index of a new or already existing field reference item. - */ -public int newField(final String owner, final String name, final String desc){ - return newFieldItem(owner, name, desc).index; -} - -/** - * Adds a method reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * - * @param owner the internal name of the method's owner class. - * @param name the method's name. - * @param desc the method's descriptor. - * @param itf true if owner is an interface. - * @return a new or already existing method reference item. - */ -Item newMethodItem( - final String owner, - final String name, - final String desc, - final boolean itf){ - int type = itf ? IMETH : METH; - key3.set(type, owner, name, desc); - Item result = get(key3); - if(result == null) - { - put122(type, newClass(owner), newNameType(name, desc)); - result = new Item(index++, key3); - put(result); - } - return result; -} - -/** - * Adds a method reference to the constant pool of the class being build. - * Does nothing if the constant pool already contains a similar item. - * This method is intended for {@link Attribute} sub classes, and is - * normally not needed by class generators or adapters. - * - * @param owner the internal name of the method's owner class. - * @param name the method's name. - * @param desc the method's descriptor. - * @param itf true if owner is an interface. - * @return the index of a new or already existing method reference item. - */ -public int newMethod( - final String owner, - final String name, - final String desc, - final boolean itf){ - return newMethodItem(owner, name, desc, itf).index; -} - -/** - * Adds an integer to the constant pool of the class being build. Does - * nothing if the constant pool already contains a similar item. - * - * @param value the int value. - * @return a new or already existing int item. - */ -Item newInteger(final int value){ - key.set(value); - Item result = get(key); - if(result == null) - { - pool.putByte(INT).putInt(value); - result = new Item(index++, key); - put(result); - } - return result; -} - -/** - * Adds a float to the constant pool of the class being build. Does nothing - * if the constant pool already contains a similar item. - * - * @param value the float value. - * @return a new or already existing float item. - */ -Item newFloat(final float value){ - key.set(value); - Item result = get(key); - if(result == null) - { - pool.putByte(FLOAT).putInt(key.intVal); - result = new Item(index++, key); - put(result); - } - return result; -} - -/** - * Adds a long to the constant pool of the class being build. Does nothing - * if the constant pool already contains a similar item. - * - * @param value the long value. - * @return a new or already existing long item. - */ -Item newLong(final long value){ - key.set(value); - Item result = get(key); - if(result == null) - { - pool.putByte(LONG).putLong(value); - result = new Item(index, key); - put(result); - index += 2; - } - return result; -} - -/** - * Adds a double to the constant pool of the class being build. Does nothing - * if the constant pool already contains a similar item. - * - * @param value the double value. - * @return a new or already existing double item. - */ -Item newDouble(final double value){ - key.set(value); - Item result = get(key); - if(result == null) - { - pool.putByte(DOUBLE).putLong(key.longVal); - result = new Item(index, key); - put(result); - index += 2; - } - return result; -} - -/** - * Adds a string to the constant pool of the class being build. Does nothing - * if the constant pool already contains a similar item. - * - * @param value the String value. - * @return a new or already existing string item. - */ -private Item newString(final String value){ - key2.set(STR, value, null, null); - Item result = get(key2); - if(result == null) - { - pool.put12(STR, newUTF8(value)); - result = new Item(index++, key2); - put(result); - } - return result; -} - -/** - * Adds a name and type to the constant pool of the class being build. Does - * nothing if the constant pool already contains a similar item. This - * method is intended for {@link Attribute} sub classes, and is normally not - * needed by class generators or adapters. - * - * @param name a name. - * @param desc a type descriptor. - * @return the index of a new or already existing name and type item. - */ -public int newNameType(final String name, final String desc){ - key2.set(NAME_TYPE, name, desc, null); - Item result = get(key2); - if(result == null) - { - put122(NAME_TYPE, newUTF8(name), newUTF8(desc)); - result = new Item(index++, key2); - put(result); - } - return result.index; -} - -/** - * Adds the given internal name to {@link #typeTable} and returns its index. - * Does nothing if the type table already contains this internal name. - * - * @param type the internal name to be added to the type table. - * @return the index of this internal name in the type table. - */ -int addType(final String type){ - key.set(TYPE_NORMAL, type, null, null); - Item result = get(key); - if(result == null) - { - result = addType(key); - } - return result.index; -} - -/** - * Adds the given "uninitialized" type to {@link #typeTable} and returns its - * index. This method is used for UNINITIALIZED types, made of an internal - * name and a bytecode offset. - * - * @param type the internal name to be added to the type table. - * @param offset the bytecode offset of the NEW instruction that created - * this UNINITIALIZED type value. - * @return the index of this internal name in the type table. - */ -int addUninitializedType(final String type, final int offset){ - key.type = TYPE_UNINIT; - key.intVal = offset; - key.strVal1 = type; - key.hashCode = 0x7FFFFFFF & (TYPE_UNINIT + type.hashCode() + offset); - Item result = get(key); - if(result == null) - { - result = addType(key); - } - return result.index; -} - -/** - * Adds the given Item to {@link #typeTable}. - * - * @param item the value to be added to the type table. - * @return the added Item, which a new Item instance with the same value as - * the given Item. - */ -private Item addType(final Item item){ - ++typeCount; - Item result = new Item(typeCount, key); - put(result); - if(typeTable == null) - { - typeTable = new Item[16]; - } - if(typeCount == typeTable.length) - { - Item[] newTable = new Item[2 * typeTable.length]; - System.arraycopy(typeTable, 0, newTable, 0, typeTable.length); - typeTable = newTable; - } - typeTable[typeCount] = result; - return result; -} - -/** - * Returns the index of the common super type of the two given types. This - * method calls {@link #getCommonSuperClass} and caches the result in the - * {@link #items} hash table to speedup future calls with the same - * parameters. - * - * @param type1 index of an internal name in {@link #typeTable}. - * @param type2 index of an internal name in {@link #typeTable}. - * @return the index of the common super type of the two given types. - */ -int getMergedType(final int type1, final int type2){ - key2.type = TYPE_MERGED; - key2.longVal = type1 | (((long) type2) << 32); - key2.hashCode = 0x7FFFFFFF & (TYPE_MERGED + type1 + type2); - Item result = get(key2); - if(result == null) - { - String t = typeTable[type1].strVal1; - String u = typeTable[type2].strVal1; - key2.intVal = addType(getCommonSuperClass(t, u)); - result = new Item((short) 0, key2); - put(result); - } - return result.intVal; -} - -/** - * Returns the common super type of the two given types. The default - * implementation of this method loads the two given classes and uses - * the java.lang.Class methods to find the common super class. It can be - * overriden to compute this common super type in other ways, in particular - * without actually loading any class, or to take into account the class - * that is currently being generated by this ClassWriter, which can of - * course not be loaded since it is under construction. - * - * @param type1 the internal name of a class. - * @param type2 the internal name of another class. - * @return the internal name of the common super class of the two given - * classes. - */ -protected String getCommonSuperClass(final String type1, final String type2){ - Class c, d; - try - { - c = Class.forName(type1.replace('/', '.')); - d = Class.forName(type2.replace('/', '.')); - } - catch(ClassNotFoundException e) - { - throw new RuntimeException(e); - } - if(c.isAssignableFrom(d)) - { - return type1; - } - if(d.isAssignableFrom(c)) - { - return type2; - } - if(c.isInterface() || d.isInterface()) - { - return "java/lang/Object"; - } - else - { - do - { - c = c.getSuperclass(); - } while(!c.isAssignableFrom(d)); - return c.getName().replace('.', '/'); - } -} - -/** - * Returns the constant pool's hash table item which is equal to the given - * item. - * - * @param key a constant pool item. - * @return the constant pool's hash table item which is equal to the given - * item, or null if there is no such item. - */ -private Item get(final Item key){ - Item i = items[key.hashCode % items.length]; - while(i != null && !key.isEqualTo(i)) - { - i = i.next; - } - return i; -} - -/** - * Puts the given item in the constant pool's hash table. The hash table - * must not already contains this item. - * - * @param i the item to be added to the constant pool's hash table. - */ -private void put(final Item i){ - if(index > threshold) - { - int ll = items.length; - int nl = ll * 2 + 1; - Item[] newItems = new Item[nl]; - for(int l = ll - 1; l >= 0; --l) - { - Item j = items[l]; - while(j != null) - { - int index = j.hashCode % newItems.length; - Item k = j.next; - j.next = newItems[index]; - newItems[index] = j; - j = k; - } - } - items = newItems; - threshold = (int) (nl * 0.75); - } - int index = i.hashCode % items.length; - i.next = items[index]; - items[index] = i; -} - -/** - * Puts one byte and two shorts into the constant pool. - * - * @param b a byte. - * @param s1 a short. - * @param s2 another short. - */ -private void put122(final int b, final int s1, final int s2){ - pool.put12(b, s1).putShort(s2); -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * A {@link ClassVisitor} that generates classes in bytecode form. More + * precisely this visitor generates a byte array conforming to the Java class + * file format. It can be used alone, to generate a Java class "from scratch", + * or with one or more {@link ClassReader ClassReader} and adapter class visitor + * to generate a modified class from one or more existing Java classes. + * + * @author Eric Bruneton + */ +public class ClassWriter implements ClassVisitor{ + +/** + * Flag to automatically compute the maximum stack size and the maximum + * number of local variables of methods. If this flag is set, then the + * arguments of the {@link MethodVisitor#visitMaxs visitMaxs} method of the + * {@link MethodVisitor} returned by the {@link #visitMethod visitMethod} + * method will be ignored, and computed automatically from the signature and + * the bytecode of each method. + * + * @see #ClassWriter(int) + */ +public final static int COMPUTE_MAXS = 1; + +/** + * Flag to automatically compute the stack map frames of methods from + * scratch. If this flag is set, then the calls to the + * {@link MethodVisitor#visitFrame} method are ignored, and the stack map + * frames are recomputed from the methods bytecode. The arguments of the + * {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and + * recomputed from the bytecode. In other words, computeFrames implies + * computeMaxs. + * + * @see #ClassWriter(int) + */ +public final static int COMPUTE_FRAMES = 2; + +/** + * The type of instructions without any argument. + */ +final static int NOARG_INSN = 0; + +/** + * The type of instructions with an signed byte argument. + */ +final static int SBYTE_INSN = 1; + +/** + * The type of instructions with an signed short argument. + */ +final static int SHORT_INSN = 2; + +/** + * The type of instructions with a local variable index argument. + */ +final static int VAR_INSN = 3; + +/** + * The type of instructions with an implicit local variable index argument. + */ +final static int IMPLVAR_INSN = 4; + +/** + * The type of instructions with a type descriptor argument. + */ +final static int TYPE_INSN = 5; + +/** + * The type of field and method invocations instructions. + */ +final static int FIELDORMETH_INSN = 6; + +/** + * The type of the INVOKEINTERFACE instruction. + */ +final static int ITFMETH_INSN = 7; + +/** + * The type of instructions with a 2 bytes bytecode offset label. + */ +final static int LABEL_INSN = 8; + +/** + * The type of instructions with a 4 bytes bytecode offset label. + */ +final static int LABELW_INSN = 9; + +/** + * The type of the LDC instruction. + */ +final static int LDC_INSN = 10; + +/** + * The type of the LDC_W and LDC2_W instructions. + */ +final static int LDCW_INSN = 11; + +/** + * The type of the IINC instruction. + */ +final static int IINC_INSN = 12; + +/** + * The type of the TABLESWITCH instruction. + */ +final static int TABL_INSN = 13; + +/** + * The type of the LOOKUPSWITCH instruction. + */ +final static int LOOK_INSN = 14; + +/** + * The type of the MULTIANEWARRAY instruction. + */ +final static int MANA_INSN = 15; + +/** + * The type of the WIDE instruction. + */ +final static int WIDE_INSN = 16; + +/** + * The instruction types of all JVM opcodes. + */ +static byte[] TYPE; + +/** + * The type of CONSTANT_Class constant pool items. + */ +final static int CLASS = 7; + +/** + * The type of CONSTANT_Fieldref constant pool items. + */ +final static int FIELD = 9; + +/** + * The type of CONSTANT_Methodref constant pool items. + */ +final static int METH = 10; + +/** + * The type of CONSTANT_InterfaceMethodref constant pool items. + */ +final static int IMETH = 11; + +/** + * The type of CONSTANT_String constant pool items. + */ +final static int STR = 8; + +/** + * The type of CONSTANT_Integer constant pool items. + */ +final static int INT = 3; + +/** + * The type of CONSTANT_Float constant pool items. + */ +final static int FLOAT = 4; + +/** + * The type of CONSTANT_Long constant pool items. + */ +final static int LONG = 5; + +/** + * The type of CONSTANT_Double constant pool items. + */ +final static int DOUBLE = 6; + +/** + * The type of CONSTANT_NameAndType constant pool items. + */ +final static int NAME_TYPE = 12; + +/** + * The type of CONSTANT_Utf8 constant pool items. + */ +final static int UTF8 = 1; + +/** + * Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable}, + * instead of the constant pool, in order to avoid clashes with normal + * constant pool items in the ClassWriter constant pool's hash table. + */ +final static int TYPE_NORMAL = 13; + +/** + * Uninitialized type Item stored in the ClassWriter + * {@link ClassWriter#typeTable}, instead of the constant pool, in order to + * avoid clashes with normal constant pool items in the ClassWriter constant + * pool's hash table. + */ +final static int TYPE_UNINIT = 14; + +/** + * Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable}, + * instead of the constant pool, in order to avoid clashes with normal + * constant pool items in the ClassWriter constant pool's hash table. + */ +final static int TYPE_MERGED = 15; + +/** + * The class reader from which this class writer was constructed, if any. + */ +ClassReader cr; + +/** + * Minor and major version numbers of the class to be generated. + */ +int version; + +/** + * Index of the next item to be added in the constant pool. + */ +int index; + +/** + * The constant pool of this class. + */ +ByteVector pool; + +/** + * The constant pool's hash table data. + */ +Item[] items; + +/** + * The threshold of the constant pool's hash table. + */ +int threshold; + +/** + * A reusable key used to look for items in the {@link #items} hash table. + */ +Item key; + +/** + * A reusable key used to look for items in the {@link #items} hash table. + */ +Item key2; + +/** + * A reusable key used to look for items in the {@link #items} hash table. + */ +Item key3; + +/** + * A type table used to temporarily store internal names that will not + * necessarily be stored in the constant pool. This type table is used by + * the control flow and data flow analysis algorithm used to compute stack + * map frames from scratch. This array associates to each index i + * the Item whose index is i. All Item objects stored in this + * array are also stored in the {@link #items} hash table. These two arrays + * allow to retrieve an Item from its index or, conversly, to get the index + * of an Item from its value. Each Item stores an internal name in its + * {@link Item#strVal1} field. + */ +Item[] typeTable; + +/** + * Number of elements in the {@link #typeTable} array. + */ +private short typeCount; // TODO int? + +/** + * The access flags of this class. + */ +private int access; + +/** + * The constant pool item that contains the internal name of this class. + */ +private int name; + +/** + * The internal name of this class. + */ +String thisName; + +/** + * The constant pool item that contains the signature of this class. + */ +private int signature; + +/** + * The constant pool item that contains the internal name of the super class + * of this class. + */ +private int superName; + +/** + * Number of interfaces implemented or extended by this class or interface. + */ +private int interfaceCount; + +/** + * The interfaces implemented or extended by this class or interface. More + * precisely, this array contains the indexes of the constant pool items + * that contain the internal names of these interfaces. + */ +private int[] interfaces; + +/** + * The index of the constant pool item that contains the name of the source + * file from which this class was compiled. + */ +private int sourceFile; + +/** + * The SourceDebug attribute of this class. + */ +private ByteVector sourceDebug; + +/** + * The constant pool item that contains the name of the enclosing class of + * this class. + */ +private int enclosingMethodOwner; + +/** + * The constant pool item that contains the name and descriptor of the + * enclosing method of this class. + */ +private int enclosingMethod; + +/** + * The runtime visible annotations of this class. + */ +private AnnotationWriter anns; + +/** + * The runtime invisible annotations of this class. + */ +private AnnotationWriter ianns; + +/** + * The non standard attributes of this class. + */ +private Attribute attrs; + +/** + * The number of entries in the InnerClasses attribute. + */ +private int innerClassesCount; + +/** + * The InnerClasses attribute. + */ +private ByteVector innerClasses; + +/** + * The fields of this class. These fields are stored in a linked list of + * {@link FieldWriter} objects, linked to each other by their + * {@link FieldWriter#next} field. This field stores the first element of + * this list. + */ +FieldWriter firstField; + +/** + * The fields of this class. These fields are stored in a linked list of + * {@link FieldWriter} objects, linked to each other by their + * {@link FieldWriter#next} field. This field stores the last element of + * this list. + */ +FieldWriter lastField; + +/** + * The methods of this class. These methods are stored in a linked list of + * {@link MethodWriter} objects, linked to each other by their + * {@link MethodWriter#next} field. This field stores the first element of + * this list. + */ +MethodWriter firstMethod; + +/** + * The methods of this class. These methods are stored in a linked list of + * {@link MethodWriter} objects, linked to each other by their + * {@link MethodWriter#next} field. This field stores the last element of + * this list. + */ +MethodWriter lastMethod; + +/** + * true if the maximum stack size and number of local variables + * must be automatically computed. + */ +private boolean computeMaxs; + +/** + * true if the stack map frames must be recomputed from scratch. + */ +private boolean computeFrames; + +/** + * true if the stack map tables of this class are invalid. The + * {@link MethodWriter#resizeInstructions} method cannot transform existing + * stack map tables, and so produces potentially invalid classes when it is + * executed. In this case the class is reread and rewritten with the + * {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize + * stack map tables when this option is used). + */ +boolean invalidFrames; + +// ------------------------------------------------------------------------ +// Static initializer +// ------------------------------------------------------------------------ + +/** + * Computes the instruction types of JVM opcodes. + */ +static + { + int i; + byte[] b = new byte[220]; + String s = "AAAAAAAAAAAAAAAABCKLLDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD" + + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAIIIIIIIIIIIIIIIIDNOAA" + + "AAAAGGGGGGGHAFBFAAFFAAQPIIJJIIIIIIIIIIIIIIIIII"; + for(i = 0; i < b.length; ++i) + { + b[i] = (byte) (s.charAt(i) - 'A'); + } + TYPE = b; + + // code to generate the above string + // + // // SBYTE_INSN instructions + // b[Constants.NEWARRAY] = SBYTE_INSN; + // b[Constants.BIPUSH] = SBYTE_INSN; + // + // // SHORT_INSN instructions + // b[Constants.SIPUSH] = SHORT_INSN; + // + // // (IMPL)VAR_INSN instructions + // b[Constants.RET] = VAR_INSN; + // for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) { + // b[i] = VAR_INSN; + // } + // for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) { + // b[i] = VAR_INSN; + // } + // for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3 + // b[i] = IMPLVAR_INSN; + // } + // for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3 + // b[i] = IMPLVAR_INSN; + // } + // + // // TYPE_INSN instructions + // b[Constants.NEW] = TYPE_INSN; + // b[Constants.ANEWARRAY] = TYPE_INSN; + // b[Constants.CHECKCAST] = TYPE_INSN; + // b[Constants.INSTANCEOF] = TYPE_INSN; + // + // // (Set)FIELDORMETH_INSN instructions + // for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) { + // b[i] = FIELDORMETH_INSN; + // } + // b[Constants.INVOKEINTERFACE] = ITFMETH_INSN; + // + // // LABEL(W)_INSN instructions + // for (i = Constants.IFEQ; i <= Constants.JSR; ++i) { + // b[i] = LABEL_INSN; + // } + // b[Constants.IFNULL] = LABEL_INSN; + // b[Constants.IFNONNULL] = LABEL_INSN; + // b[200] = LABELW_INSN; // GOTO_W + // b[201] = LABELW_INSN; // JSR_W + // // temporary opcodes used internally by ASM - see Label and + // MethodWriter + // for (i = 202; i < 220; ++i) { + // b[i] = LABEL_INSN; + // } + // + // // LDC(_W) instructions + // b[Constants.LDC] = LDC_INSN; + // b[19] = LDCW_INSN; // LDC_W + // b[20] = LDCW_INSN; // LDC2_W + // + // // special instructions + // b[Constants.IINC] = IINC_INSN; + // b[Constants.TABLESWITCH] = TABL_INSN; + // b[Constants.LOOKUPSWITCH] = LOOK_INSN; + // b[Constants.MULTIANEWARRAY] = MANA_INSN; + // b[196] = WIDE_INSN; // WIDE + // + // for (i = 0; i < b.length; ++i) { + // System.err.print((char)('A' + b[i])); + // } + // System.err.println(); + } + +// ------------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------------ + +/** + * Constructs a new {@link ClassWriter} object. + * + * @param flags option flags that can be used to modify the default behavior + * of this class. See {@link #COMPUTE_MAXS}, {@link #COMPUTE_FRAMES}. + */ +public ClassWriter(final int flags){ + index = 1; + pool = new ByteVector(); + items = new Item[256]; + threshold = (int) (0.75d * items.length); + key = new Item(); + key2 = new Item(); + key3 = new Item(); + this.computeMaxs = (flags & COMPUTE_MAXS) != 0; + this.computeFrames = (flags & COMPUTE_FRAMES) != 0; +} + +/** + * Constructs a new {@link ClassWriter} object and enables optimizations for + * "mostly add" bytecode transformations. These optimizations are the + * following: + *

+ *

  • The constant pool from the original class is copied as is in + * the new class, which saves time. New constant pool entries will be added + * at the end if necessary, but unused constant pool entries won't be + * removed.
  • Methods that are not transformed are copied as + * is in the new class, directly from the original class bytecode (i.e. + * without emitting visit events for all the method instructions), which + * saves a lot of time. Untransformed methods are detected by the + * fact that the {@link ClassReader} receives {@link MethodVisitor} objects + * that come from a {@link ClassWriter} (and not from a custom + * {@link ClassAdapter} or any other {@link ClassVisitor} instance).
  • + *
+ * + * @param classReader the {@link ClassReader} used to read the original + * class. It will be used to copy the entire constant pool from the + * original class and also to copy other fragments of original + * bytecode where applicable. + * @param flags option flags that can be used to modify the default behavior + * of this class. See {@link #COMPUTE_MAXS}, {@link #COMPUTE_FRAMES}. + */ +public ClassWriter(final ClassReader classReader, final int flags){ + this(flags); + classReader.copyPool(this); + this.cr = classReader; +} + +// ------------------------------------------------------------------------ +// Implementation of the ClassVisitor interface +// ------------------------------------------------------------------------ + +public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces){ + this.version = version; + this.access = access; + this.name = newClass(name); + thisName = name; + if(signature != null) + { + this.signature = newUTF8(signature); + } + this.superName = superName == null ? 0 : newClass(superName); + if(interfaces != null && interfaces.length > 0) + { + interfaceCount = interfaces.length; + this.interfaces = new int[interfaceCount]; + for(int i = 0; i < interfaceCount; ++i) + { + this.interfaces[i] = newClass(interfaces[i]); + } + } +} + +public void visitSource(final String file, final String debug){ + if(file != null) + { + sourceFile = newUTF8(file); + } + if(debug != null) + { + sourceDebug = new ByteVector().putUTF8(debug); + } +} + +public void visitOuterClass( + final String owner, + final String name, + final String desc){ + enclosingMethodOwner = newClass(owner); + if(name != null && desc != null) + { + enclosingMethod = newNameType(name, desc); + } +} + +public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible){ + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2); + if(visible) + { + aw.next = anns; + anns = aw; + } + else + { + aw.next = ianns; + ianns = aw; + } + return aw; +} + +public void visitAttribute(final Attribute attr){ + attr.next = attrs; + attrs = attr; +} + +public void visitInnerClass( + final String name, + final String outerName, + final String innerName, + final int access){ + if(innerClasses == null) + { + innerClasses = new ByteVector(); + } + ++innerClassesCount; + innerClasses.putShort(name == null ? 0 : newClass(name)); + innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); + innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); + innerClasses.putShort(access); +} + +public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value){ + return new FieldWriter(this, access, name, desc, signature, value); +} + +public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions){ + return new MethodWriter(this, + access, + name, + desc, + signature, + exceptions, + computeMaxs, + computeFrames); +} + +public void visitEnd(){ +} + +// ------------------------------------------------------------------------ +// Other public methods +// ------------------------------------------------------------------------ + +/** + * Returns the bytecode of the class that was build with this class writer. + * + * @return the bytecode of the class that was build with this class writer. + */ +public byte[] toByteArray(){ + // computes the real size of the bytecode of this class + int size = 24 + 2 * interfaceCount; + int nbFields = 0; + FieldWriter fb = firstField; + while(fb != null) + { + ++nbFields; + size += fb.getSize(); + fb = fb.next; + } + int nbMethods = 0; + MethodWriter mb = firstMethod; + while(mb != null) + { + ++nbMethods; + size += mb.getSize(); + mb = mb.next; + } + int attributeCount = 0; + if(signature != 0) + { + ++attributeCount; + size += 8; + newUTF8("Signature"); + } + if(sourceFile != 0) + { + ++attributeCount; + size += 8; + newUTF8("SourceFile"); + } + if(sourceDebug != null) + { + ++attributeCount; + size += sourceDebug.length + 4; + newUTF8("SourceDebugExtension"); + } + if(enclosingMethodOwner != 0) + { + ++attributeCount; + size += 10; + newUTF8("EnclosingMethod"); + } + if((access & Opcodes.ACC_DEPRECATED) != 0) + { + ++attributeCount; + size += 6; + newUTF8("Deprecated"); + } + if((access & Opcodes.ACC_SYNTHETIC) != 0 + && (version & 0xffff) < Opcodes.V1_5) + { + ++attributeCount; + size += 6; + newUTF8("Synthetic"); + } + if(innerClasses != null) + { + ++attributeCount; + size += 8 + innerClasses.length; + newUTF8("InnerClasses"); + } + if(anns != null) + { + ++attributeCount; + size += 8 + anns.getSize(); + newUTF8("RuntimeVisibleAnnotations"); + } + if(ianns != null) + { + ++attributeCount; + size += 8 + ianns.getSize(); + newUTF8("RuntimeInvisibleAnnotations"); + } + if(attrs != null) + { + attributeCount += attrs.getCount(); + size += attrs.getSize(this, null, 0, -1, -1); + } + size += pool.length; + // allocates a byte vector of this size, in order to avoid unnecessary + // arraycopy operations in the ByteVector.enlarge() method + ByteVector out = new ByteVector(size); + out.putInt(0xCAFEBABE).putInt(version); + out.putShort(index).putByteArray(pool.data, 0, pool.length); + out.putShort(access).putShort(name).putShort(superName); + out.putShort(interfaceCount); + for(int i = 0; i < interfaceCount; ++i) + { + out.putShort(interfaces[i]); + } + out.putShort(nbFields); + fb = firstField; + while(fb != null) + { + fb.put(out); + fb = fb.next; + } + out.putShort(nbMethods); + mb = firstMethod; + while(mb != null) + { + mb.put(out); + mb = mb.next; + } + out.putShort(attributeCount); + if(signature != 0) + { + out.putShort(newUTF8("Signature")).putInt(2).putShort(signature); + } + if(sourceFile != 0) + { + out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); + } + if(sourceDebug != null) + { + int len = sourceDebug.length - 2; + out.putShort(newUTF8("SourceDebugExtension")).putInt(len); + out.putByteArray(sourceDebug.data, 2, len); + } + if(enclosingMethodOwner != 0) + { + out.putShort(newUTF8("EnclosingMethod")).putInt(4); + out.putShort(enclosingMethodOwner).putShort(enclosingMethod); + } + if((access & Opcodes.ACC_DEPRECATED) != 0) + { + out.putShort(newUTF8("Deprecated")).putInt(0); + } + if((access & Opcodes.ACC_SYNTHETIC) != 0 + && (version & 0xffff) < Opcodes.V1_5) + { + out.putShort(newUTF8("Synthetic")).putInt(0); + } + if(innerClasses != null) + { + out.putShort(newUTF8("InnerClasses")); + out.putInt(innerClasses.length + 2).putShort(innerClassesCount); + out.putByteArray(innerClasses.data, 0, innerClasses.length); + } + if(anns != null) + { + out.putShort(newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if(ianns != null) + { + out.putShort(newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + if(attrs != null) + { + attrs.put(this, null, 0, -1, -1, out); + } + if(invalidFrames) + { + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + new ClassReader(out.data).accept(cw, ClassReader.SKIP_FRAMES); + return cw.toByteArray(); + } + return out.data; +} + +// ------------------------------------------------------------------------ +// Utility methods: constant pool management +// ------------------------------------------------------------------------ + +/** + * Adds a number or string constant to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * + * @param cst the value of the constant to be added to the constant pool. + * This parameter must be an {@link Integer}, a {@link Float}, a + * {@link Long}, a {@link Double}, a {@link String} or a + * {@link Type}. + * @return a new or already existing constant item with the given value. + */ +Item newConstItem(final Object cst){ + if(cst instanceof Integer) + { + int val = ((Integer) cst).intValue(); + return newInteger(val); + } + else if(cst instanceof Byte) + { + int val = ((Byte) cst).intValue(); + return newInteger(val); + } + else if(cst instanceof Character) + { + int val = ((Character) cst).charValue(); + return newInteger(val); + } + else if(cst instanceof Short) + { + int val = ((Short) cst).intValue(); + return newInteger(val); + } + else if(cst instanceof Boolean) + { + int val = ((Boolean) cst).booleanValue() ? 1 : 0; + return newInteger(val); + } + else if(cst instanceof Float) + { + float val = ((Float) cst).floatValue(); + return newFloat(val); + } + else if(cst instanceof Long) + { + long val = ((Long) cst).longValue(); + return newLong(val); + } + else if(cst instanceof Double) + { + double val = ((Double) cst).doubleValue(); + return newDouble(val); + } + else if(cst instanceof String) + { + return newString((String) cst); + } + else if(cst instanceof Type) + { + Type t = (Type) cst; + return newClassItem(t.getSort() == Type.OBJECT + ? t.getInternalName() + : t.getDescriptor()); + } + else + { + throw new IllegalArgumentException("value " + cst); + } +} + +/** + * Adds a number or string constant to the constant pool of the class being + * build. Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param cst the value of the constant to be added to the constant pool. + * This parameter must be an {@link Integer}, a {@link Float}, a + * {@link Long}, a {@link Double} or a {@link String}. + * @return the index of a new or already existing constant item with the + * given value. + */ +public int newConst(final Object cst){ + return newConstItem(cst).index; +} + +/** + * Adds an UTF8 string to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param value the String value. + * @return the index of a new or already existing UTF8 item. + */ +public int newUTF8(final String value){ + key.set(UTF8, value, null, null); + Item result = get(key); + if(result == null) + { + pool.putByte(UTF8).putUTF8(value); + result = new Item(index++, key); + put(result); + } + return result.index; +} + +/** + * Adds a class reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param value the internal name of the class. + * @return a new or already existing class reference item. + */ +Item newClassItem(final String value){ + key2.set(CLASS, value, null, null); + Item result = get(key2); + if(result == null) + { + pool.put12(CLASS, newUTF8(value)); + result = new Item(index++, key2); + put(result); + } + return result; +} + +/** + * Adds a class reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param value the internal name of the class. + * @return the index of a new or already existing class reference item. + */ +public int newClass(final String value){ + return newClassItem(value).index; +} + +/** + * Adds a field reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * + * @param owner the internal name of the field's owner class. + * @param name the field's name. + * @param desc the field's descriptor. + * @return a new or already existing field reference item. + */ +Item newFieldItem(final String owner, final String name, final String desc){ + key3.set(FIELD, owner, name, desc); + Item result = get(key3); + if(result == null) + { + put122(FIELD, newClass(owner), newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result; +} + +/** + * Adds a field reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param owner the internal name of the field's owner class. + * @param name the field's name. + * @param desc the field's descriptor. + * @return the index of a new or already existing field reference item. + */ +public int newField(final String owner, final String name, final String desc){ + return newFieldItem(owner, name, desc).index; +} + +/** + * Adds a method reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * + * @param owner the internal name of the method's owner class. + * @param name the method's name. + * @param desc the method's descriptor. + * @param itf true if owner is an interface. + * @return a new or already existing method reference item. + */ +Item newMethodItem( + final String owner, + final String name, + final String desc, + final boolean itf){ + int type = itf ? IMETH : METH; + key3.set(type, owner, name, desc); + Item result = get(key3); + if(result == null) + { + put122(type, newClass(owner), newNameType(name, desc)); + result = new Item(index++, key3); + put(result); + } + return result; +} + +/** + * Adds a method reference to the constant pool of the class being build. + * Does nothing if the constant pool already contains a similar item. + * This method is intended for {@link Attribute} sub classes, and is + * normally not needed by class generators or adapters. + * + * @param owner the internal name of the method's owner class. + * @param name the method's name. + * @param desc the method's descriptor. + * @param itf true if owner is an interface. + * @return the index of a new or already existing method reference item. + */ +public int newMethod( + final String owner, + final String name, + final String desc, + final boolean itf){ + return newMethodItem(owner, name, desc, itf).index; +} + +/** + * Adds an integer to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. + * + * @param value the int value. + * @return a new or already existing int item. + */ +Item newInteger(final int value){ + key.set(value); + Item result = get(key); + if(result == null) + { + pool.putByte(INT).putInt(value); + result = new Item(index++, key); + put(result); + } + return result; +} + +/** + * Adds a float to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value the float value. + * @return a new or already existing float item. + */ +Item newFloat(final float value){ + key.set(value); + Item result = get(key); + if(result == null) + { + pool.putByte(FLOAT).putInt(key.intVal); + result = new Item(index++, key); + put(result); + } + return result; +} + +/** + * Adds a long to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value the long value. + * @return a new or already existing long item. + */ +Item newLong(final long value){ + key.set(value); + Item result = get(key); + if(result == null) + { + pool.putByte(LONG).putLong(value); + result = new Item(index, key); + put(result); + index += 2; + } + return result; +} + +/** + * Adds a double to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value the double value. + * @return a new or already existing double item. + */ +Item newDouble(final double value){ + key.set(value); + Item result = get(key); + if(result == null) + { + pool.putByte(DOUBLE).putLong(key.longVal); + result = new Item(index, key); + put(result); + index += 2; + } + return result; +} + +/** + * Adds a string to the constant pool of the class being build. Does nothing + * if the constant pool already contains a similar item. + * + * @param value the String value. + * @return a new or already existing string item. + */ +private Item newString(final String value){ + key2.set(STR, value, null, null); + Item result = get(key2); + if(result == null) + { + pool.put12(STR, newUTF8(value)); + result = new Item(index++, key2); + put(result); + } + return result; +} + +/** + * Adds a name and type to the constant pool of the class being build. Does + * nothing if the constant pool already contains a similar item. This + * method is intended for {@link Attribute} sub classes, and is normally not + * needed by class generators or adapters. + * + * @param name a name. + * @param desc a type descriptor. + * @return the index of a new or already existing name and type item. + */ +public int newNameType(final String name, final String desc){ + key2.set(NAME_TYPE, name, desc, null); + Item result = get(key2); + if(result == null) + { + put122(NAME_TYPE, newUTF8(name), newUTF8(desc)); + result = new Item(index++, key2); + put(result); + } + return result.index; +} + +/** + * Adds the given internal name to {@link #typeTable} and returns its index. + * Does nothing if the type table already contains this internal name. + * + * @param type the internal name to be added to the type table. + * @return the index of this internal name in the type table. + */ +int addType(final String type){ + key.set(TYPE_NORMAL, type, null, null); + Item result = get(key); + if(result == null) + { + result = addType(key); + } + return result.index; +} + +/** + * Adds the given "uninitialized" type to {@link #typeTable} and returns its + * index. This method is used for UNINITIALIZED types, made of an internal + * name and a bytecode offset. + * + * @param type the internal name to be added to the type table. + * @param offset the bytecode offset of the NEW instruction that created + * this UNINITIALIZED type value. + * @return the index of this internal name in the type table. + */ +int addUninitializedType(final String type, final int offset){ + key.type = TYPE_UNINIT; + key.intVal = offset; + key.strVal1 = type; + key.hashCode = 0x7FFFFFFF & (TYPE_UNINIT + type.hashCode() + offset); + Item result = get(key); + if(result == null) + { + result = addType(key); + } + return result.index; +} + +/** + * Adds the given Item to {@link #typeTable}. + * + * @param item the value to be added to the type table. + * @return the added Item, which a new Item instance with the same value as + * the given Item. + */ +private Item addType(final Item item){ + ++typeCount; + Item result = new Item(typeCount, key); + put(result); + if(typeTable == null) + { + typeTable = new Item[16]; + } + if(typeCount == typeTable.length) + { + Item[] newTable = new Item[2 * typeTable.length]; + System.arraycopy(typeTable, 0, newTable, 0, typeTable.length); + typeTable = newTable; + } + typeTable[typeCount] = result; + return result; +} + +/** + * Returns the index of the common super type of the two given types. This + * method calls {@link #getCommonSuperClass} and caches the result in the + * {@link #items} hash table to speedup future calls with the same + * parameters. + * + * @param type1 index of an internal name in {@link #typeTable}. + * @param type2 index of an internal name in {@link #typeTable}. + * @return the index of the common super type of the two given types. + */ +int getMergedType(final int type1, final int type2){ + key2.type = TYPE_MERGED; + key2.longVal = type1 | (((long) type2) << 32); + key2.hashCode = 0x7FFFFFFF & (TYPE_MERGED + type1 + type2); + Item result = get(key2); + if(result == null) + { + String t = typeTable[type1].strVal1; + String u = typeTable[type2].strVal1; + key2.intVal = addType(getCommonSuperClass(t, u)); + result = new Item((short) 0, key2); + put(result); + } + return result.intVal; +} + +/** + * Returns the common super type of the two given types. The default + * implementation of this method loads the two given classes and uses + * the java.lang.Class methods to find the common super class. It can be + * overriden to compute this common super type in other ways, in particular + * without actually loading any class, or to take into account the class + * that is currently being generated by this ClassWriter, which can of + * course not be loaded since it is under construction. + * + * @param type1 the internal name of a class. + * @param type2 the internal name of another class. + * @return the internal name of the common super class of the two given + * classes. + */ +protected String getCommonSuperClass(final String type1, final String type2){ + Class c, d; + try + { + c = Class.forName(type1.replace('/', '.')); + d = Class.forName(type2.replace('/', '.')); + } + catch(ClassNotFoundException e) + { + throw new RuntimeException(e); + } + if(c.isAssignableFrom(d)) + { + return type1; + } + if(d.isAssignableFrom(c)) + { + return type2; + } + if(c.isInterface() || d.isInterface()) + { + return "java/lang/Object"; + } + else + { + do + { + c = c.getSuperclass(); + } while(!c.isAssignableFrom(d)); + return c.getName().replace('.', '/'); + } +} + +/** + * Returns the constant pool's hash table item which is equal to the given + * item. + * + * @param key a constant pool item. + * @return the constant pool's hash table item which is equal to the given + * item, or null if there is no such item. + */ +private Item get(final Item key){ + Item i = items[key.hashCode % items.length]; + while(i != null && !key.isEqualTo(i)) + { + i = i.next; + } + return i; +} + +/** + * Puts the given item in the constant pool's hash table. The hash table + * must not already contains this item. + * + * @param i the item to be added to the constant pool's hash table. + */ +private void put(final Item i){ + if(index > threshold) + { + int ll = items.length; + int nl = ll * 2 + 1; + Item[] newItems = new Item[nl]; + for(int l = ll - 1; l >= 0; --l) + { + Item j = items[l]; + while(j != null) + { + int index = j.hashCode % newItems.length; + Item k = j.next; + j.next = newItems[index]; + newItems[index] = j; + j = k; + } + } + items = newItems; + threshold = (int) (nl * 0.75); + } + int index = i.hashCode % items.length; + i.next = items[index]; + items[index] = i; +} + +/** + * Puts one byte and two shorts into the constant pool. + * + * @param b a byte. + * @param s1 a short. + * @param s2 another short. + */ +private void put122(final int b, final int s1, final int s2){ + pool.put12(b, s1).putShort(s2); +} +} diff --git a/src/jvm/clojure/asm/Edge.java b/src/jvm/clojure/asm/Edge.java index f549cfb..2974051 100644 --- a/src/jvm/clojure/asm/Edge.java +++ b/src/jvm/clojure/asm/Edge.java @@ -1,75 +1,75 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * An edge in the control flow graph of a method body. See {@link Label Label}. - * - * @author Eric Bruneton - */ -class Edge{ - -/** - * Denotes a normal control flow graph edge. - */ -final static int NORMAL = 0; - -/** - * Denotes a control flow graph edge corresponding to an exception handler. - * More precisely any {@link Edge} whose {@link #info} is strictly positive - * corresponds to an exception handler. The actual value of {@link #info} is - * the index, in the {@link ClassWriter} type table, of the exception that - * is catched. - */ -final static int EXCEPTION = 0x7FFFFFFF; - -/** - * Information about this control flow graph edge. If - * {@link ClassWriter#COMPUTE_MAXS} is used this field is the (relative) - * stack size in the basic block from which this edge originates. This size - * is equal to the stack size at the "jump" instruction to which this edge - * corresponds, relatively to the stack size at the beginning of the - * originating basic block. If {@link ClassWriter#COMPUTE_FRAMES} is used, - * this field is the kind of this control flow graph edge (i.e. NORMAL or - * EXCEPTION). - */ -int info; - -/** - * The successor block of the basic block from which this edge originates. - */ -Label successor; - -/** - * The next edge in the list of successors of the originating basic block. - * See {@link Label#successors successors}. - */ -Edge next; -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * An edge in the control flow graph of a method body. See {@link Label Label}. + * + * @author Eric Bruneton + */ +class Edge{ + +/** + * Denotes a normal control flow graph edge. + */ +final static int NORMAL = 0; + +/** + * Denotes a control flow graph edge corresponding to an exception handler. + * More precisely any {@link Edge} whose {@link #info} is strictly positive + * corresponds to an exception handler. The actual value of {@link #info} is + * the index, in the {@link ClassWriter} type table, of the exception that + * is catched. + */ +final static int EXCEPTION = 0x7FFFFFFF; + +/** + * Information about this control flow graph edge. If + * {@link ClassWriter#COMPUTE_MAXS} is used this field is the (relative) + * stack size in the basic block from which this edge originates. This size + * is equal to the stack size at the "jump" instruction to which this edge + * corresponds, relatively to the stack size at the beginning of the + * originating basic block. If {@link ClassWriter#COMPUTE_FRAMES} is used, + * this field is the kind of this control flow graph edge (i.e. NORMAL or + * EXCEPTION). + */ +int info; + +/** + * The successor block of the basic block from which this edge originates. + */ +Label successor; + +/** + * The next edge in the list of successors of the originating basic block. + * See {@link Label#successors successors}. + */ +Edge next; +} diff --git a/src/jvm/clojure/asm/FieldVisitor.java b/src/jvm/clojure/asm/FieldVisitor.java index 649fd6a..faf66f2 100644 --- a/src/jvm/clojure/asm/FieldVisitor.java +++ b/src/jvm/clojure/asm/FieldVisitor.java @@ -1,64 +1,64 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * A visitor to visit a Java field. The methods of this interface must be called - * in the following order: ( visitAnnotation | - * visitAttribute )* visitEnd. - * - * @author Eric Bruneton - */ -public interface FieldVisitor{ - -/** - * Visits an annotation of the field. - * - * @param desc the class descriptor of the annotation class. - * @param visible true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ -AnnotationVisitor visitAnnotation(String desc, boolean visible); - -/** - * Visits a non standard attribute of the field. - * - * @param attr an attribute. - */ -void visitAttribute(Attribute attr); - -/** - * Visits the end of the field. This method, which is the last one to be - * called, is used to inform the visitor that all the annotations and - * attributes of the field have been visited. - */ -void visitEnd(); -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * A visitor to visit a Java field. The methods of this interface must be called + * in the following order: ( visitAnnotation | + * visitAttribute )* visitEnd. + * + * @author Eric Bruneton + */ +public interface FieldVisitor{ + +/** + * Visits an annotation of the field. + * + * @param desc the class descriptor of the annotation class. + * @param visible true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ +AnnotationVisitor visitAnnotation(String desc, boolean visible); + +/** + * Visits a non standard attribute of the field. + * + * @param attr an attribute. + */ +void visitAttribute(Attribute attr); + +/** + * Visits the end of the field. This method, which is the last one to be + * called, is used to inform the visitor that all the annotations and + * attributes of the field have been visited. + */ +void visitEnd(); +} diff --git a/src/jvm/clojure/asm/FieldWriter.java b/src/jvm/clojure/asm/FieldWriter.java index 6ac63f7..bafef78 100644 --- a/src/jvm/clojure/asm/FieldWriter.java +++ b/src/jvm/clojure/asm/FieldWriter.java @@ -1,290 +1,290 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * An {@link FieldVisitor} that generates Java fields in bytecode form. - * - * @author Eric Bruneton - */ -final class FieldWriter implements FieldVisitor{ - -/** - * Next field writer (see {@link ClassWriter#firstField firstField}). - */ -FieldWriter next; - -/** - * The class writer to which this field must be added. - */ -private ClassWriter cw; - -/** - * Access flags of this field. - */ -private int access; - -/** - * The index of the constant pool item that contains the name of this - * method. - */ -private int name; - -/** - * The index of the constant pool item that contains the descriptor of this - * field. - */ -private int desc; - -/** - * The index of the constant pool item that contains the signature of this - * field. - */ -private int signature; - -/** - * The index of the constant pool item that contains the constant value of - * this field. - */ -private int value; - -/** - * The runtime visible annotations of this field. May be null. - */ -private AnnotationWriter anns; - -/** - * The runtime invisible annotations of this field. May be null. - */ -private AnnotationWriter ianns; - -/** - * The non standard attributes of this field. May be null. - */ -private Attribute attrs; - -// ------------------------------------------------------------------------ -// Constructor -// ------------------------------------------------------------------------ - -/** - * Constructs a new {@link FieldWriter}. - * - * @param cw the class writer to which this field must be added. - * @param access the field's access flags (see {@link Opcodes}). - * @param name the field's name. - * @param desc the field's descriptor (see {@link Type}). - * @param signature the field's signature. May be null. - * @param value the field's constant value. May be null. - */ -protected FieldWriter( - final ClassWriter cw, - final int access, - final String name, - final String desc, - final String signature, - final Object value){ - if(cw.firstField == null) - { - cw.firstField = this; - } - else - { - cw.lastField.next = this; - } - cw.lastField = this; - this.cw = cw; - this.access = access; - this.name = cw.newUTF8(name); - this.desc = cw.newUTF8(desc); - if(signature != null) - { - this.signature = cw.newUTF8(signature); - } - if(value != null) - { - this.value = cw.newConstItem(value).index; - } -} - -// ------------------------------------------------------------------------ -// Implementation of the FieldVisitor interface -// ------------------------------------------------------------------------ - -public AnnotationVisitor visitAnnotation( - final String desc, - final boolean visible){ - ByteVector bv = new ByteVector(); - // write type, and reserve space for values count - bv.putShort(cw.newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); - if(visible) - { - aw.next = anns; - anns = aw; - } - else - { - aw.next = ianns; - ianns = aw; - } - return aw; -} - -public void visitAttribute(final Attribute attr){ - attr.next = attrs; - attrs = attr; -} - -public void visitEnd(){ -} - -// ------------------------------------------------------------------------ -// Utility methods -// ------------------------------------------------------------------------ - -/** - * Returns the size of this field. - * - * @return the size of this field. - */ -int getSize(){ - int size = 8; - if(value != 0) - { - cw.newUTF8("ConstantValue"); - size += 8; - } - if((access & Opcodes.ACC_SYNTHETIC) != 0 - && (cw.version & 0xffff) < Opcodes.V1_5) - { - cw.newUTF8("Synthetic"); - size += 6; - } - if((access & Opcodes.ACC_DEPRECATED) != 0) - { - cw.newUTF8("Deprecated"); - size += 6; - } - if(signature != 0) - { - cw.newUTF8("Signature"); - size += 8; - } - if(anns != null) - { - cw.newUTF8("RuntimeVisibleAnnotations"); - size += 8 + anns.getSize(); - } - if(ianns != null) - { - cw.newUTF8("RuntimeInvisibleAnnotations"); - size += 8 + ianns.getSize(); - } - if(attrs != null) - { - size += attrs.getSize(cw, null, 0, -1, -1); - } - return size; -} - -/** - * Puts the content of this field into the given byte vector. - * - * @param out where the content of this field must be put. - */ -void put(final ByteVector out){ - out.putShort(access).putShort(name).putShort(desc); - int attributeCount = 0; - if(value != 0) - { - ++attributeCount; - } - if((access & Opcodes.ACC_SYNTHETIC) != 0 - && (cw.version & 0xffff) < Opcodes.V1_5) - { - ++attributeCount; - } - if((access & Opcodes.ACC_DEPRECATED) != 0) - { - ++attributeCount; - } - if(signature != 0) - { - ++attributeCount; - } - if(anns != null) - { - ++attributeCount; - } - if(ianns != null) - { - ++attributeCount; - } - if(attrs != null) - { - attributeCount += attrs.getCount(); - } - out.putShort(attributeCount); - if(value != 0) - { - out.putShort(cw.newUTF8("ConstantValue")); - out.putInt(2).putShort(value); - } - if((access & Opcodes.ACC_SYNTHETIC) != 0 - && (cw.version & 0xffff) < Opcodes.V1_5) - { - out.putShort(cw.newUTF8("Synthetic")).putInt(0); - } - if((access & Opcodes.ACC_DEPRECATED) != 0) - { - out.putShort(cw.newUTF8("Deprecated")).putInt(0); - } - if(signature != 0) - { - out.putShort(cw.newUTF8("Signature")); - out.putInt(2).putShort(signature); - } - if(anns != null) - { - out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); - anns.put(out); - } - if(ianns != null) - { - out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); - ianns.put(out); - } - if(attrs != null) - { - attrs.put(cw, null, 0, -1, -1, out); - } -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * An {@link FieldVisitor} that generates Java fields in bytecode form. + * + * @author Eric Bruneton + */ +final class FieldWriter implements FieldVisitor{ + +/** + * Next field writer (see {@link ClassWriter#firstField firstField}). + */ +FieldWriter next; + +/** + * The class writer to which this field must be added. + */ +private ClassWriter cw; + +/** + * Access flags of this field. + */ +private int access; + +/** + * The index of the constant pool item that contains the name of this + * method. + */ +private int name; + +/** + * The index of the constant pool item that contains the descriptor of this + * field. + */ +private int desc; + +/** + * The index of the constant pool item that contains the signature of this + * field. + */ +private int signature; + +/** + * The index of the constant pool item that contains the constant value of + * this field. + */ +private int value; + +/** + * The runtime visible annotations of this field. May be null. + */ +private AnnotationWriter anns; + +/** + * The runtime invisible annotations of this field. May be null. + */ +private AnnotationWriter ianns; + +/** + * The non standard attributes of this field. May be null. + */ +private Attribute attrs; + +// ------------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------------ + +/** + * Constructs a new {@link FieldWriter}. + * + * @param cw the class writer to which this field must be added. + * @param access the field's access flags (see {@link Opcodes}). + * @param name the field's name. + * @param desc the field's descriptor (see {@link Type}). + * @param signature the field's signature. May be null. + * @param value the field's constant value. May be null. + */ +protected FieldWriter( + final ClassWriter cw, + final int access, + final String name, + final String desc, + final String signature, + final Object value){ + if(cw.firstField == null) + { + cw.firstField = this; + } + else + { + cw.lastField.next = this; + } + cw.lastField = this; + this.cw = cw; + this.access = access; + this.name = cw.newUTF8(name); + this.desc = cw.newUTF8(desc); + if(signature != null) + { + this.signature = cw.newUTF8(signature); + } + if(value != null) + { + this.value = cw.newConstItem(value).index; + } +} + +// ------------------------------------------------------------------------ +// Implementation of the FieldVisitor interface +// ------------------------------------------------------------------------ + +public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible){ + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if(visible) + { + aw.next = anns; + anns = aw; + } + else + { + aw.next = ianns; + ianns = aw; + } + return aw; +} + +public void visitAttribute(final Attribute attr){ + attr.next = attrs; + attrs = attr; +} + +public void visitEnd(){ +} + +// ------------------------------------------------------------------------ +// Utility methods +// ------------------------------------------------------------------------ + +/** + * Returns the size of this field. + * + * @return the size of this field. + */ +int getSize(){ + int size = 8; + if(value != 0) + { + cw.newUTF8("ConstantValue"); + size += 8; + } + if((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + cw.newUTF8("Synthetic"); + size += 6; + } + if((access & Opcodes.ACC_DEPRECATED) != 0) + { + cw.newUTF8("Deprecated"); + size += 6; + } + if(signature != 0) + { + cw.newUTF8("Signature"); + size += 8; + } + if(anns != null) + { + cw.newUTF8("RuntimeVisibleAnnotations"); + size += 8 + anns.getSize(); + } + if(ianns != null) + { + cw.newUTF8("RuntimeInvisibleAnnotations"); + size += 8 + ianns.getSize(); + } + if(attrs != null) + { + size += attrs.getSize(cw, null, 0, -1, -1); + } + return size; +} + +/** + * Puts the content of this field into the given byte vector. + * + * @param out where the content of this field must be put. + */ +void put(final ByteVector out){ + out.putShort(access).putShort(name).putShort(desc); + int attributeCount = 0; + if(value != 0) + { + ++attributeCount; + } + if((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + ++attributeCount; + } + if((access & Opcodes.ACC_DEPRECATED) != 0) + { + ++attributeCount; + } + if(signature != 0) + { + ++attributeCount; + } + if(anns != null) + { + ++attributeCount; + } + if(ianns != null) + { + ++attributeCount; + } + if(attrs != null) + { + attributeCount += attrs.getCount(); + } + out.putShort(attributeCount); + if(value != 0) + { + out.putShort(cw.newUTF8("ConstantValue")); + out.putInt(2).putShort(value); + } + if((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + out.putShort(cw.newUTF8("Synthetic")).putInt(0); + } + if((access & Opcodes.ACC_DEPRECATED) != 0) + { + out.putShort(cw.newUTF8("Deprecated")).putInt(0); + } + if(signature != 0) + { + out.putShort(cw.newUTF8("Signature")); + out.putInt(2).putShort(signature); + } + if(anns != null) + { + out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if(ianns != null) + { + out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + if(attrs != null) + { + attrs.put(cw, null, 0, -1, -1, out); + } +} +} diff --git a/src/jvm/clojure/asm/Frame.java b/src/jvm/clojure/asm/Frame.java index 810c82b..8bc22e9 100644 --- a/src/jvm/clojure/asm/Frame.java +++ b/src/jvm/clojure/asm/Frame.java @@ -1,1506 +1,1506 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * Information about the input and output stack map frames of a basic block. - * - * @author Eric Bruneton - */ -final class Frame{ - -/* - * Frames are computed in a two steps process: during the visit of each - * instruction, the state of the frame at the end of current basic block is - * updated by simulating the action of the instruction on the previous state - * of this so called "output frame". In visitMaxs, a fix point algorithm is - * used to compute the "input frame" of each basic block, i.e. the stack map - * frame at the begining of the basic block, starting from the input frame - * of the first basic block (which is computed from the method descriptor), - * and by using the previously computed output frames to compute the input - * state of the other blocks. - * - * All output and input frames are stored as arrays of integers. Reference - * and array types are represented by an index into a type table (which is - * not the same as the constant pool of the class, in order to avoid adding - * unnecessary constants in the pool - not all computed frames will end up - * being stored in the stack map table). This allows very fast type - * comparisons. - * - * Output stack map frames are computed relatively to the input frame of the - * basic block, which is not yet known when output frames are computed. It - * is therefore necessary to be able to represent abstract types such as - * "the type at position x in the input frame locals" or "the type at - * position x from the top of the input frame stack" or even "the type at - * position x in the input frame, with y more (or less) array dimensions". - * This explains the rather complicated type format used in output frames. - * - * This format is the following: DIM KIND VALUE (4, 4 and 24 bits). DIM is a - * signed number of array dimensions (from -8 to 7). KIND is either BASE, - * LOCAL or STACK. BASE is used for types that are not relative to the input - * frame. LOCAL is used for types that are relative to the input local - * variable types. STACK is used for types that are relative to the input - * stack types. VALUE depends on KIND. For LOCAL types, it is an index in - * the input local variable types. For STACK types, it is a position - * relatively to the top of input frame stack. For BASE types, it is either - * one of the constants defined in FrameVisitor, or for OBJECT and - * UNINITIALIZED types, a tag and an index in the type table. - * - * Output frames can contain types of any kind and with a positive or - * negative dimension (and even unassigned types, represented by 0 - which - * does not correspond to any valid type value). Input frames can only - * contain BASE types of positive or null dimension. In all cases the type - * table contains only internal type names (array type descriptors are - * forbidden - dimensions must be represented through the DIM field). - * - * The LONG and DOUBLE types are always represented by using two slots (LONG + - * TOP or DOUBLE + TOP), for local variable types as well as in the operand - * stack. This is necessary to be able to simulate DUPx_y instructions, - * whose effect would be dependent on the actual type values if types were - * always represented by a single slot in the stack (and this is not - * possible, since actual type values are not always known - cf LOCAL and - * STACK type kinds). - */ - -/** - * Mask to get the dimension of a frame type. This dimension is a signed - * integer between -8 and 7. - */ -final static int DIM = 0xF0000000; - -/** - * Constant to be added to a type to get a type with one more dimension. - */ -final static int ARRAY_OF = 0x10000000; - -/** - * Constant to be added to a type to get a type with one less dimension. - */ -final static int ELEMENT_OF = 0xF0000000; - -/** - * Mask to get the kind of a frame type. - * - * @see #BASE - * @see #LOCAL - * @see #STACK - */ -final static int KIND = 0xF000000; - -/** - * Mask to get the value of a frame type. - */ -final static int VALUE = 0xFFFFFF; - -/** - * Mask to get the kind of base types. - */ -final static int BASE_KIND = 0xFF00000; - -/** - * Mask to get the value of base types. - */ -final static int BASE_VALUE = 0xFFFFF; - -/** - * Kind of the types that are not relative to an input stack map frame. - */ -final static int BASE = 0x1000000; - -/** - * Base kind of the base reference types. The BASE_VALUE of such types is an - * index into the type table. - */ -final static int OBJECT = BASE | 0x700000; - -/** - * Base kind of the uninitialized base types. The BASE_VALUE of such types - * in an index into the type table (the Item at that index contains both an - * instruction offset and an internal class name). - */ -final static int UNINITIALIZED = BASE | 0x800000; - -/** - * Kind of the types that are relative to the local variable types of an - * input stack map frame. The value of such types is a local variable index. - */ -private final static int LOCAL = 0x2000000; - -/** - * Kind of the the types that are relative to the stack of an input stack - * map frame. The value of such types is a position relatively to the top of - * this stack. - */ -private final static int STACK = 0x3000000; - -/** - * The TOP type. This is a BASE type. - */ -final static int TOP = BASE | 0; - -/** - * The BOOLEAN type. This is a BASE type mainly used for array types. - */ -final static int BOOLEAN = BASE | 9; - -/** - * The BYTE type. This is a BASE type mainly used for array types. - */ -final static int BYTE = BASE | 10; - -/** - * The CHAR type. This is a BASE type mainly used for array types. - */ -final static int CHAR = BASE | 11; - -/** - * The SHORT type. This is a BASE type mainly used for array types. - */ -final static int SHORT = BASE | 12; - -/** - * The INTEGER type. This is a BASE type. - */ -final static int INTEGER = BASE | 1; - -/** - * The FLOAT type. This is a BASE type. - */ -final static int FLOAT = BASE | 2; - -/** - * The DOUBLE type. This is a BASE type. - */ -final static int DOUBLE = BASE | 3; - -/** - * The LONG type. This is a BASE type. - */ -final static int LONG = BASE | 4; - -/** - * The NULL type. This is a BASE type. - */ -final static int NULL = BASE | 5; - -/** - * The UNINITIALIZED_THIS type. This is a BASE type. - */ -final static int UNINITIALIZED_THIS = BASE | 6; - -/** - * The stack size variation corresponding to each JVM instruction. This - * stack variation is equal to the size of the values produced by an - * instruction, minus the size of the values consumed by this instruction. - */ -final static int[] SIZE; - -/** - * Computes the stack size variation corresponding to each JVM instruction. - */ -static - { - int i; - int[] b = new int[202]; - String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD" - + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD" - + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED" - + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; - for(i = 0; i < b.length; ++i) - { - b[i] = s.charAt(i) - 'E'; - } - SIZE = b; - - // code to generate the above string - // - // int NA = 0; // not applicable (unused opcode or variable size opcode) - // - // b = new int[] { - // 0, //NOP, // visitInsn - // 1, //ACONST_NULL, // - - // 1, //ICONST_M1, // - - // 1, //ICONST_0, // - - // 1, //ICONST_1, // - - // 1, //ICONST_2, // - - // 1, //ICONST_3, // - - // 1, //ICONST_4, // - - // 1, //ICONST_5, // - - // 2, //LCONST_0, // - - // 2, //LCONST_1, // - - // 1, //FCONST_0, // - - // 1, //FCONST_1, // - - // 1, //FCONST_2, // - - // 2, //DCONST_0, // - - // 2, //DCONST_1, // - - // 1, //BIPUSH, // visitIntInsn - // 1, //SIPUSH, // - - // 1, //LDC, // visitLdcInsn - // NA, //LDC_W, // - - // NA, //LDC2_W, // - - // 1, //ILOAD, // visitVarInsn - // 2, //LLOAD, // - - // 1, //FLOAD, // - - // 2, //DLOAD, // - - // 1, //ALOAD, // - - // NA, //ILOAD_0, // - - // NA, //ILOAD_1, // - - // NA, //ILOAD_2, // - - // NA, //ILOAD_3, // - - // NA, //LLOAD_0, // - - // NA, //LLOAD_1, // - - // NA, //LLOAD_2, // - - // NA, //LLOAD_3, // - - // NA, //FLOAD_0, // - - // NA, //FLOAD_1, // - - // NA, //FLOAD_2, // - - // NA, //FLOAD_3, // - - // NA, //DLOAD_0, // - - // NA, //DLOAD_1, // - - // NA, //DLOAD_2, // - - // NA, //DLOAD_3, // - - // NA, //ALOAD_0, // - - // NA, //ALOAD_1, // - - // NA, //ALOAD_2, // - - // NA, //ALOAD_3, // - - // -1, //IALOAD, // visitInsn - // 0, //LALOAD, // - - // -1, //FALOAD, // - - // 0, //DALOAD, // - - // -1, //AALOAD, // - - // -1, //BALOAD, // - - // -1, //CALOAD, // - - // -1, //SALOAD, // - - // -1, //ISTORE, // visitVarInsn - // -2, //LSTORE, // - - // -1, //FSTORE, // - - // -2, //DSTORE, // - - // -1, //ASTORE, // - - // NA, //ISTORE_0, // - - // NA, //ISTORE_1, // - - // NA, //ISTORE_2, // - - // NA, //ISTORE_3, // - - // NA, //LSTORE_0, // - - // NA, //LSTORE_1, // - - // NA, //LSTORE_2, // - - // NA, //LSTORE_3, // - - // NA, //FSTORE_0, // - - // NA, //FSTORE_1, // - - // NA, //FSTORE_2, // - - // NA, //FSTORE_3, // - - // NA, //DSTORE_0, // - - // NA, //DSTORE_1, // - - // NA, //DSTORE_2, // - - // NA, //DSTORE_3, // - - // NA, //ASTORE_0, // - - // NA, //ASTORE_1, // - - // NA, //ASTORE_2, // - - // NA, //ASTORE_3, // - - // -3, //IASTORE, // visitInsn - // -4, //LASTORE, // - - // -3, //FASTORE, // - - // -4, //DASTORE, // - - // -3, //AASTORE, // - - // -3, //BASTORE, // - - // -3, //CASTORE, // - - // -3, //SASTORE, // - - // -1, //POP, // - - // -2, //POP2, // - - // 1, //DUP, // - - // 1, //DUP_X1, // - - // 1, //DUP_X2, // - - // 2, //DUP2, // - - // 2, //DUP2_X1, // - - // 2, //DUP2_X2, // - - // 0, //SWAP, // - - // -1, //IADD, // - - // -2, //LADD, // - - // -1, //FADD, // - - // -2, //DADD, // - - // -1, //ISUB, // - - // -2, //LSUB, // - - // -1, //FSUB, // - - // -2, //DSUB, // - - // -1, //IMUL, // - - // -2, //LMUL, // - - // -1, //FMUL, // - - // -2, //DMUL, // - - // -1, //IDIV, // - - // -2, //LDIV, // - - // -1, //FDIV, // - - // -2, //DDIV, // - - // -1, //IREM, // - - // -2, //LREM, // - - // -1, //FREM, // - - // -2, //DREM, // - - // 0, //INEG, // - - // 0, //LNEG, // - - // 0, //FNEG, // - - // 0, //DNEG, // - - // -1, //ISHL, // - - // -1, //LSHL, // - - // -1, //ISHR, // - - // -1, //LSHR, // - - // -1, //IUSHR, // - - // -1, //LUSHR, // - - // -1, //IAND, // - - // -2, //LAND, // - - // -1, //IOR, // - - // -2, //LOR, // - - // -1, //IXOR, // - - // -2, //LXOR, // - - // 0, //IINC, // visitIincInsn - // 1, //I2L, // visitInsn - // 0, //I2F, // - - // 1, //I2D, // - - // -1, //L2I, // - - // -1, //L2F, // - - // 0, //L2D, // - - // 0, //F2I, // - - // 1, //F2L, // - - // 1, //F2D, // - - // -1, //D2I, // - - // 0, //D2L, // - - // -1, //D2F, // - - // 0, //I2B, // - - // 0, //I2C, // - - // 0, //I2S, // - - // -3, //LCMP, // - - // -1, //FCMPL, // - - // -1, //FCMPG, // - - // -3, //DCMPL, // - - // -3, //DCMPG, // - - // -1, //IFEQ, // visitJumpInsn - // -1, //IFNE, // - - // -1, //IFLT, // - - // -1, //IFGE, // - - // -1, //IFGT, // - - // -1, //IFLE, // - - // -2, //IF_ICMPEQ, // - - // -2, //IF_ICMPNE, // - - // -2, //IF_ICMPLT, // - - // -2, //IF_ICMPGE, // - - // -2, //IF_ICMPGT, // - - // -2, //IF_ICMPLE, // - - // -2, //IF_ACMPEQ, // - - // -2, //IF_ACMPNE, // - - // 0, //GOTO, // - - // 1, //JSR, // - - // 0, //RET, // visitVarInsn - // -1, //TABLESWITCH, // visiTableSwitchInsn - // -1, //LOOKUPSWITCH, // visitLookupSwitch - // -1, //IRETURN, // visitInsn - // -2, //LRETURN, // - - // -1, //FRETURN, // - - // -2, //DRETURN, // - - // -1, //ARETURN, // - - // 0, //RETURN, // - - // NA, //GETSTATIC, // visitFieldInsn - // NA, //PUTSTATIC, // - - // NA, //GETFIELD, // - - // NA, //PUTFIELD, // - - // NA, //INVOKEVIRTUAL, // visitMethodInsn - // NA, //INVOKESPECIAL, // - - // NA, //INVOKESTATIC, // - - // NA, //INVOKEINTERFACE, // - - // NA, //UNUSED, // NOT VISITED - // 1, //NEW, // visitTypeInsn - // 0, //NEWARRAY, // visitIntInsn - // 0, //ANEWARRAY, // visitTypeInsn - // 0, //ARRAYLENGTH, // visitInsn - // NA, //ATHROW, // - - // 0, //CHECKCAST, // visitTypeInsn - // 0, //INSTANCEOF, // - - // -1, //MONITORENTER, // visitInsn - // -1, //MONITOREXIT, // - - // NA, //WIDE, // NOT VISITED - // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn - // -1, //IFNULL, // visitJumpInsn - // -1, //IFNONNULL, // - - // NA, //GOTO_W, // - - // NA, //JSR_W, // - - // }; - // for (i = 0; i < b.length; ++i) { - // System.err.print((char)('E' + b[i])); - // } - // System.err.println(); - } - -/** - * The label (i.e. basic block) to which these input and output stack map - * frames correspond. - */ -Label owner; - -/** - * The input stack map frame locals. - */ -int[] inputLocals; - -/** - * The input stack map frame stack. - */ -int[] inputStack; - -/** - * The output stack map frame locals. - */ -private int[] outputLocals; - -/** - * The output stack map frame stack. - */ -private int[] outputStack; - -/** - * Relative size of the output stack. The exact semantics of this field - * depends on the algorithm that is used. - *

- * When only the maximum stack size is computed, this field is the size of - * the output stack relatively to the top of the input stack. - *

- * When the stack map frames are completely computed, this field is the - * actual number of types in {@link #outputStack}. - */ -private int outputStackTop; - -/** - * Number of types that are initialized in the basic block. - * - * @see #initializations - */ -private int initializationCount; - -/** - * The types that are initialized in the basic block. A constructor - * invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace - * every occurence of this type in the local variables and in the - * operand stack. This cannot be done during the first phase of the - * algorithm since, during this phase, the local variables and the operand - * stack are not completely computed. It is therefore necessary to store the - * types on which constructors are invoked in the basic block, in order to - * do this replacement during the second phase of the algorithm, where the - * frames are fully computed. Note that this array can contain types that - * are relative to input locals or to the input stack (see below for the - * description of the algorithm). - */ -private int[] initializations; - -/** - * Returns the output frame local variable type at the given index. - * - * @param local the index of the local that must be returned. - * @return the output frame local variable type at the given index. - */ -private int get(final int local){ - if(outputLocals == null || local >= outputLocals.length) - { - // this local has never been assigned in this basic block, - // so it is still equal to its value in the input frame - return LOCAL | local; - } - else - { - int type = outputLocals[local]; - if(type == 0) - { - // this local has never been assigned in this basic block, - // so it is still equal to its value in the input frame - type = outputLocals[local] = LOCAL | local; - } - return type; - } -} - -/** - * Sets the output frame local variable type at the given index. - * - * @param local the index of the local that must be set. - * @param type the value of the local that must be set. - */ -private void set(final int local, final int type){ - // creates and/or resizes the output local variables array if necessary - if(outputLocals == null) - { - outputLocals = new int[10]; - } - int n = outputLocals.length; - if(local >= n) - { - int[] t = new int[Math.max(local + 1, 2 * n)]; - System.arraycopy(outputLocals, 0, t, 0, n); - outputLocals = t; - } - // sets the local variable - outputLocals[local] = type; -} - -/** - * Pushes a new type onto the output frame stack. - * - * @param type the type that must be pushed. - */ -private void push(final int type){ - // creates and/or resizes the output stack array if necessary - if(outputStack == null) - { - outputStack = new int[10]; - } - int n = outputStack.length; - if(outputStackTop >= n) - { - int[] t = new int[Math.max(outputStackTop + 1, 2 * n)]; - System.arraycopy(outputStack, 0, t, 0, n); - outputStack = t; - } - // pushes the type on the output stack - outputStack[outputStackTop++] = type; - // updates the maximun height reached by the output stack, if needed - int top = owner.inputStackTop + outputStackTop; - if(top > owner.outputStackMax) - { - owner.outputStackMax = top; - } -} - -/** - * Pushes a new type onto the output frame stack. - * - * @param cw the ClassWriter to which this label belongs. - * @param desc the descriptor of the type to be pushed. Can also be a method - * descriptor (in this case this method pushes its return type onto - * the output frame stack). - */ -private void push(final ClassWriter cw, final String desc){ - int type = type(cw, desc); - if(type != 0) - { - push(type); - if(type == LONG || type == DOUBLE) - { - push(TOP); - } - } -} - -/** - * Returns the int encoding of the given type. - * - * @param cw the ClassWriter to which this label belongs. - * @param desc a type descriptor. - * @return the int encoding of the given type. - */ -private int type(final ClassWriter cw, final String desc){ - String t; - int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; - switch(desc.charAt(index)) - { - case'V': - return 0; - case'Z': - case'C': - case'B': - case'S': - case'I': - return INTEGER; - case'F': - return FLOAT; - case'J': - return LONG; - case'D': - return DOUBLE; - case'L': - // stores the internal name, not the descriptor! - t = desc.substring(index + 1, desc.length() - 1); - return OBJECT | cw.addType(t); - // case '[': - default: - // extracts the dimensions and the element type - int data; - int dims = index + 1; - while(desc.charAt(dims) == '[') - { - ++dims; - } - switch(desc.charAt(dims)) - { - case'Z': - data = BOOLEAN; - break; - case'C': - data = CHAR; - break; - case'B': - data = BYTE; - break; - case'S': - data = SHORT; - break; - case'I': - data = INTEGER; - break; - case'F': - data = FLOAT; - break; - case'J': - data = LONG; - break; - case'D': - data = DOUBLE; - break; - // case 'L': - default: - // stores the internal name, not the descriptor - t = desc.substring(dims + 1, desc.length() - 1); - data = OBJECT | cw.addType(t); - } - return (dims - index) << 28 | data; - } -} - -/** - * Pops a type from the output frame stack and returns its value. - * - * @return the type that has been popped from the output frame stack. - */ -private int pop(){ - if(outputStackTop > 0) - { - return outputStack[--outputStackTop]; - } - else - { - // if the output frame stack is empty, pops from the input stack - return STACK | -(--owner.inputStackTop); - } -} - -/** - * Pops the given number of types from the output frame stack. - * - * @param elements the number of types that must be popped. - */ -private void pop(final int elements){ - if(outputStackTop >= elements) - { - outputStackTop -= elements; - } - else - { - // if the number of elements to be popped is greater than the number - // of elements in the output stack, clear it, and pops the remaining - // elements from the input stack. - owner.inputStackTop -= elements - outputStackTop; - outputStackTop = 0; - } -} - -/** - * Pops a type from the output frame stack. - * - * @param desc the descriptor of the type to be popped. Can also be a method - * descriptor (in this case this method pops the types corresponding - * to the method arguments). - */ -private void pop(final String desc){ - char c = desc.charAt(0); - if(c == '(') - { - pop((MethodWriter.getArgumentsAndReturnSizes(desc) >> 2) - 1); - } - else if(c == 'J' || c == 'D') - { - pop(2); - } - else - { - pop(1); - } -} - -/** - * Adds a new type to the list of types on which a constructor is invoked in - * the basic block. - * - * @param var a type on a which a constructor is invoked. - */ -private void init(final int var){ - // creates and/or resizes the initializations array if necessary - if(initializations == null) - { - initializations = new int[2]; - } - int n = initializations.length; - if(initializationCount >= n) - { - int[] t = new int[Math.max(initializationCount + 1, 2 * n)]; - System.arraycopy(initializations, 0, t, 0, n); - initializations = t; - } - // stores the type to be initialized - initializations[initializationCount++] = var; -} - -/** - * Replaces the given type with the appropriate type if it is one of the - * types on which a constructor is invoked in the basic block. - * - * @param cw the ClassWriter to which this label belongs. - * @param t a type - * @return t or, if t is one of the types on which a constructor is invoked - * in the basic block, the type corresponding to this constructor. - */ -private int init(final ClassWriter cw, final int t){ - int s; - if(t == UNINITIALIZED_THIS) - { - s = OBJECT | cw.addType(cw.thisName); - } - else if((t & (DIM | BASE_KIND)) == UNINITIALIZED) - { - String type = cw.typeTable[t & BASE_VALUE].strVal1; - s = OBJECT | cw.addType(type); - } - else - { - return t; - } - for(int j = 0; j < initializationCount; ++j) - { - int u = initializations[j]; - int dim = u & DIM; - int kind = u & KIND; - if(kind == LOCAL) - { - u = dim + inputLocals[u & VALUE]; - } - else if(kind == STACK) - { - u = dim + inputStack[inputStack.length - (u & VALUE)]; - } - if(t == u) - { - return s; - } - } - return t; -} - -/** - * Initializes the input frame of the first basic block from the method - * descriptor. - * - * @param cw the ClassWriter to which this label belongs. - * @param access the access flags of the method to which this label belongs. - * @param args the formal parameter types of this method. - * @param maxLocals the maximum number of local variables of this method. - */ -void initInputFrame( - final ClassWriter cw, - final int access, - final Type[] args, - final int maxLocals){ - inputLocals = new int[maxLocals]; - inputStack = new int[0]; - int i = 0; - if((access & Opcodes.ACC_STATIC) == 0) - { - if((access & MethodWriter.ACC_CONSTRUCTOR) == 0) - { - inputLocals[i++] = OBJECT | cw.addType(cw.thisName); - } - else - { - inputLocals[i++] = UNINITIALIZED_THIS; - } - } - for(int j = 0; j < args.length; ++j) - { - int t = type(cw, args[j].getDescriptor()); - inputLocals[i++] = t; - if(t == LONG || t == DOUBLE) - { - inputLocals[i++] = TOP; - } - } - while(i < maxLocals) - { - inputLocals[i++] = TOP; - } -} - -/** - * Simulates the action of the given instruction on the output stack frame. - * - * @param opcode the opcode of the instruction. - * @param arg the operand of the instruction, if any. - * @param cw the class writer to which this label belongs. - * @param item the operand of the instructions, if any. - */ -void execute( - final int opcode, - final int arg, - final ClassWriter cw, - final Item item){ - int t1, t2, t3, t4; - switch(opcode) - { - case Opcodes.NOP: - case Opcodes.INEG: - case Opcodes.LNEG: - case Opcodes.FNEG: - case Opcodes.DNEG: - case Opcodes.I2B: - case Opcodes.I2C: - case Opcodes.I2S: - case Opcodes.GOTO: - case Opcodes.RETURN: - break; - case Opcodes.ACONST_NULL: - push(NULL); - break; - case Opcodes.ICONST_M1: - case Opcodes.ICONST_0: - case Opcodes.ICONST_1: - case Opcodes.ICONST_2: - case Opcodes.ICONST_3: - case Opcodes.ICONST_4: - case Opcodes.ICONST_5: - case Opcodes.BIPUSH: - case Opcodes.SIPUSH: - case Opcodes.ILOAD: - push(INTEGER); - break; - case Opcodes.LCONST_0: - case Opcodes.LCONST_1: - case Opcodes.LLOAD: - push(LONG); - push(TOP); - break; - case Opcodes.FCONST_0: - case Opcodes.FCONST_1: - case Opcodes.FCONST_2: - case Opcodes.FLOAD: - push(FLOAT); - break; - case Opcodes.DCONST_0: - case Opcodes.DCONST_1: - case Opcodes.DLOAD: - push(DOUBLE); - push(TOP); - break; - case Opcodes.LDC: - switch(item.type) - { - case ClassWriter.INT: - push(INTEGER); - break; - case ClassWriter.LONG: - push(LONG); - push(TOP); - break; - case ClassWriter.FLOAT: - push(FLOAT); - break; - case ClassWriter.DOUBLE: - push(DOUBLE); - push(TOP); - break; - case ClassWriter.CLASS: - push(OBJECT | cw.addType("java/lang/Class")); - break; - // case ClassWriter.STR: - default: - push(OBJECT | cw.addType("java/lang/String")); - } - break; - case Opcodes.ALOAD: - push(get(arg)); - break; - case Opcodes.IALOAD: - case Opcodes.BALOAD: - case Opcodes.CALOAD: - case Opcodes.SALOAD: - pop(2); - push(INTEGER); - break; - case Opcodes.LALOAD: - case Opcodes.D2L: - pop(2); - push(LONG); - push(TOP); - break; - case Opcodes.FALOAD: - pop(2); - push(FLOAT); - break; - case Opcodes.DALOAD: - case Opcodes.L2D: - pop(2); - push(DOUBLE); - push(TOP); - break; - case Opcodes.AALOAD: - pop(1); - t1 = pop(); - push(ELEMENT_OF + t1); - break; - case Opcodes.ISTORE: - case Opcodes.FSTORE: - case Opcodes.ASTORE: - t1 = pop(); - set(arg, t1); - if(arg > 0) - { - t2 = get(arg - 1); - // if t2 is of kind STACK or LOCAL we cannot know its size! - if(t2 == LONG || t2 == DOUBLE) - { - set(arg - 1, TOP); - } - } - break; - case Opcodes.LSTORE: - case Opcodes.DSTORE: - pop(1); - t1 = pop(); - set(arg, t1); - set(arg + 1, TOP); - if(arg > 0) - { - t2 = get(arg - 1); - // if t2 is of kind STACK or LOCAL we cannot know its size! - if(t2 == LONG || t2 == DOUBLE) - { - set(arg - 1, TOP); - } - } - break; - case Opcodes.IASTORE: - case Opcodes.BASTORE: - case Opcodes.CASTORE: - case Opcodes.SASTORE: - case Opcodes.FASTORE: - case Opcodes.AASTORE: - pop(3); - break; - case Opcodes.LASTORE: - case Opcodes.DASTORE: - pop(4); - break; - case Opcodes.POP: - case Opcodes.IFEQ: - case Opcodes.IFNE: - case Opcodes.IFLT: - case Opcodes.IFGE: - case Opcodes.IFGT: - case Opcodes.IFLE: - case Opcodes.IRETURN: - case Opcodes.FRETURN: - case Opcodes.ARETURN: - case Opcodes.TABLESWITCH: - case Opcodes.LOOKUPSWITCH: - case Opcodes.ATHROW: - case Opcodes.MONITORENTER: - case Opcodes.MONITOREXIT: - case Opcodes.IFNULL: - case Opcodes.IFNONNULL: - pop(1); - break; - case Opcodes.POP2: - case Opcodes.IF_ICMPEQ: - case Opcodes.IF_ICMPNE: - case Opcodes.IF_ICMPLT: - case Opcodes.IF_ICMPGE: - case Opcodes.IF_ICMPGT: - case Opcodes.IF_ICMPLE: - case Opcodes.IF_ACMPEQ: - case Opcodes.IF_ACMPNE: - case Opcodes.LRETURN: - case Opcodes.DRETURN: - pop(2); - break; - case Opcodes.DUP: - t1 = pop(); - push(t1); - push(t1); - break; - case Opcodes.DUP_X1: - t1 = pop(); - t2 = pop(); - push(t1); - push(t2); - push(t1); - break; - case Opcodes.DUP_X2: - t1 = pop(); - t2 = pop(); - t3 = pop(); - push(t1); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.DUP2: - t1 = pop(); - t2 = pop(); - push(t2); - push(t1); - push(t2); - push(t1); - break; - case Opcodes.DUP2_X1: - t1 = pop(); - t2 = pop(); - t3 = pop(); - push(t2); - push(t1); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.DUP2_X2: - t1 = pop(); - t2 = pop(); - t3 = pop(); - t4 = pop(); - push(t2); - push(t1); - push(t4); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.SWAP: - t1 = pop(); - t2 = pop(); - push(t1); - push(t2); - break; - case Opcodes.IADD: - case Opcodes.ISUB: - case Opcodes.IMUL: - case Opcodes.IDIV: - case Opcodes.IREM: - case Opcodes.IAND: - case Opcodes.IOR: - case Opcodes.IXOR: - case Opcodes.ISHL: - case Opcodes.ISHR: - case Opcodes.IUSHR: - case Opcodes.L2I: - case Opcodes.D2I: - case Opcodes.FCMPL: - case Opcodes.FCMPG: - pop(2); - push(INTEGER); - break; - case Opcodes.LADD: - case Opcodes.LSUB: - case Opcodes.LMUL: - case Opcodes.LDIV: - case Opcodes.LREM: - case Opcodes.LAND: - case Opcodes.LOR: - case Opcodes.LXOR: - pop(4); - push(LONG); - push(TOP); - break; - case Opcodes.FADD: - case Opcodes.FSUB: - case Opcodes.FMUL: - case Opcodes.FDIV: - case Opcodes.FREM: - case Opcodes.L2F: - case Opcodes.D2F: - pop(2); - push(FLOAT); - break; - case Opcodes.DADD: - case Opcodes.DSUB: - case Opcodes.DMUL: - case Opcodes.DDIV: - case Opcodes.DREM: - pop(4); - push(DOUBLE); - push(TOP); - break; - case Opcodes.LSHL: - case Opcodes.LSHR: - case Opcodes.LUSHR: - pop(3); - push(LONG); - push(TOP); - break; - case Opcodes.IINC: - set(arg, INTEGER); - break; - case Opcodes.I2L: - case Opcodes.F2L: - pop(1); - push(LONG); - push(TOP); - break; - case Opcodes.I2F: - pop(1); - push(FLOAT); - break; - case Opcodes.I2D: - case Opcodes.F2D: - pop(1); - push(DOUBLE); - push(TOP); - break; - case Opcodes.F2I: - case Opcodes.ARRAYLENGTH: - case Opcodes.INSTANCEOF: - pop(1); - push(INTEGER); - break; - case Opcodes.LCMP: - case Opcodes.DCMPL: - case Opcodes.DCMPG: - pop(4); - push(INTEGER); - break; - case Opcodes.JSR: - case Opcodes.RET: - throw new RuntimeException("JSR/RET are not supported with computeFrames option"); - case Opcodes.GETSTATIC: - push(cw, item.strVal3); - break; - case Opcodes.PUTSTATIC: - pop(item.strVal3); - break; - case Opcodes.GETFIELD: - pop(1); - push(cw, item.strVal3); - break; - case Opcodes.PUTFIELD: - pop(item.strVal3); - pop(); - break; - case Opcodes.INVOKEVIRTUAL: - case Opcodes.INVOKESPECIAL: - case Opcodes.INVOKESTATIC: - case Opcodes.INVOKEINTERFACE: - pop(item.strVal3); - if(opcode != Opcodes.INVOKESTATIC) - { - t1 = pop(); - if(opcode == Opcodes.INVOKESPECIAL - && item.strVal2.charAt(0) == '<') - { - init(t1); - } - } - push(cw, item.strVal3); - break; - case Opcodes.NEW: - push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, arg)); - break; - case Opcodes.NEWARRAY: - pop(); - switch(arg) - { - case Opcodes.T_BOOLEAN: - push(ARRAY_OF | BOOLEAN); - break; - case Opcodes.T_CHAR: - push(ARRAY_OF | CHAR); - break; - case Opcodes.T_BYTE: - push(ARRAY_OF | BYTE); - break; - case Opcodes.T_SHORT: - push(ARRAY_OF | SHORT); - break; - case Opcodes.T_INT: - push(ARRAY_OF | INTEGER); - break; - case Opcodes.T_FLOAT: - push(ARRAY_OF | FLOAT); - break; - case Opcodes.T_DOUBLE: - push(ARRAY_OF | DOUBLE); - break; - // case Opcodes.T_LONG: - default: - push(ARRAY_OF | LONG); - break; - } - break; - case Opcodes.ANEWARRAY: - String s = item.strVal1; - pop(); - if(s.charAt(0) == '[') - { - push(cw, "[" + s); - } - else - { - push(ARRAY_OF | OBJECT | cw.addType(s)); - } - break; - case Opcodes.CHECKCAST: - s = item.strVal1; - pop(); - if(s.charAt(0) == '[') - { - push(cw, s); - } - else - { - push(OBJECT | cw.addType(s)); - } - break; - // case Opcodes.MULTIANEWARRAY: - default: - pop(arg); - push(cw, item.strVal1); - break; - } -} - -/** - * Merges the input frame of the given basic block with the input and output - * frames of this basic block. Returns true if the input frame of - * the given label has been changed by this operation. - * - * @param cw the ClassWriter to which this label belongs. - * @param frame the basic block whose input frame must be updated. - * @param edge the kind of the {@link Edge} between this label and 'label'. - * See {@link Edge#info}. - * @return true if the input frame of the given label has been - * changed by this operation. - */ -boolean merge(final ClassWriter cw, final Frame frame, final int edge){ - boolean changed = false; - int i, s, dim, kind, t; - - int nLocal = inputLocals.length; - int nStack = inputStack.length; - if(frame.inputLocals == null) - { - frame.inputLocals = new int[nLocal]; - changed = true; - } - - for(i = 0; i < nLocal; ++i) - { - if(outputLocals != null && i < outputLocals.length) - { - s = outputLocals[i]; - if(s == 0) - { - t = inputLocals[i]; - } - else - { - dim = s & DIM; - kind = s & KIND; - if(kind == LOCAL) - { - t = dim + inputLocals[s & VALUE]; - } - else if(kind == STACK) - { - t = dim + inputStack[nStack - (s & VALUE)]; - } - else - { - t = s; - } - } - } - else - { - t = inputLocals[i]; - } - if(initializations != null) - { - t = init(cw, t); - } - changed |= merge(cw, t, frame.inputLocals, i); - } - - if(edge > 0) - { - for(i = 0; i < nLocal; ++i) - { - t = inputLocals[i]; - changed |= merge(cw, t, frame.inputLocals, i); - } - if(frame.inputStack == null) - { - frame.inputStack = new int[1]; - changed = true; - } - changed |= merge(cw, edge, frame.inputStack, 0); - return changed; - } - - int nInputStack = inputStack.length + owner.inputStackTop; - if(frame.inputStack == null) - { - frame.inputStack = new int[nInputStack + outputStackTop]; - changed = true; - } - - for(i = 0; i < nInputStack; ++i) - { - t = inputStack[i]; - if(initializations != null) - { - t = init(cw, t); - } - changed |= merge(cw, t, frame.inputStack, i); - } - for(i = 0; i < outputStackTop; ++i) - { - s = outputStack[i]; - dim = s & DIM; - kind = s & KIND; - if(kind == LOCAL) - { - t = dim + inputLocals[s & VALUE]; - } - else if(kind == STACK) - { - t = dim + inputStack[nStack - (s & VALUE)]; - } - else - { - t = s; - } - if(initializations != null) - { - t = init(cw, t); - } - changed |= merge(cw, t, frame.inputStack, nInputStack + i); - } - return changed; -} - -/** - * Merges the type at the given index in the given type array with the given - * type. Returns true if the type array has been modified by this - * operation. - * - * @param cw the ClassWriter to which this label belongs. - * @param t the type with which the type array element must be merged. - * @param types an array of types. - * @param index the index of the type that must be merged in 'types'. - * @return true if the type array has been modified by this - * operation. - */ -private boolean merge( - final ClassWriter cw, - int t, - final int[] types, - final int index){ - int u = types[index]; - if(u == t) - { - // if the types are equal, merge(u,t)=u, so there is no change - return false; - } - if((t & ~DIM) == NULL) - { - if(u == NULL) - { - return false; - } - t = NULL; - } - if(u == 0) - { - // if types[index] has never been assigned, merge(u,t)=t - types[index] = t; - return true; - } - int v; - if((u & BASE_KIND) == OBJECT || (u & DIM) != 0) - { - // if u is a reference type of any dimension - if(t == NULL) - { - // if t is the NULL type, merge(u,t)=u, so there is no change - return false; - } - else if((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) - { - if((u & BASE_KIND) == OBJECT) - { - // if t is also a reference type, and if u and t have the - // same dimension merge(u,t) = dim(t) | common parent of the - // element types of u and t - v = (t & DIM) | OBJECT - | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE); - } - else - { - // if u and t are array types, but not with the same element - // type, merge(u,t)=java/lang/Object - v = OBJECT | cw.addType("java/lang/Object"); - } - } - else if((t & BASE_KIND) == OBJECT || (t & DIM) != 0) - { - // if t is any other reference or array type, - // merge(u,t)=java/lang/Object - v = OBJECT | cw.addType("java/lang/Object"); - } - else - { - // if t is any other type, merge(u,t)=TOP - v = TOP; - } - } - else if(u == NULL) - { - // if u is the NULL type, merge(u,t)=t, - // or TOP if t is not a reference type - v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP; - } - else - { - // if u is any other type, merge(u,t)=TOP whatever t - v = TOP; - } - if(u != v) - { - types[index] = v; - return true; - } - return false; -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * Information about the input and output stack map frames of a basic block. + * + * @author Eric Bruneton + */ +final class Frame{ + +/* + * Frames are computed in a two steps process: during the visit of each + * instruction, the state of the frame at the end of current basic block is + * updated by simulating the action of the instruction on the previous state + * of this so called "output frame". In visitMaxs, a fix point algorithm is + * used to compute the "input frame" of each basic block, i.e. the stack map + * frame at the begining of the basic block, starting from the input frame + * of the first basic block (which is computed from the method descriptor), + * and by using the previously computed output frames to compute the input + * state of the other blocks. + * + * All output and input frames are stored as arrays of integers. Reference + * and array types are represented by an index into a type table (which is + * not the same as the constant pool of the class, in order to avoid adding + * unnecessary constants in the pool - not all computed frames will end up + * being stored in the stack map table). This allows very fast type + * comparisons. + * + * Output stack map frames are computed relatively to the input frame of the + * basic block, which is not yet known when output frames are computed. It + * is therefore necessary to be able to represent abstract types such as + * "the type at position x in the input frame locals" or "the type at + * position x from the top of the input frame stack" or even "the type at + * position x in the input frame, with y more (or less) array dimensions". + * This explains the rather complicated type format used in output frames. + * + * This format is the following: DIM KIND VALUE (4, 4 and 24 bits). DIM is a + * signed number of array dimensions (from -8 to 7). KIND is either BASE, + * LOCAL or STACK. BASE is used for types that are not relative to the input + * frame. LOCAL is used for types that are relative to the input local + * variable types. STACK is used for types that are relative to the input + * stack types. VALUE depends on KIND. For LOCAL types, it is an index in + * the input local variable types. For STACK types, it is a position + * relatively to the top of input frame stack. For BASE types, it is either + * one of the constants defined in FrameVisitor, or for OBJECT and + * UNINITIALIZED types, a tag and an index in the type table. + * + * Output frames can contain types of any kind and with a positive or + * negative dimension (and even unassigned types, represented by 0 - which + * does not correspond to any valid type value). Input frames can only + * contain BASE types of positive or null dimension. In all cases the type + * table contains only internal type names (array type descriptors are + * forbidden - dimensions must be represented through the DIM field). + * + * The LONG and DOUBLE types are always represented by using two slots (LONG + + * TOP or DOUBLE + TOP), for local variable types as well as in the operand + * stack. This is necessary to be able to simulate DUPx_y instructions, + * whose effect would be dependent on the actual type values if types were + * always represented by a single slot in the stack (and this is not + * possible, since actual type values are not always known - cf LOCAL and + * STACK type kinds). + */ + +/** + * Mask to get the dimension of a frame type. This dimension is a signed + * integer between -8 and 7. + */ +final static int DIM = 0xF0000000; + +/** + * Constant to be added to a type to get a type with one more dimension. + */ +final static int ARRAY_OF = 0x10000000; + +/** + * Constant to be added to a type to get a type with one less dimension. + */ +final static int ELEMENT_OF = 0xF0000000; + +/** + * Mask to get the kind of a frame type. + * + * @see #BASE + * @see #LOCAL + * @see #STACK + */ +final static int KIND = 0xF000000; + +/** + * Mask to get the value of a frame type. + */ +final static int VALUE = 0xFFFFFF; + +/** + * Mask to get the kind of base types. + */ +final static int BASE_KIND = 0xFF00000; + +/** + * Mask to get the value of base types. + */ +final static int BASE_VALUE = 0xFFFFF; + +/** + * Kind of the types that are not relative to an input stack map frame. + */ +final static int BASE = 0x1000000; + +/** + * Base kind of the base reference types. The BASE_VALUE of such types is an + * index into the type table. + */ +final static int OBJECT = BASE | 0x700000; + +/** + * Base kind of the uninitialized base types. The BASE_VALUE of such types + * in an index into the type table (the Item at that index contains both an + * instruction offset and an internal class name). + */ +final static int UNINITIALIZED = BASE | 0x800000; + +/** + * Kind of the types that are relative to the local variable types of an + * input stack map frame. The value of such types is a local variable index. + */ +private final static int LOCAL = 0x2000000; + +/** + * Kind of the the types that are relative to the stack of an input stack + * map frame. The value of such types is a position relatively to the top of + * this stack. + */ +private final static int STACK = 0x3000000; + +/** + * The TOP type. This is a BASE type. + */ +final static int TOP = BASE | 0; + +/** + * The BOOLEAN type. This is a BASE type mainly used for array types. + */ +final static int BOOLEAN = BASE | 9; + +/** + * The BYTE type. This is a BASE type mainly used for array types. + */ +final static int BYTE = BASE | 10; + +/** + * The CHAR type. This is a BASE type mainly used for array types. + */ +final static int CHAR = BASE | 11; + +/** + * The SHORT type. This is a BASE type mainly used for array types. + */ +final static int SHORT = BASE | 12; + +/** + * The INTEGER type. This is a BASE type. + */ +final static int INTEGER = BASE | 1; + +/** + * The FLOAT type. This is a BASE type. + */ +final static int FLOAT = BASE | 2; + +/** + * The DOUBLE type. This is a BASE type. + */ +final static int DOUBLE = BASE | 3; + +/** + * The LONG type. This is a BASE type. + */ +final static int LONG = BASE | 4; + +/** + * The NULL type. This is a BASE type. + */ +final static int NULL = BASE | 5; + +/** + * The UNINITIALIZED_THIS type. This is a BASE type. + */ +final static int UNINITIALIZED_THIS = BASE | 6; + +/** + * The stack size variation corresponding to each JVM instruction. This + * stack variation is equal to the size of the values produced by an + * instruction, minus the size of the values consumed by this instruction. + */ +final static int[] SIZE; + +/** + * Computes the stack size variation corresponding to each JVM instruction. + */ +static + { + int i; + int[] b = new int[202]; + String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD" + + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD" + + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED" + + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; + for(i = 0; i < b.length; ++i) + { + b[i] = s.charAt(i) - 'E'; + } + SIZE = b; + + // code to generate the above string + // + // int NA = 0; // not applicable (unused opcode or variable size opcode) + // + // b = new int[] { + // 0, //NOP, // visitInsn + // 1, //ACONST_NULL, // - + // 1, //ICONST_M1, // - + // 1, //ICONST_0, // - + // 1, //ICONST_1, // - + // 1, //ICONST_2, // - + // 1, //ICONST_3, // - + // 1, //ICONST_4, // - + // 1, //ICONST_5, // - + // 2, //LCONST_0, // - + // 2, //LCONST_1, // - + // 1, //FCONST_0, // - + // 1, //FCONST_1, // - + // 1, //FCONST_2, // - + // 2, //DCONST_0, // - + // 2, //DCONST_1, // - + // 1, //BIPUSH, // visitIntInsn + // 1, //SIPUSH, // - + // 1, //LDC, // visitLdcInsn + // NA, //LDC_W, // - + // NA, //LDC2_W, // - + // 1, //ILOAD, // visitVarInsn + // 2, //LLOAD, // - + // 1, //FLOAD, // - + // 2, //DLOAD, // - + // 1, //ALOAD, // - + // NA, //ILOAD_0, // - + // NA, //ILOAD_1, // - + // NA, //ILOAD_2, // - + // NA, //ILOAD_3, // - + // NA, //LLOAD_0, // - + // NA, //LLOAD_1, // - + // NA, //LLOAD_2, // - + // NA, //LLOAD_3, // - + // NA, //FLOAD_0, // - + // NA, //FLOAD_1, // - + // NA, //FLOAD_2, // - + // NA, //FLOAD_3, // - + // NA, //DLOAD_0, // - + // NA, //DLOAD_1, // - + // NA, //DLOAD_2, // - + // NA, //DLOAD_3, // - + // NA, //ALOAD_0, // - + // NA, //ALOAD_1, // - + // NA, //ALOAD_2, // - + // NA, //ALOAD_3, // - + // -1, //IALOAD, // visitInsn + // 0, //LALOAD, // - + // -1, //FALOAD, // - + // 0, //DALOAD, // - + // -1, //AALOAD, // - + // -1, //BALOAD, // - + // -1, //CALOAD, // - + // -1, //SALOAD, // - + // -1, //ISTORE, // visitVarInsn + // -2, //LSTORE, // - + // -1, //FSTORE, // - + // -2, //DSTORE, // - + // -1, //ASTORE, // - + // NA, //ISTORE_0, // - + // NA, //ISTORE_1, // - + // NA, //ISTORE_2, // - + // NA, //ISTORE_3, // - + // NA, //LSTORE_0, // - + // NA, //LSTORE_1, // - + // NA, //LSTORE_2, // - + // NA, //LSTORE_3, // - + // NA, //FSTORE_0, // - + // NA, //FSTORE_1, // - + // NA, //FSTORE_2, // - + // NA, //FSTORE_3, // - + // NA, //DSTORE_0, // - + // NA, //DSTORE_1, // - + // NA, //DSTORE_2, // - + // NA, //DSTORE_3, // - + // NA, //ASTORE_0, // - + // NA, //ASTORE_1, // - + // NA, //ASTORE_2, // - + // NA, //ASTORE_3, // - + // -3, //IASTORE, // visitInsn + // -4, //LASTORE, // - + // -3, //FASTORE, // - + // -4, //DASTORE, // - + // -3, //AASTORE, // - + // -3, //BASTORE, // - + // -3, //CASTORE, // - + // -3, //SASTORE, // - + // -1, //POP, // - + // -2, //POP2, // - + // 1, //DUP, // - + // 1, //DUP_X1, // - + // 1, //DUP_X2, // - + // 2, //DUP2, // - + // 2, //DUP2_X1, // - + // 2, //DUP2_X2, // - + // 0, //SWAP, // - + // -1, //IADD, // - + // -2, //LADD, // - + // -1, //FADD, // - + // -2, //DADD, // - + // -1, //ISUB, // - + // -2, //LSUB, // - + // -1, //FSUB, // - + // -2, //DSUB, // - + // -1, //IMUL, // - + // -2, //LMUL, // - + // -1, //FMUL, // - + // -2, //DMUL, // - + // -1, //IDIV, // - + // -2, //LDIV, // - + // -1, //FDIV, // - + // -2, //DDIV, // - + // -1, //IREM, // - + // -2, //LREM, // - + // -1, //FREM, // - + // -2, //DREM, // - + // 0, //INEG, // - + // 0, //LNEG, // - + // 0, //FNEG, // - + // 0, //DNEG, // - + // -1, //ISHL, // - + // -1, //LSHL, // - + // -1, //ISHR, // - + // -1, //LSHR, // - + // -1, //IUSHR, // - + // -1, //LUSHR, // - + // -1, //IAND, // - + // -2, //LAND, // - + // -1, //IOR, // - + // -2, //LOR, // - + // -1, //IXOR, // - + // -2, //LXOR, // - + // 0, //IINC, // visitIincInsn + // 1, //I2L, // visitInsn + // 0, //I2F, // - + // 1, //I2D, // - + // -1, //L2I, // - + // -1, //L2F, // - + // 0, //L2D, // - + // 0, //F2I, // - + // 1, //F2L, // - + // 1, //F2D, // - + // -1, //D2I, // - + // 0, //D2L, // - + // -1, //D2F, // - + // 0, //I2B, // - + // 0, //I2C, // - + // 0, //I2S, // - + // -3, //LCMP, // - + // -1, //FCMPL, // - + // -1, //FCMPG, // - + // -3, //DCMPL, // - + // -3, //DCMPG, // - + // -1, //IFEQ, // visitJumpInsn + // -1, //IFNE, // - + // -1, //IFLT, // - + // -1, //IFGE, // - + // -1, //IFGT, // - + // -1, //IFLE, // - + // -2, //IF_ICMPEQ, // - + // -2, //IF_ICMPNE, // - + // -2, //IF_ICMPLT, // - + // -2, //IF_ICMPGE, // - + // -2, //IF_ICMPGT, // - + // -2, //IF_ICMPLE, // - + // -2, //IF_ACMPEQ, // - + // -2, //IF_ACMPNE, // - + // 0, //GOTO, // - + // 1, //JSR, // - + // 0, //RET, // visitVarInsn + // -1, //TABLESWITCH, // visiTableSwitchInsn + // -1, //LOOKUPSWITCH, // visitLookupSwitch + // -1, //IRETURN, // visitInsn + // -2, //LRETURN, // - + // -1, //FRETURN, // - + // -2, //DRETURN, // - + // -1, //ARETURN, // - + // 0, //RETURN, // - + // NA, //GETSTATIC, // visitFieldInsn + // NA, //PUTSTATIC, // - + // NA, //GETFIELD, // - + // NA, //PUTFIELD, // - + // NA, //INVOKEVIRTUAL, // visitMethodInsn + // NA, //INVOKESPECIAL, // - + // NA, //INVOKESTATIC, // - + // NA, //INVOKEINTERFACE, // - + // NA, //UNUSED, // NOT VISITED + // 1, //NEW, // visitTypeInsn + // 0, //NEWARRAY, // visitIntInsn + // 0, //ANEWARRAY, // visitTypeInsn + // 0, //ARRAYLENGTH, // visitInsn + // NA, //ATHROW, // - + // 0, //CHECKCAST, // visitTypeInsn + // 0, //INSTANCEOF, // - + // -1, //MONITORENTER, // visitInsn + // -1, //MONITOREXIT, // - + // NA, //WIDE, // NOT VISITED + // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn + // -1, //IFNULL, // visitJumpInsn + // -1, //IFNONNULL, // - + // NA, //GOTO_W, // - + // NA, //JSR_W, // - + // }; + // for (i = 0; i < b.length; ++i) { + // System.err.print((char)('E' + b[i])); + // } + // System.err.println(); + } + +/** + * The label (i.e. basic block) to which these input and output stack map + * frames correspond. + */ +Label owner; + +/** + * The input stack map frame locals. + */ +int[] inputLocals; + +/** + * The input stack map frame stack. + */ +int[] inputStack; + +/** + * The output stack map frame locals. + */ +private int[] outputLocals; + +/** + * The output stack map frame stack. + */ +private int[] outputStack; + +/** + * Relative size of the output stack. The exact semantics of this field + * depends on the algorithm that is used. + *

+ * When only the maximum stack size is computed, this field is the size of + * the output stack relatively to the top of the input stack. + *

+ * When the stack map frames are completely computed, this field is the + * actual number of types in {@link #outputStack}. + */ +private int outputStackTop; + +/** + * Number of types that are initialized in the basic block. + * + * @see #initializations + */ +private int initializationCount; + +/** + * The types that are initialized in the basic block. A constructor + * invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace + * every occurence of this type in the local variables and in the + * operand stack. This cannot be done during the first phase of the + * algorithm since, during this phase, the local variables and the operand + * stack are not completely computed. It is therefore necessary to store the + * types on which constructors are invoked in the basic block, in order to + * do this replacement during the second phase of the algorithm, where the + * frames are fully computed. Note that this array can contain types that + * are relative to input locals or to the input stack (see below for the + * description of the algorithm). + */ +private int[] initializations; + +/** + * Returns the output frame local variable type at the given index. + * + * @param local the index of the local that must be returned. + * @return the output frame local variable type at the given index. + */ +private int get(final int local){ + if(outputLocals == null || local >= outputLocals.length) + { + // this local has never been assigned in this basic block, + // so it is still equal to its value in the input frame + return LOCAL | local; + } + else + { + int type = outputLocals[local]; + if(type == 0) + { + // this local has never been assigned in this basic block, + // so it is still equal to its value in the input frame + type = outputLocals[local] = LOCAL | local; + } + return type; + } +} + +/** + * Sets the output frame local variable type at the given index. + * + * @param local the index of the local that must be set. + * @param type the value of the local that must be set. + */ +private void set(final int local, final int type){ + // creates and/or resizes the output local variables array if necessary + if(outputLocals == null) + { + outputLocals = new int[10]; + } + int n = outputLocals.length; + if(local >= n) + { + int[] t = new int[Math.max(local + 1, 2 * n)]; + System.arraycopy(outputLocals, 0, t, 0, n); + outputLocals = t; + } + // sets the local variable + outputLocals[local] = type; +} + +/** + * Pushes a new type onto the output frame stack. + * + * @param type the type that must be pushed. + */ +private void push(final int type){ + // creates and/or resizes the output stack array if necessary + if(outputStack == null) + { + outputStack = new int[10]; + } + int n = outputStack.length; + if(outputStackTop >= n) + { + int[] t = new int[Math.max(outputStackTop + 1, 2 * n)]; + System.arraycopy(outputStack, 0, t, 0, n); + outputStack = t; + } + // pushes the type on the output stack + outputStack[outputStackTop++] = type; + // updates the maximun height reached by the output stack, if needed + int top = owner.inputStackTop + outputStackTop; + if(top > owner.outputStackMax) + { + owner.outputStackMax = top; + } +} + +/** + * Pushes a new type onto the output frame stack. + * + * @param cw the ClassWriter to which this label belongs. + * @param desc the descriptor of the type to be pushed. Can also be a method + * descriptor (in this case this method pushes its return type onto + * the output frame stack). + */ +private void push(final ClassWriter cw, final String desc){ + int type = type(cw, desc); + if(type != 0) + { + push(type); + if(type == LONG || type == DOUBLE) + { + push(TOP); + } + } +} + +/** + * Returns the int encoding of the given type. + * + * @param cw the ClassWriter to which this label belongs. + * @param desc a type descriptor. + * @return the int encoding of the given type. + */ +private int type(final ClassWriter cw, final String desc){ + String t; + int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; + switch(desc.charAt(index)) + { + case'V': + return 0; + case'Z': + case'C': + case'B': + case'S': + case'I': + return INTEGER; + case'F': + return FLOAT; + case'J': + return LONG; + case'D': + return DOUBLE; + case'L': + // stores the internal name, not the descriptor! + t = desc.substring(index + 1, desc.length() - 1); + return OBJECT | cw.addType(t); + // case '[': + default: + // extracts the dimensions and the element type + int data; + int dims = index + 1; + while(desc.charAt(dims) == '[') + { + ++dims; + } + switch(desc.charAt(dims)) + { + case'Z': + data = BOOLEAN; + break; + case'C': + data = CHAR; + break; + case'B': + data = BYTE; + break; + case'S': + data = SHORT; + break; + case'I': + data = INTEGER; + break; + case'F': + data = FLOAT; + break; + case'J': + data = LONG; + break; + case'D': + data = DOUBLE; + break; + // case 'L': + default: + // stores the internal name, not the descriptor + t = desc.substring(dims + 1, desc.length() - 1); + data = OBJECT | cw.addType(t); + } + return (dims - index) << 28 | data; + } +} + +/** + * Pops a type from the output frame stack and returns its value. + * + * @return the type that has been popped from the output frame stack. + */ +private int pop(){ + if(outputStackTop > 0) + { + return outputStack[--outputStackTop]; + } + else + { + // if the output frame stack is empty, pops from the input stack + return STACK | -(--owner.inputStackTop); + } +} + +/** + * Pops the given number of types from the output frame stack. + * + * @param elements the number of types that must be popped. + */ +private void pop(final int elements){ + if(outputStackTop >= elements) + { + outputStackTop -= elements; + } + else + { + // if the number of elements to be popped is greater than the number + // of elements in the output stack, clear it, and pops the remaining + // elements from the input stack. + owner.inputStackTop -= elements - outputStackTop; + outputStackTop = 0; + } +} + +/** + * Pops a type from the output frame stack. + * + * @param desc the descriptor of the type to be popped. Can also be a method + * descriptor (in this case this method pops the types corresponding + * to the method arguments). + */ +private void pop(final String desc){ + char c = desc.charAt(0); + if(c == '(') + { + pop((MethodWriter.getArgumentsAndReturnSizes(desc) >> 2) - 1); + } + else if(c == 'J' || c == 'D') + { + pop(2); + } + else + { + pop(1); + } +} + +/** + * Adds a new type to the list of types on which a constructor is invoked in + * the basic block. + * + * @param var a type on a which a constructor is invoked. + */ +private void init(final int var){ + // creates and/or resizes the initializations array if necessary + if(initializations == null) + { + initializations = new int[2]; + } + int n = initializations.length; + if(initializationCount >= n) + { + int[] t = new int[Math.max(initializationCount + 1, 2 * n)]; + System.arraycopy(initializations, 0, t, 0, n); + initializations = t; + } + // stores the type to be initialized + initializations[initializationCount++] = var; +} + +/** + * Replaces the given type with the appropriate type if it is one of the + * types on which a constructor is invoked in the basic block. + * + * @param cw the ClassWriter to which this label belongs. + * @param t a type + * @return t or, if t is one of the types on which a constructor is invoked + * in the basic block, the type corresponding to this constructor. + */ +private int init(final ClassWriter cw, final int t){ + int s; + if(t == UNINITIALIZED_THIS) + { + s = OBJECT | cw.addType(cw.thisName); + } + else if((t & (DIM | BASE_KIND)) == UNINITIALIZED) + { + String type = cw.typeTable[t & BASE_VALUE].strVal1; + s = OBJECT | cw.addType(type); + } + else + { + return t; + } + for(int j = 0; j < initializationCount; ++j) + { + int u = initializations[j]; + int dim = u & DIM; + int kind = u & KIND; + if(kind == LOCAL) + { + u = dim + inputLocals[u & VALUE]; + } + else if(kind == STACK) + { + u = dim + inputStack[inputStack.length - (u & VALUE)]; + } + if(t == u) + { + return s; + } + } + return t; +} + +/** + * Initializes the input frame of the first basic block from the method + * descriptor. + * + * @param cw the ClassWriter to which this label belongs. + * @param access the access flags of the method to which this label belongs. + * @param args the formal parameter types of this method. + * @param maxLocals the maximum number of local variables of this method. + */ +void initInputFrame( + final ClassWriter cw, + final int access, + final Type[] args, + final int maxLocals){ + inputLocals = new int[maxLocals]; + inputStack = new int[0]; + int i = 0; + if((access & Opcodes.ACC_STATIC) == 0) + { + if((access & MethodWriter.ACC_CONSTRUCTOR) == 0) + { + inputLocals[i++] = OBJECT | cw.addType(cw.thisName); + } + else + { + inputLocals[i++] = UNINITIALIZED_THIS; + } + } + for(int j = 0; j < args.length; ++j) + { + int t = type(cw, args[j].getDescriptor()); + inputLocals[i++] = t; + if(t == LONG || t == DOUBLE) + { + inputLocals[i++] = TOP; + } + } + while(i < maxLocals) + { + inputLocals[i++] = TOP; + } +} + +/** + * Simulates the action of the given instruction on the output stack frame. + * + * @param opcode the opcode of the instruction. + * @param arg the operand of the instruction, if any. + * @param cw the class writer to which this label belongs. + * @param item the operand of the instructions, if any. + */ +void execute( + final int opcode, + final int arg, + final ClassWriter cw, + final Item item){ + int t1, t2, t3, t4; + switch(opcode) + { + case Opcodes.NOP: + case Opcodes.INEG: + case Opcodes.LNEG: + case Opcodes.FNEG: + case Opcodes.DNEG: + case Opcodes.I2B: + case Opcodes.I2C: + case Opcodes.I2S: + case Opcodes.GOTO: + case Opcodes.RETURN: + break; + case Opcodes.ACONST_NULL: + push(NULL); + break; + case Opcodes.ICONST_M1: + case Opcodes.ICONST_0: + case Opcodes.ICONST_1: + case Opcodes.ICONST_2: + case Opcodes.ICONST_3: + case Opcodes.ICONST_4: + case Opcodes.ICONST_5: + case Opcodes.BIPUSH: + case Opcodes.SIPUSH: + case Opcodes.ILOAD: + push(INTEGER); + break; + case Opcodes.LCONST_0: + case Opcodes.LCONST_1: + case Opcodes.LLOAD: + push(LONG); + push(TOP); + break; + case Opcodes.FCONST_0: + case Opcodes.FCONST_1: + case Opcodes.FCONST_2: + case Opcodes.FLOAD: + push(FLOAT); + break; + case Opcodes.DCONST_0: + case Opcodes.DCONST_1: + case Opcodes.DLOAD: + push(DOUBLE); + push(TOP); + break; + case Opcodes.LDC: + switch(item.type) + { + case ClassWriter.INT: + push(INTEGER); + break; + case ClassWriter.LONG: + push(LONG); + push(TOP); + break; + case ClassWriter.FLOAT: + push(FLOAT); + break; + case ClassWriter.DOUBLE: + push(DOUBLE); + push(TOP); + break; + case ClassWriter.CLASS: + push(OBJECT | cw.addType("java/lang/Class")); + break; + // case ClassWriter.STR: + default: + push(OBJECT | cw.addType("java/lang/String")); + } + break; + case Opcodes.ALOAD: + push(get(arg)); + break; + case Opcodes.IALOAD: + case Opcodes.BALOAD: + case Opcodes.CALOAD: + case Opcodes.SALOAD: + pop(2); + push(INTEGER); + break; + case Opcodes.LALOAD: + case Opcodes.D2L: + pop(2); + push(LONG); + push(TOP); + break; + case Opcodes.FALOAD: + pop(2); + push(FLOAT); + break; + case Opcodes.DALOAD: + case Opcodes.L2D: + pop(2); + push(DOUBLE); + push(TOP); + break; + case Opcodes.AALOAD: + pop(1); + t1 = pop(); + push(ELEMENT_OF + t1); + break; + case Opcodes.ISTORE: + case Opcodes.FSTORE: + case Opcodes.ASTORE: + t1 = pop(); + set(arg, t1); + if(arg > 0) + { + t2 = get(arg - 1); + // if t2 is of kind STACK or LOCAL we cannot know its size! + if(t2 == LONG || t2 == DOUBLE) + { + set(arg - 1, TOP); + } + } + break; + case Opcodes.LSTORE: + case Opcodes.DSTORE: + pop(1); + t1 = pop(); + set(arg, t1); + set(arg + 1, TOP); + if(arg > 0) + { + t2 = get(arg - 1); + // if t2 is of kind STACK or LOCAL we cannot know its size! + if(t2 == LONG || t2 == DOUBLE) + { + set(arg - 1, TOP); + } + } + break; + case Opcodes.IASTORE: + case Opcodes.BASTORE: + case Opcodes.CASTORE: + case Opcodes.SASTORE: + case Opcodes.FASTORE: + case Opcodes.AASTORE: + pop(3); + break; + case Opcodes.LASTORE: + case Opcodes.DASTORE: + pop(4); + break; + case Opcodes.POP: + case Opcodes.IFEQ: + case Opcodes.IFNE: + case Opcodes.IFLT: + case Opcodes.IFGE: + case Opcodes.IFGT: + case Opcodes.IFLE: + case Opcodes.IRETURN: + case Opcodes.FRETURN: + case Opcodes.ARETURN: + case Opcodes.TABLESWITCH: + case Opcodes.LOOKUPSWITCH: + case Opcodes.ATHROW: + case Opcodes.MONITORENTER: + case Opcodes.MONITOREXIT: + case Opcodes.IFNULL: + case Opcodes.IFNONNULL: + pop(1); + break; + case Opcodes.POP2: + case Opcodes.IF_ICMPEQ: + case Opcodes.IF_ICMPNE: + case Opcodes.IF_ICMPLT: + case Opcodes.IF_ICMPGE: + case Opcodes.IF_ICMPGT: + case Opcodes.IF_ICMPLE: + case Opcodes.IF_ACMPEQ: + case Opcodes.IF_ACMPNE: + case Opcodes.LRETURN: + case Opcodes.DRETURN: + pop(2); + break; + case Opcodes.DUP: + t1 = pop(); + push(t1); + push(t1); + break; + case Opcodes.DUP_X1: + t1 = pop(); + t2 = pop(); + push(t1); + push(t2); + push(t1); + break; + case Opcodes.DUP_X2: + t1 = pop(); + t2 = pop(); + t3 = pop(); + push(t1); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.DUP2: + t1 = pop(); + t2 = pop(); + push(t2); + push(t1); + push(t2); + push(t1); + break; + case Opcodes.DUP2_X1: + t1 = pop(); + t2 = pop(); + t3 = pop(); + push(t2); + push(t1); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.DUP2_X2: + t1 = pop(); + t2 = pop(); + t3 = pop(); + t4 = pop(); + push(t2); + push(t1); + push(t4); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.SWAP: + t1 = pop(); + t2 = pop(); + push(t1); + push(t2); + break; + case Opcodes.IADD: + case Opcodes.ISUB: + case Opcodes.IMUL: + case Opcodes.IDIV: + case Opcodes.IREM: + case Opcodes.IAND: + case Opcodes.IOR: + case Opcodes.IXOR: + case Opcodes.ISHL: + case Opcodes.ISHR: + case Opcodes.IUSHR: + case Opcodes.L2I: + case Opcodes.D2I: + case Opcodes.FCMPL: + case Opcodes.FCMPG: + pop(2); + push(INTEGER); + break; + case Opcodes.LADD: + case Opcodes.LSUB: + case Opcodes.LMUL: + case Opcodes.LDIV: + case Opcodes.LREM: + case Opcodes.LAND: + case Opcodes.LOR: + case Opcodes.LXOR: + pop(4); + push(LONG); + push(TOP); + break; + case Opcodes.FADD: + case Opcodes.FSUB: + case Opcodes.FMUL: + case Opcodes.FDIV: + case Opcodes.FREM: + case Opcodes.L2F: + case Opcodes.D2F: + pop(2); + push(FLOAT); + break; + case Opcodes.DADD: + case Opcodes.DSUB: + case Opcodes.DMUL: + case Opcodes.DDIV: + case Opcodes.DREM: + pop(4); + push(DOUBLE); + push(TOP); + break; + case Opcodes.LSHL: + case Opcodes.LSHR: + case Opcodes.LUSHR: + pop(3); + push(LONG); + push(TOP); + break; + case Opcodes.IINC: + set(arg, INTEGER); + break; + case Opcodes.I2L: + case Opcodes.F2L: + pop(1); + push(LONG); + push(TOP); + break; + case Opcodes.I2F: + pop(1); + push(FLOAT); + break; + case Opcodes.I2D: + case Opcodes.F2D: + pop(1); + push(DOUBLE); + push(TOP); + break; + case Opcodes.F2I: + case Opcodes.ARRAYLENGTH: + case Opcodes.INSTANCEOF: + pop(1); + push(INTEGER); + break; + case Opcodes.LCMP: + case Opcodes.DCMPL: + case Opcodes.DCMPG: + pop(4); + push(INTEGER); + break; + case Opcodes.JSR: + case Opcodes.RET: + throw new RuntimeException("JSR/RET are not supported with computeFrames option"); + case Opcodes.GETSTATIC: + push(cw, item.strVal3); + break; + case Opcodes.PUTSTATIC: + pop(item.strVal3); + break; + case Opcodes.GETFIELD: + pop(1); + push(cw, item.strVal3); + break; + case Opcodes.PUTFIELD: + pop(item.strVal3); + pop(); + break; + case Opcodes.INVOKEVIRTUAL: + case Opcodes.INVOKESPECIAL: + case Opcodes.INVOKESTATIC: + case Opcodes.INVOKEINTERFACE: + pop(item.strVal3); + if(opcode != Opcodes.INVOKESTATIC) + { + t1 = pop(); + if(opcode == Opcodes.INVOKESPECIAL + && item.strVal2.charAt(0) == '<') + { + init(t1); + } + } + push(cw, item.strVal3); + break; + case Opcodes.NEW: + push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, arg)); + break; + case Opcodes.NEWARRAY: + pop(); + switch(arg) + { + case Opcodes.T_BOOLEAN: + push(ARRAY_OF | BOOLEAN); + break; + case Opcodes.T_CHAR: + push(ARRAY_OF | CHAR); + break; + case Opcodes.T_BYTE: + push(ARRAY_OF | BYTE); + break; + case Opcodes.T_SHORT: + push(ARRAY_OF | SHORT); + break; + case Opcodes.T_INT: + push(ARRAY_OF | INTEGER); + break; + case Opcodes.T_FLOAT: + push(ARRAY_OF | FLOAT); + break; + case Opcodes.T_DOUBLE: + push(ARRAY_OF | DOUBLE); + break; + // case Opcodes.T_LONG: + default: + push(ARRAY_OF | LONG); + break; + } + break; + case Opcodes.ANEWARRAY: + String s = item.strVal1; + pop(); + if(s.charAt(0) == '[') + { + push(cw, "[" + s); + } + else + { + push(ARRAY_OF | OBJECT | cw.addType(s)); + } + break; + case Opcodes.CHECKCAST: + s = item.strVal1; + pop(); + if(s.charAt(0) == '[') + { + push(cw, s); + } + else + { + push(OBJECT | cw.addType(s)); + } + break; + // case Opcodes.MULTIANEWARRAY: + default: + pop(arg); + push(cw, item.strVal1); + break; + } +} + +/** + * Merges the input frame of the given basic block with the input and output + * frames of this basic block. Returns true if the input frame of + * the given label has been changed by this operation. + * + * @param cw the ClassWriter to which this label belongs. + * @param frame the basic block whose input frame must be updated. + * @param edge the kind of the {@link Edge} between this label and 'label'. + * See {@link Edge#info}. + * @return true if the input frame of the given label has been + * changed by this operation. + */ +boolean merge(final ClassWriter cw, final Frame frame, final int edge){ + boolean changed = false; + int i, s, dim, kind, t; + + int nLocal = inputLocals.length; + int nStack = inputStack.length; + if(frame.inputLocals == null) + { + frame.inputLocals = new int[nLocal]; + changed = true; + } + + for(i = 0; i < nLocal; ++i) + { + if(outputLocals != null && i < outputLocals.length) + { + s = outputLocals[i]; + if(s == 0) + { + t = inputLocals[i]; + } + else + { + dim = s & DIM; + kind = s & KIND; + if(kind == LOCAL) + { + t = dim + inputLocals[s & VALUE]; + } + else if(kind == STACK) + { + t = dim + inputStack[nStack - (s & VALUE)]; + } + else + { + t = s; + } + } + } + else + { + t = inputLocals[i]; + } + if(initializations != null) + { + t = init(cw, t); + } + changed |= merge(cw, t, frame.inputLocals, i); + } + + if(edge > 0) + { + for(i = 0; i < nLocal; ++i) + { + t = inputLocals[i]; + changed |= merge(cw, t, frame.inputLocals, i); + } + if(frame.inputStack == null) + { + frame.inputStack = new int[1]; + changed = true; + } + changed |= merge(cw, edge, frame.inputStack, 0); + return changed; + } + + int nInputStack = inputStack.length + owner.inputStackTop; + if(frame.inputStack == null) + { + frame.inputStack = new int[nInputStack + outputStackTop]; + changed = true; + } + + for(i = 0; i < nInputStack; ++i) + { + t = inputStack[i]; + if(initializations != null) + { + t = init(cw, t); + } + changed |= merge(cw, t, frame.inputStack, i); + } + for(i = 0; i < outputStackTop; ++i) + { + s = outputStack[i]; + dim = s & DIM; + kind = s & KIND; + if(kind == LOCAL) + { + t = dim + inputLocals[s & VALUE]; + } + else if(kind == STACK) + { + t = dim + inputStack[nStack - (s & VALUE)]; + } + else + { + t = s; + } + if(initializations != null) + { + t = init(cw, t); + } + changed |= merge(cw, t, frame.inputStack, nInputStack + i); + } + return changed; +} + +/** + * Merges the type at the given index in the given type array with the given + * type. Returns true if the type array has been modified by this + * operation. + * + * @param cw the ClassWriter to which this label belongs. + * @param t the type with which the type array element must be merged. + * @param types an array of types. + * @param index the index of the type that must be merged in 'types'. + * @return true if the type array has been modified by this + * operation. + */ +private boolean merge( + final ClassWriter cw, + int t, + final int[] types, + final int index){ + int u = types[index]; + if(u == t) + { + // if the types are equal, merge(u,t)=u, so there is no change + return false; + } + if((t & ~DIM) == NULL) + { + if(u == NULL) + { + return false; + } + t = NULL; + } + if(u == 0) + { + // if types[index] has never been assigned, merge(u,t)=t + types[index] = t; + return true; + } + int v; + if((u & BASE_KIND) == OBJECT || (u & DIM) != 0) + { + // if u is a reference type of any dimension + if(t == NULL) + { + // if t is the NULL type, merge(u,t)=u, so there is no change + return false; + } + else if((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) + { + if((u & BASE_KIND) == OBJECT) + { + // if t is also a reference type, and if u and t have the + // same dimension merge(u,t) = dim(t) | common parent of the + // element types of u and t + v = (t & DIM) | OBJECT + | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE); + } + else + { + // if u and t are array types, but not with the same element + // type, merge(u,t)=java/lang/Object + v = OBJECT | cw.addType("java/lang/Object"); + } + } + else if((t & BASE_KIND) == OBJECT || (t & DIM) != 0) + { + // if t is any other reference or array type, + // merge(u,t)=java/lang/Object + v = OBJECT | cw.addType("java/lang/Object"); + } + else + { + // if t is any other type, merge(u,t)=TOP + v = TOP; + } + } + else if(u == NULL) + { + // if u is the NULL type, merge(u,t)=t, + // or TOP if t is not a reference type + v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP; + } + else + { + // if u is any other type, merge(u,t)=TOP whatever t + v = TOP; + } + if(u != v) + { + types[index] = v; + return true; + } + return false; +} +} diff --git a/src/jvm/clojure/asm/Handler.java b/src/jvm/clojure/asm/Handler.java index 8e98a0b..70d3936 100644 --- a/src/jvm/clojure/asm/Handler.java +++ b/src/jvm/clojure/asm/Handler.java @@ -1,70 +1,70 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * Information about an exception handler block. - * - * @author Eric Bruneton - */ -class Handler{ - -/** - * Beginning of the exception handler's scope (inclusive). - */ -Label start; - -/** - * End of the exception handler's scope (exclusive). - */ -Label end; - -/** - * Beginning of the exception handler's code. - */ -Label handler; - -/** - * Internal name of the type of exceptions handled by this handler, or - * null to catch any exceptions. - */ -String desc; - -/** - * Constant pool index of the internal name of the type of exceptions - * handled by this handler, or 0 to catch any exceptions. - */ -int type; - -/** - * Next exception handler block info. - */ -Handler next; -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * Information about an exception handler block. + * + * @author Eric Bruneton + */ +class Handler{ + +/** + * Beginning of the exception handler's scope (inclusive). + */ +Label start; + +/** + * End of the exception handler's scope (exclusive). + */ +Label end; + +/** + * Beginning of the exception handler's code. + */ +Label handler; + +/** + * Internal name of the type of exceptions handled by this handler, or + * null to catch any exceptions. + */ +String desc; + +/** + * Constant pool index of the internal name of the type of exceptions + * handled by this handler, or 0 to catch any exceptions. + */ +int type; + +/** + * Next exception handler block info. + */ +Handler next; +} diff --git a/src/jvm/clojure/asm/Item.java b/src/jvm/clojure/asm/Item.java index 19923d7..aa12ee7 100644 --- a/src/jvm/clojure/asm/Item.java +++ b/src/jvm/clojure/asm/Item.java @@ -1,258 +1,258 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * A constant pool item. Constant pool items can be created with the 'newXXX' - * methods in the {@link ClassWriter} class. - * - * @author Eric Bruneton - */ -final class Item{ - -/** - * Index of this item in the constant pool. - */ -int index; - -/** - * Type of this constant pool item. A single class is used to represent all - * constant pool item types, in order to minimize the bytecode size of this - * package. The value of this field is one of {@link ClassWriter#INT}, - * {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT}, - * {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8}, - * {@link ClassWriter#STR}, {@link ClassWriter#CLASS}, - * {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD}, - * {@link ClassWriter#METH}, {@link ClassWriter#IMETH}. - *

- * Special Item types are used for Items that are stored in the ClassWriter - * {@link ClassWriter#typeTable}, instead of the constant pool, in order to - * avoid clashes with normal constant pool items in the ClassWriter constant - * pool's hash table. These special item types are - * {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and - * {@link ClassWriter#TYPE_MERGED}. - */ -int type; - -/** - * Value of this item, for an integer item. - */ -int intVal; - -/** - * Value of this item, for a long item. - */ -long longVal; - -/** - * First part of the value of this item, for items that do not hold a - * primitive value. - */ -String strVal1; - -/** - * Second part of the value of this item, for items that do not hold a - * primitive value. - */ -String strVal2; - -/** - * Third part of the value of this item, for items that do not hold a - * primitive value. - */ -String strVal3; - -/** - * The hash code value of this constant pool item. - */ -int hashCode; - -/** - * Link to another constant pool item, used for collision lists in the - * constant pool's hash table. - */ -Item next; - -/** - * Constructs an uninitialized {@link Item}. - */ -Item(){ -} - -/** - * Constructs an uninitialized {@link Item} for constant pool element at - * given position. - * - * @param index index of the item to be constructed. - */ -Item(final int index){ - this.index = index; -} - -/** - * Constructs a copy of the given item. - * - * @param index index of the item to be constructed. - * @param i the item that must be copied into the item to be constructed. - */ -Item(final int index, final Item i){ - this.index = index; - type = i.type; - intVal = i.intVal; - longVal = i.longVal; - strVal1 = i.strVal1; - strVal2 = i.strVal2; - strVal3 = i.strVal3; - hashCode = i.hashCode; -} - -/** - * Sets this item to an integer item. - * - * @param intVal the value of this item. - */ -void set(final int intVal){ - this.type = ClassWriter.INT; - this.intVal = intVal; - this.hashCode = 0x7FFFFFFF & (type + intVal); -} - -/** - * Sets this item to a long item. - * - * @param longVal the value of this item. - */ -void set(final long longVal){ - this.type = ClassWriter.LONG; - this.longVal = longVal; - this.hashCode = 0x7FFFFFFF & (type + (int) longVal); -} - -/** - * Sets this item to a float item. - * - * @param floatVal the value of this item. - */ -void set(final float floatVal){ - this.type = ClassWriter.FLOAT; - this.intVal = Float.floatToRawIntBits(floatVal); - this.hashCode = 0x7FFFFFFF & (type + (int) floatVal); -} - -/** - * Sets this item to a double item. - * - * @param doubleVal the value of this item. - */ -void set(final double doubleVal){ - this.type = ClassWriter.DOUBLE; - this.longVal = Double.doubleToRawLongBits(doubleVal); - this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal); -} - -/** - * Sets this item to an item that do not hold a primitive value. - * - * @param type the type of this item. - * @param strVal1 first part of the value of this item. - * @param strVal2 second part of the value of this item. - * @param strVal3 third part of the value of this item. - */ -void set( - final int type, - final String strVal1, - final String strVal2, - final String strVal3){ - this.type = type; - this.strVal1 = strVal1; - this.strVal2 = strVal2; - this.strVal3 = strVal3; - switch(type) - { - case ClassWriter.UTF8: - case ClassWriter.STR: - case ClassWriter.CLASS: - case ClassWriter.TYPE_NORMAL: - hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); - return; - case ClassWriter.NAME_TYPE: - hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() - * strVal2.hashCode()); - return; - // ClassWriter.FIELD: - // ClassWriter.METH: - // ClassWriter.IMETH: - default: - hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() - * strVal2.hashCode() * strVal3.hashCode()); - } -} - -/** - * Indicates if the given item is equal to this one. - * - * @param i the item to be compared to this one. - * @return true if the given item if equal to this one, - * false otherwise. - */ -boolean isEqualTo(final Item i){ - if(i.type == type) - { - switch(type) - { - case ClassWriter.INT: - case ClassWriter.FLOAT: - return i.intVal == intVal; - case ClassWriter.TYPE_MERGED: - case ClassWriter.LONG: - case ClassWriter.DOUBLE: - return i.longVal == longVal; - case ClassWriter.UTF8: - case ClassWriter.STR: - case ClassWriter.CLASS: - case ClassWriter.TYPE_NORMAL: - return i.strVal1.equals(strVal1); - case ClassWriter.TYPE_UNINIT: - return i.intVal == intVal && i.strVal1.equals(strVal1); - case ClassWriter.NAME_TYPE: - return i.strVal1.equals(strVal1) - && i.strVal2.equals(strVal2); - // ClassWriter.FIELD: - // ClassWriter.METH: - // ClassWriter.IMETH: - default: - return i.strVal1.equals(strVal1) - && i.strVal2.equals(strVal2) - && i.strVal3.equals(strVal3); - } - } - return false; -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * A constant pool item. Constant pool items can be created with the 'newXXX' + * methods in the {@link ClassWriter} class. + * + * @author Eric Bruneton + */ +final class Item{ + +/** + * Index of this item in the constant pool. + */ +int index; + +/** + * Type of this constant pool item. A single class is used to represent all + * constant pool item types, in order to minimize the bytecode size of this + * package. The value of this field is one of {@link ClassWriter#INT}, + * {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT}, + * {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8}, + * {@link ClassWriter#STR}, {@link ClassWriter#CLASS}, + * {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD}, + * {@link ClassWriter#METH}, {@link ClassWriter#IMETH}. + *

+ * Special Item types are used for Items that are stored in the ClassWriter + * {@link ClassWriter#typeTable}, instead of the constant pool, in order to + * avoid clashes with normal constant pool items in the ClassWriter constant + * pool's hash table. These special item types are + * {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and + * {@link ClassWriter#TYPE_MERGED}. + */ +int type; + +/** + * Value of this item, for an integer item. + */ +int intVal; + +/** + * Value of this item, for a long item. + */ +long longVal; + +/** + * First part of the value of this item, for items that do not hold a + * primitive value. + */ +String strVal1; + +/** + * Second part of the value of this item, for items that do not hold a + * primitive value. + */ +String strVal2; + +/** + * Third part of the value of this item, for items that do not hold a + * primitive value. + */ +String strVal3; + +/** + * The hash code value of this constant pool item. + */ +int hashCode; + +/** + * Link to another constant pool item, used for collision lists in the + * constant pool's hash table. + */ +Item next; + +/** + * Constructs an uninitialized {@link Item}. + */ +Item(){ +} + +/** + * Constructs an uninitialized {@link Item} for constant pool element at + * given position. + * + * @param index index of the item to be constructed. + */ +Item(final int index){ + this.index = index; +} + +/** + * Constructs a copy of the given item. + * + * @param index index of the item to be constructed. + * @param i the item that must be copied into the item to be constructed. + */ +Item(final int index, final Item i){ + this.index = index; + type = i.type; + intVal = i.intVal; + longVal = i.longVal; + strVal1 = i.strVal1; + strVal2 = i.strVal2; + strVal3 = i.strVal3; + hashCode = i.hashCode; +} + +/** + * Sets this item to an integer item. + * + * @param intVal the value of this item. + */ +void set(final int intVal){ + this.type = ClassWriter.INT; + this.intVal = intVal; + this.hashCode = 0x7FFFFFFF & (type + intVal); +} + +/** + * Sets this item to a long item. + * + * @param longVal the value of this item. + */ +void set(final long longVal){ + this.type = ClassWriter.LONG; + this.longVal = longVal; + this.hashCode = 0x7FFFFFFF & (type + (int) longVal); +} + +/** + * Sets this item to a float item. + * + * @param floatVal the value of this item. + */ +void set(final float floatVal){ + this.type = ClassWriter.FLOAT; + this.intVal = Float.floatToRawIntBits(floatVal); + this.hashCode = 0x7FFFFFFF & (type + (int) floatVal); +} + +/** + * Sets this item to a double item. + * + * @param doubleVal the value of this item. + */ +void set(final double doubleVal){ + this.type = ClassWriter.DOUBLE; + this.longVal = Double.doubleToRawLongBits(doubleVal); + this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal); +} + +/** + * Sets this item to an item that do not hold a primitive value. + * + * @param type the type of this item. + * @param strVal1 first part of the value of this item. + * @param strVal2 second part of the value of this item. + * @param strVal3 third part of the value of this item. + */ +void set( + final int type, + final String strVal1, + final String strVal2, + final String strVal3){ + this.type = type; + this.strVal1 = strVal1; + this.strVal2 = strVal2; + this.strVal3 = strVal3; + switch(type) + { + case ClassWriter.UTF8: + case ClassWriter.STR: + case ClassWriter.CLASS: + case ClassWriter.TYPE_NORMAL: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); + return; + case ClassWriter.NAME_TYPE: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() + * strVal2.hashCode()); + return; + // ClassWriter.FIELD: + // ClassWriter.METH: + // ClassWriter.IMETH: + default: + hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() + * strVal2.hashCode() * strVal3.hashCode()); + } +} + +/** + * Indicates if the given item is equal to this one. + * + * @param i the item to be compared to this one. + * @return true if the given item if equal to this one, + * false otherwise. + */ +boolean isEqualTo(final Item i){ + if(i.type == type) + { + switch(type) + { + case ClassWriter.INT: + case ClassWriter.FLOAT: + return i.intVal == intVal; + case ClassWriter.TYPE_MERGED: + case ClassWriter.LONG: + case ClassWriter.DOUBLE: + return i.longVal == longVal; + case ClassWriter.UTF8: + case ClassWriter.STR: + case ClassWriter.CLASS: + case ClassWriter.TYPE_NORMAL: + return i.strVal1.equals(strVal1); + case ClassWriter.TYPE_UNINIT: + return i.intVal == intVal && i.strVal1.equals(strVal1); + case ClassWriter.NAME_TYPE: + return i.strVal1.equals(strVal1) + && i.strVal2.equals(strVal2); + // ClassWriter.FIELD: + // ClassWriter.METH: + // ClassWriter.IMETH: + default: + return i.strVal1.equals(strVal1) + && i.strVal2.equals(strVal2) + && i.strVal3.equals(strVal3); + } + } + return false; +} +} diff --git a/src/jvm/clojure/asm/Label.java b/src/jvm/clojure/asm/Label.java index a26020b..4cecc49 100644 --- a/src/jvm/clojure/asm/Label.java +++ b/src/jvm/clojure/asm/Label.java @@ -1,437 +1,437 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * A label represents a position in the bytecode of a method. Labels are used - * for jump, goto, and switch instructions, and for try catch blocks. - * - * @author Eric Bruneton - */ -public class Label{ - -/** - * Indicates if this label is only used for debug attributes. Such a label - * is not the start of a basic block, the target of a jump instruction, or - * an exception handler. It can be safely ignored in control flow graph - * analysis algorithms (for optimization purposes). - */ -final static int DEBUG = 1; - -/** - * Indicates if the position of this label is known. - */ -final static int RESOLVED = 2; - -/** - * Indicates if this label has been updated, after instruction resizing. - */ -final static int RESIZED = 4; - -/** - * Indicates if this basic block has been pushed in the basic block stack. - * See {@link MethodWriter#visitMaxs visitMaxs}. - */ -final static int PUSHED = 8; - -/** - * Indicates if this label is the target of a jump instruction, or the start - * of an exception handler. - */ -final static int TARGET = 16; - -/** - * Indicates if a stack map frame must be stored for this label. - */ -final static int STORE = 32; - -/** - * Indicates if this label corresponds to a reachable basic block. - */ -final static int REACHABLE = 64; - -/** - * Indicates if this basic block ends with a JSR instruction. - */ -final static int JSR = 128; - -/** - * Indicates if this basic block ends with a RET instruction. - */ -final static int RET = 256; - -/** - * Field used to associate user information to a label. - */ -public Object info; - -/** - * Flags that indicate the status of this label. - * - * @see #DEBUG - * @see #RESOLVED - * @see #RESIZED - * @see #PUSHED - * @see #TARGET - * @see #STORE - * @see #REACHABLE - * @see #JSR - * @see #RET - */ -int status; - -/** - * The line number corresponding to this label, if known. - */ -int line; - -/** - * The position of this label in the code, if known. - */ -int position; - -/** - * Number of forward references to this label, times two. - */ -private int referenceCount; - -/** - * Informations about forward references. Each forward reference is - * described by two consecutive integers in this array: the first one is the - * position of the first byte of the bytecode instruction that contains the - * forward reference, while the second is the position of the first byte of - * the forward reference itself. In fact the sign of the first integer - * indicates if this reference uses 2 or 4 bytes, and its absolute value - * gives the position of the bytecode instruction. - */ -private int[] srcAndRefPositions; - -// ------------------------------------------------------------------------ - -/* - * Fields for the control flow and data flow graph analysis algorithms (used - * to compute the maximum stack size or the stack map frames). A control - * flow graph contains one node per "basic block", and one edge per "jump" - * from one basic block to another. Each node (i.e., each basic block) is - * represented by the Label object that corresponds to the first instruction - * of this basic block. Each node also stores the list of its successors in - * the graph, as a linked list of Edge objects. - * - * The control flow analysis algorithms used to compute the maximum stack - * size or the stack map frames are similar and use two steps. The first - * step, during the visit of each instruction, builds information about the - * state of the local variables and the operand stack at the end of each - * basic block, called the "output frame", relatively to the frame - * state at the beginning of the basic block, which is called the "input - * frame", and which is unknown during this step. The second step, - * in {@link MethodWriter#visitMaxs}, is a fix point algorithm that - * computes information about the input frame of each basic block, from the - * input state of the first basic block (known from the method signature), - * and by the using the previously computed relative output frames. - * - * The algorithm used to compute the maximum stack size only computes the - * relative output and absolute input stack heights, while the algorithm - * used to compute stack map frames computes relative output frames and - * absolute input frames. - */ - -/** - * Start of the output stack relatively to the input stack. The exact - * semantics of this field depends on the algorithm that is used. - *

- * When only the maximum stack size is computed, this field is the number of - * elements in the input stack. - *

- * When the stack map frames are completely computed, this field is the - * offset of the first output stack element relatively to the top of the - * input stack. This offset is always negative or null. A null offset means - * that the output stack must be appended to the input stack. A -n offset - * means that the first n output stack elements must replace the top n input - * stack elements, and that the other elements must be appended to the input - * stack. - */ -int inputStackTop; - -/** - * Maximum height reached by the output stack, relatively to the top of the - * input stack. This maximum is always positive or null. - */ -int outputStackMax; - -/** - * Information about the input and output stack map frames of this basic - * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES} - * option is used. - */ -Frame frame; - -/** - * The successor of this label, in the order they are visited. This linked - * list does not include labels used for debug info only. If - * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it - * does not contain successive labels that denote the same bytecode position - * (in this case only the first label appears in this list). - */ -Label successor; - -/** - * The successors of this node in the control flow graph. These successors - * are stored in a linked list of {@link Edge Edge} objects, linked to each - * other by their {@link Edge#next} field. - */ -Edge successors; - -/** - * The next basic block in the basic block stack. This stack is used in the - * main loop of the fix point algorithm used in the second step of the - * control flow analysis algorithms. - * - * @see MethodWriter#visitMaxs - */ -Label next; - -// ------------------------------------------------------------------------ -// Constructor -// ------------------------------------------------------------------------ - -/** - * Constructs a new label. - */ -public Label(){ -} - -/** - * Constructs a new label. - * - * @param debug if this label is only used for debug attributes. - */ -Label(final boolean debug){ - this.status = debug ? DEBUG : 0; -} - -// ------------------------------------------------------------------------ -// Methods to compute offsets and to manage forward references -// ------------------------------------------------------------------------ - -/** - * Returns the offset corresponding to this label. This offset is computed - * from the start of the method's bytecode. This method is intended for - * {@link Attribute} sub classes, and is normally not needed by class - * generators or adapters. - * - * @return the offset corresponding to this label. - * @throws IllegalStateException if this label is not resolved yet. - */ -public int getOffset(){ - if((status & RESOLVED) == 0) - { - throw new IllegalStateException("Label offset position has not been resolved yet"); - } - return position; -} - -/** - * Puts a reference to this label in the bytecode of a method. If the - * position of the label is known, the offset is computed and written - * directly. Otherwise, a null offset is written and a new forward reference - * is declared for this label. - * - * @param owner the code writer that calls this method. - * @param out the bytecode of the method. - * @param source the position of first byte of the bytecode instruction that - * contains this label. - * @param wideOffset true if the reference must be stored in 4 - * bytes, or false if it must be stored with 2 bytes. - * @throws IllegalArgumentException if this label has not been created by - * the given code writer. - */ -void put( - final MethodWriter owner, - final ByteVector out, - final int source, - final boolean wideOffset){ - if((status & RESOLVED) != 0) - { - if(wideOffset) - { - out.putInt(position - source); - } - else - { - out.putShort(position - source); - } - } - else - { - if(wideOffset) - { - addReference(-1 - source, out.length); - out.putInt(-1); - } - else - { - addReference(source, out.length); - out.putShort(-1); - } - } -} - -/** - * Adds a forward reference to this label. This method must be called only - * for a true forward reference, i.e. only if this label is not resolved - * yet. For backward references, the offset of the reference can be, and - * must be, computed and stored directly. - * - * @param sourcePosition the position of the referencing instruction. This - * position will be used to compute the offset of this forward - * reference. - * @param referencePosition the position where the offset for this forward - * reference must be stored. - */ -private void addReference( - final int sourcePosition, - final int referencePosition){ - if(srcAndRefPositions == null) - { - srcAndRefPositions = new int[6]; - } - if(referenceCount >= srcAndRefPositions.length) - { - int[] a = new int[srcAndRefPositions.length + 6]; - System.arraycopy(srcAndRefPositions, - 0, - a, - 0, - srcAndRefPositions.length); - srcAndRefPositions = a; - } - srcAndRefPositions[referenceCount++] = sourcePosition; - srcAndRefPositions[referenceCount++] = referencePosition; -} - -/** - * Resolves all forward references to this label. This method must be called - * when this label is added to the bytecode of the method, i.e. when its - * position becomes known. This method fills in the blanks that where left - * in the bytecode by each forward reference previously added to this label. - * - * @param owner the code writer that calls this method. - * @param position the position of this label in the bytecode. - * @param data the bytecode of the method. - * @return true if a blank that was left for this label was to - * small to store the offset. In such a case the corresponding jump - * instruction is replaced with a pseudo instruction (using unused - * opcodes) using an unsigned two bytes offset. These pseudo - * instructions will need to be replaced with true instructions with - * wider offsets (4 bytes instead of 2). This is done in - * {@link MethodWriter#resizeInstructions}. - * @throws IllegalArgumentException if this label has already been resolved, - * or if it has not been created by the given code writer. - */ -boolean resolve( - final MethodWriter owner, - final int position, - final byte[] data){ - boolean needUpdate = false; - this.status |= RESOLVED; - this.position = position; - int i = 0; - while(i < referenceCount) - { - int source = srcAndRefPositions[i++]; - int reference = srcAndRefPositions[i++]; - int offset; - if(source >= 0) - { - offset = position - source; - if(offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) - { - /* - * changes the opcode of the jump instruction, in order to - * be able to find it later (see resizeInstructions in - * MethodWriter). These temporary opcodes are similar to - * jump instruction opcodes, except that the 2 bytes offset - * is unsigned (and can therefore represent values from 0 to - * 65535, which is sufficient since the size of a method is - * limited to 65535 bytes). - */ - int opcode = data[reference - 1] & 0xFF; - if(opcode <= Opcodes.JSR) - { - // changes IFEQ ... JSR to opcodes 202 to 217 - data[reference - 1] = (byte) (opcode + 49); - } - else - { - // changes IFNULL and IFNONNULL to opcodes 218 and 219 - data[reference - 1] = (byte) (opcode + 20); - } - needUpdate = true; - } - data[reference++] = (byte) (offset >>> 8); - data[reference] = (byte) offset; - } - else - { - offset = position + source + 1; - data[reference++] = (byte) (offset >>> 24); - data[reference++] = (byte) (offset >>> 16); - data[reference++] = (byte) (offset >>> 8); - data[reference] = (byte) offset; - } - } - return needUpdate; -} - -/** - * Returns the first label of the series to which this label belongs. For an - * isolated label or for the first label in a series of successive labels, - * this method returns the label itself. For other labels it returns the - * first label of the series. - * - * @return the first label of the series to which this label belongs. - */ -Label getFirst(){ - return frame == null ? this : frame.owner; -} - -// ------------------------------------------------------------------------ -// Overriden Object methods -// ------------------------------------------------------------------------ - -/** - * Returns a string representation of this label. - * - * @return a string representation of this label. - */ -public String toString(){ - return "L" + System.identityHashCode(this); -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * A label represents a position in the bytecode of a method. Labels are used + * for jump, goto, and switch instructions, and for try catch blocks. + * + * @author Eric Bruneton + */ +public class Label{ + +/** + * Indicates if this label is only used for debug attributes. Such a label + * is not the start of a basic block, the target of a jump instruction, or + * an exception handler. It can be safely ignored in control flow graph + * analysis algorithms (for optimization purposes). + */ +final static int DEBUG = 1; + +/** + * Indicates if the position of this label is known. + */ +final static int RESOLVED = 2; + +/** + * Indicates if this label has been updated, after instruction resizing. + */ +final static int RESIZED = 4; + +/** + * Indicates if this basic block has been pushed in the basic block stack. + * See {@link MethodWriter#visitMaxs visitMaxs}. + */ +final static int PUSHED = 8; + +/** + * Indicates if this label is the target of a jump instruction, or the start + * of an exception handler. + */ +final static int TARGET = 16; + +/** + * Indicates if a stack map frame must be stored for this label. + */ +final static int STORE = 32; + +/** + * Indicates if this label corresponds to a reachable basic block. + */ +final static int REACHABLE = 64; + +/** + * Indicates if this basic block ends with a JSR instruction. + */ +final static int JSR = 128; + +/** + * Indicates if this basic block ends with a RET instruction. + */ +final static int RET = 256; + +/** + * Field used to associate user information to a label. + */ +public Object info; + +/** + * Flags that indicate the status of this label. + * + * @see #DEBUG + * @see #RESOLVED + * @see #RESIZED + * @see #PUSHED + * @see #TARGET + * @see #STORE + * @see #REACHABLE + * @see #JSR + * @see #RET + */ +int status; + +/** + * The line number corresponding to this label, if known. + */ +int line; + +/** + * The position of this label in the code, if known. + */ +int position; + +/** + * Number of forward references to this label, times two. + */ +private int referenceCount; + +/** + * Informations about forward references. Each forward reference is + * described by two consecutive integers in this array: the first one is the + * position of the first byte of the bytecode instruction that contains the + * forward reference, while the second is the position of the first byte of + * the forward reference itself. In fact the sign of the first integer + * indicates if this reference uses 2 or 4 bytes, and its absolute value + * gives the position of the bytecode instruction. + */ +private int[] srcAndRefPositions; + +// ------------------------------------------------------------------------ + +/* + * Fields for the control flow and data flow graph analysis algorithms (used + * to compute the maximum stack size or the stack map frames). A control + * flow graph contains one node per "basic block", and one edge per "jump" + * from one basic block to another. Each node (i.e., each basic block) is + * represented by the Label object that corresponds to the first instruction + * of this basic block. Each node also stores the list of its successors in + * the graph, as a linked list of Edge objects. + * + * The control flow analysis algorithms used to compute the maximum stack + * size or the stack map frames are similar and use two steps. The first + * step, during the visit of each instruction, builds information about the + * state of the local variables and the operand stack at the end of each + * basic block, called the "output frame", relatively to the frame + * state at the beginning of the basic block, which is called the "input + * frame", and which is unknown during this step. The second step, + * in {@link MethodWriter#visitMaxs}, is a fix point algorithm that + * computes information about the input frame of each basic block, from the + * input state of the first basic block (known from the method signature), + * and by the using the previously computed relative output frames. + * + * The algorithm used to compute the maximum stack size only computes the + * relative output and absolute input stack heights, while the algorithm + * used to compute stack map frames computes relative output frames and + * absolute input frames. + */ + +/** + * Start of the output stack relatively to the input stack. The exact + * semantics of this field depends on the algorithm that is used. + *

+ * When only the maximum stack size is computed, this field is the number of + * elements in the input stack. + *

+ * When the stack map frames are completely computed, this field is the + * offset of the first output stack element relatively to the top of the + * input stack. This offset is always negative or null. A null offset means + * that the output stack must be appended to the input stack. A -n offset + * means that the first n output stack elements must replace the top n input + * stack elements, and that the other elements must be appended to the input + * stack. + */ +int inputStackTop; + +/** + * Maximum height reached by the output stack, relatively to the top of the + * input stack. This maximum is always positive or null. + */ +int outputStackMax; + +/** + * Information about the input and output stack map frames of this basic + * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES} + * option is used. + */ +Frame frame; + +/** + * The successor of this label, in the order they are visited. This linked + * list does not include labels used for debug info only. If + * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it + * does not contain successive labels that denote the same bytecode position + * (in this case only the first label appears in this list). + */ +Label successor; + +/** + * The successors of this node in the control flow graph. These successors + * are stored in a linked list of {@link Edge Edge} objects, linked to each + * other by their {@link Edge#next} field. + */ +Edge successors; + +/** + * The next basic block in the basic block stack. This stack is used in the + * main loop of the fix point algorithm used in the second step of the + * control flow analysis algorithms. + * + * @see MethodWriter#visitMaxs + */ +Label next; + +// ------------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------------ + +/** + * Constructs a new label. + */ +public Label(){ +} + +/** + * Constructs a new label. + * + * @param debug if this label is only used for debug attributes. + */ +Label(final boolean debug){ + this.status = debug ? DEBUG : 0; +} + +// ------------------------------------------------------------------------ +// Methods to compute offsets and to manage forward references +// ------------------------------------------------------------------------ + +/** + * Returns the offset corresponding to this label. This offset is computed + * from the start of the method's bytecode. This method is intended for + * {@link Attribute} sub classes, and is normally not needed by class + * generators or adapters. + * + * @return the offset corresponding to this label. + * @throws IllegalStateException if this label is not resolved yet. + */ +public int getOffset(){ + if((status & RESOLVED) == 0) + { + throw new IllegalStateException("Label offset position has not been resolved yet"); + } + return position; +} + +/** + * Puts a reference to this label in the bytecode of a method. If the + * position of the label is known, the offset is computed and written + * directly. Otherwise, a null offset is written and a new forward reference + * is declared for this label. + * + * @param owner the code writer that calls this method. + * @param out the bytecode of the method. + * @param source the position of first byte of the bytecode instruction that + * contains this label. + * @param wideOffset true if the reference must be stored in 4 + * bytes, or false if it must be stored with 2 bytes. + * @throws IllegalArgumentException if this label has not been created by + * the given code writer. + */ +void put( + final MethodWriter owner, + final ByteVector out, + final int source, + final boolean wideOffset){ + if((status & RESOLVED) != 0) + { + if(wideOffset) + { + out.putInt(position - source); + } + else + { + out.putShort(position - source); + } + } + else + { + if(wideOffset) + { + addReference(-1 - source, out.length); + out.putInt(-1); + } + else + { + addReference(source, out.length); + out.putShort(-1); + } + } +} + +/** + * Adds a forward reference to this label. This method must be called only + * for a true forward reference, i.e. only if this label is not resolved + * yet. For backward references, the offset of the reference can be, and + * must be, computed and stored directly. + * + * @param sourcePosition the position of the referencing instruction. This + * position will be used to compute the offset of this forward + * reference. + * @param referencePosition the position where the offset for this forward + * reference must be stored. + */ +private void addReference( + final int sourcePosition, + final int referencePosition){ + if(srcAndRefPositions == null) + { + srcAndRefPositions = new int[6]; + } + if(referenceCount >= srcAndRefPositions.length) + { + int[] a = new int[srcAndRefPositions.length + 6]; + System.arraycopy(srcAndRefPositions, + 0, + a, + 0, + srcAndRefPositions.length); + srcAndRefPositions = a; + } + srcAndRefPositions[referenceCount++] = sourcePosition; + srcAndRefPositions[referenceCount++] = referencePosition; +} + +/** + * Resolves all forward references to this label. This method must be called + * when this label is added to the bytecode of the method, i.e. when its + * position becomes known. This method fills in the blanks that where left + * in the bytecode by each forward reference previously added to this label. + * + * @param owner the code writer that calls this method. + * @param position the position of this label in the bytecode. + * @param data the bytecode of the method. + * @return true if a blank that was left for this label was to + * small to store the offset. In such a case the corresponding jump + * instruction is replaced with a pseudo instruction (using unused + * opcodes) using an unsigned two bytes offset. These pseudo + * instructions will need to be replaced with true instructions with + * wider offsets (4 bytes instead of 2). This is done in + * {@link MethodWriter#resizeInstructions}. + * @throws IllegalArgumentException if this label has already been resolved, + * or if it has not been created by the given code writer. + */ +boolean resolve( + final MethodWriter owner, + final int position, + final byte[] data){ + boolean needUpdate = false; + this.status |= RESOLVED; + this.position = position; + int i = 0; + while(i < referenceCount) + { + int source = srcAndRefPositions[i++]; + int reference = srcAndRefPositions[i++]; + int offset; + if(source >= 0) + { + offset = position - source; + if(offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) + { + /* + * changes the opcode of the jump instruction, in order to + * be able to find it later (see resizeInstructions in + * MethodWriter). These temporary opcodes are similar to + * jump instruction opcodes, except that the 2 bytes offset + * is unsigned (and can therefore represent values from 0 to + * 65535, which is sufficient since the size of a method is + * limited to 65535 bytes). + */ + int opcode = data[reference - 1] & 0xFF; + if(opcode <= Opcodes.JSR) + { + // changes IFEQ ... JSR to opcodes 202 to 217 + data[reference - 1] = (byte) (opcode + 49); + } + else + { + // changes IFNULL and IFNONNULL to opcodes 218 and 219 + data[reference - 1] = (byte) (opcode + 20); + } + needUpdate = true; + } + data[reference++] = (byte) (offset >>> 8); + data[reference] = (byte) offset; + } + else + { + offset = position + source + 1; + data[reference++] = (byte) (offset >>> 24); + data[reference++] = (byte) (offset >>> 16); + data[reference++] = (byte) (offset >>> 8); + data[reference] = (byte) offset; + } + } + return needUpdate; +} + +/** + * Returns the first label of the series to which this label belongs. For an + * isolated label or for the first label in a series of successive labels, + * this method returns the label itself. For other labels it returns the + * first label of the series. + * + * @return the first label of the series to which this label belongs. + */ +Label getFirst(){ + return frame == null ? this : frame.owner; +} + +// ------------------------------------------------------------------------ +// Overriden Object methods +// ------------------------------------------------------------------------ + +/** + * Returns a string representation of this label. + * + * @return a string representation of this label. + */ +public String toString(){ + return "L" + System.identityHashCode(this); +} +} diff --git a/src/jvm/clojure/asm/MethodAdapter.java b/src/jvm/clojure/asm/MethodAdapter.java index 5970862..dc87ed0 100644 --- a/src/jvm/clojure/asm/MethodAdapter.java +++ b/src/jvm/clojure/asm/MethodAdapter.java @@ -1,186 +1,186 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * An empty {@link MethodVisitor} that delegates to another - * {@link MethodVisitor}. This class can be used as a super class to quickly - * implement usefull method adapter classes, just by overriding the necessary - * methods. - * - * @author Eric Bruneton - */ -public class MethodAdapter implements MethodVisitor{ - -/** - * The {@link MethodVisitor} to which this adapter delegates calls. - */ -protected MethodVisitor mv; - -/** - * Constructs a new {@link MethodAdapter} object. - * - * @param mv the code visitor to which this adapter must delegate calls. - */ -public MethodAdapter(final MethodVisitor mv){ - this.mv = mv; -} - -public AnnotationVisitor visitAnnotationDefault(){ - return mv.visitAnnotationDefault(); -} - -public AnnotationVisitor visitAnnotation( - final String desc, - final boolean visible){ - return mv.visitAnnotation(desc, visible); -} - -public AnnotationVisitor visitParameterAnnotation( - final int parameter, - final String desc, - final boolean visible){ - return mv.visitParameterAnnotation(parameter, desc, visible); -} - -public void visitAttribute(final Attribute attr){ - mv.visitAttribute(attr); -} - -public void visitCode(){ - mv.visitCode(); -} - -public void visitFrame( - final int type, - final int nLocal, - final Object[] local, - final int nStack, - final Object[] stack){ - mv.visitFrame(type, nLocal, local, nStack, stack); -} - -public void visitInsn(final int opcode){ - mv.visitInsn(opcode); -} - -public void visitIntInsn(final int opcode, final int operand){ - mv.visitIntInsn(opcode, operand); -} - -public void visitVarInsn(final int opcode, final int var){ - mv.visitVarInsn(opcode, var); -} - -public void visitTypeInsn(final int opcode, final String desc){ - mv.visitTypeInsn(opcode, desc); -} - -public void visitFieldInsn( - final int opcode, - final String owner, - final String name, - final String desc){ - mv.visitFieldInsn(opcode, owner, name, desc); -} - -public void visitMethodInsn( - final int opcode, - final String owner, - final String name, - final String desc){ - mv.visitMethodInsn(opcode, owner, name, desc); -} - -public void visitJumpInsn(final int opcode, final Label label){ - mv.visitJumpInsn(opcode, label); -} - -public void visitLabel(final Label label){ - mv.visitLabel(label); -} - -public void visitLdcInsn(final Object cst){ - mv.visitLdcInsn(cst); -} - -public void visitIincInsn(final int var, final int increment){ - mv.visitIincInsn(var, increment); -} - -public void visitTableSwitchInsn( - final int min, - final int max, - final Label dflt, - final Label labels[]){ - mv.visitTableSwitchInsn(min, max, dflt, labels); -} - -public void visitLookupSwitchInsn( - final Label dflt, - final int keys[], - final Label labels[]){ - mv.visitLookupSwitchInsn(dflt, keys, labels); -} - -public void visitMultiANewArrayInsn(final String desc, final int dims){ - mv.visitMultiANewArrayInsn(desc, dims); -} - -public void visitTryCatchBlock( - final Label start, - final Label end, - final Label handler, - final String type){ - mv.visitTryCatchBlock(start, end, handler, type); -} - -public void visitLocalVariable( - final String name, - final String desc, - final String signature, - final Label start, - final Label end, - final int index){ - mv.visitLocalVariable(name, desc, signature, start, end, index); -} - -public void visitLineNumber(final int line, final Label start){ - mv.visitLineNumber(line, start); -} - -public void visitMaxs(final int maxStack, final int maxLocals){ - mv.visitMaxs(maxStack, maxLocals); -} - -public void visitEnd(){ - mv.visitEnd(); -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * An empty {@link MethodVisitor} that delegates to another + * {@link MethodVisitor}. This class can be used as a super class to quickly + * implement usefull method adapter classes, just by overriding the necessary + * methods. + * + * @author Eric Bruneton + */ +public class MethodAdapter implements MethodVisitor{ + +/** + * The {@link MethodVisitor} to which this adapter delegates calls. + */ +protected MethodVisitor mv; + +/** + * Constructs a new {@link MethodAdapter} object. + * + * @param mv the code visitor to which this adapter must delegate calls. + */ +public MethodAdapter(final MethodVisitor mv){ + this.mv = mv; +} + +public AnnotationVisitor visitAnnotationDefault(){ + return mv.visitAnnotationDefault(); +} + +public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible){ + return mv.visitAnnotation(desc, visible); +} + +public AnnotationVisitor visitParameterAnnotation( + final int parameter, + final String desc, + final boolean visible){ + return mv.visitParameterAnnotation(parameter, desc, visible); +} + +public void visitAttribute(final Attribute attr){ + mv.visitAttribute(attr); +} + +public void visitCode(){ + mv.visitCode(); +} + +public void visitFrame( + final int type, + final int nLocal, + final Object[] local, + final int nStack, + final Object[] stack){ + mv.visitFrame(type, nLocal, local, nStack, stack); +} + +public void visitInsn(final int opcode){ + mv.visitInsn(opcode); +} + +public void visitIntInsn(final int opcode, final int operand){ + mv.visitIntInsn(opcode, operand); +} + +public void visitVarInsn(final int opcode, final int var){ + mv.visitVarInsn(opcode, var); +} + +public void visitTypeInsn(final int opcode, final String desc){ + mv.visitTypeInsn(opcode, desc); +} + +public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc){ + mv.visitFieldInsn(opcode, owner, name, desc); +} + +public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc){ + mv.visitMethodInsn(opcode, owner, name, desc); +} + +public void visitJumpInsn(final int opcode, final Label label){ + mv.visitJumpInsn(opcode, label); +} + +public void visitLabel(final Label label){ + mv.visitLabel(label); +} + +public void visitLdcInsn(final Object cst){ + mv.visitLdcInsn(cst); +} + +public void visitIincInsn(final int var, final int increment){ + mv.visitIincInsn(var, increment); +} + +public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label labels[]){ + mv.visitTableSwitchInsn(min, max, dflt, labels); +} + +public void visitLookupSwitchInsn( + final Label dflt, + final int keys[], + final Label labels[]){ + mv.visitLookupSwitchInsn(dflt, keys, labels); +} + +public void visitMultiANewArrayInsn(final String desc, final int dims){ + mv.visitMultiANewArrayInsn(desc, dims); +} + +public void visitTryCatchBlock( + final Label start, + final Label end, + final Label handler, + final String type){ + mv.visitTryCatchBlock(start, end, handler, type); +} + +public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index){ + mv.visitLocalVariable(name, desc, signature, start, end, index); +} + +public void visitLineNumber(final int line, final Label start){ + mv.visitLineNumber(line, start); +} + +public void visitMaxs(final int maxStack, final int maxLocals){ + mv.visitMaxs(maxStack, maxLocals); +} + +public void visitEnd(){ + mv.visitEnd(); +} +} diff --git a/src/jvm/clojure/asm/MethodVisitor.java b/src/jvm/clojure/asm/MethodVisitor.java index f77a92d..8894b30 100644 --- a/src/jvm/clojure/asm/MethodVisitor.java +++ b/src/jvm/clojure/asm/MethodVisitor.java @@ -1,396 +1,396 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * A visitor to visit a Java method. The methods of this interface must be - * called in the following order: [ visitAnnotationDefault ] ( - * visitAnnotation | visitParameterAnnotation | - * visitAttribute )* [ visitCode ( visitFrame | - * visitXInsn | visitLabel | visitTryCatchBlock | - * visitLocalVariable | visitLineNumber)* visitMaxs ] - * visitEnd. In addition, the visitXInsn - * and visitLabel methods must be called in the sequential order of - * the bytecode instructions of the visited code, visitTryCatchBlock - * must be called before the labels passed as arguments have been - * visited, and the visitLocalVariable and visitLineNumber - * methods must be called after the labels passed as arguments have been - * visited. - * - * @author Eric Bruneton - */ -public interface MethodVisitor{ - -// ------------------------------------------------------------------------- -// Annotations and non standard attributes -// ------------------------------------------------------------------------- - -/** - * Visits the default value of this annotation interface method. - * - * @return a visitor to the visit the actual default value of this - * annotation interface method, or null if this visitor - * is not interested in visiting this default value. The 'name' - * parameters passed to the methods of this annotation visitor are - * ignored. Moreover, exacly one visit method must be called on this - * annotation visitor, followed by visitEnd. - */ -AnnotationVisitor visitAnnotationDefault(); - -/** - * Visits an annotation of this method. - * - * @param desc the class descriptor of the annotation class. - * @param visible true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ -AnnotationVisitor visitAnnotation(String desc, boolean visible); - -/** - * Visits an annotation of a parameter this method. - * - * @param parameter the parameter index. - * @param desc the class descriptor of the annotation class. - * @param visible true if the annotation is visible at runtime. - * @return a visitor to visit the annotation values, or null if - * this visitor is not interested in visiting this annotation. - */ -AnnotationVisitor visitParameterAnnotation( - int parameter, - String desc, - boolean visible); - -/** - * Visits a non standard attribute of this method. - * - * @param attr an attribute. - */ -void visitAttribute(Attribute attr); - -/** - * Starts the visit of the method's code, if any (i.e. non abstract method). - */ -void visitCode(); - -/** - * Visits the current state of the local variables and operand stack - * elements. This method must(*) be called just before any - * instruction i that follows an unconditionnal branch instruction - * such as GOTO or THROW, that is the target of a jump instruction, or that - * starts an exception handler block. The visited types must describe the - * values of the local variables and of the operand stack elements just - * before i is executed.

(*) this is mandatory only - * for classes whose version is greater than or equal to - * {@link Opcodes#V1_6 V1_6}.

Packed frames are basically - * "deltas" from the state of the previous frame (very first frame is - * implicitly defined by the method's parameters and access flags):

    - *
  • {@link Opcodes#F_SAME} representing frame with exactly the same - * locals as the previous frame and with the empty stack.
  • {@link Opcodes#F_SAME1} - * representing frame with exactly the same locals as the previous frame and - * with single value on the stack (nStack is 1 and - * stack[0] contains value for the type of the stack item).
  • - *
  • {@link Opcodes#F_APPEND} representing frame with current locals are - * the same as the locals in the previous frame, except that additional - * locals are defined (nLocal is 1, 2 or 3 and - * local elements contains values representing added types).
  • - *
  • {@link Opcodes#F_CHOP} representing frame with current locals are - * the same as the locals in the previous frame, except that the last 1-3 - * locals are absent and with the empty stack (nLocals is 1, - * 2 or 3).
  • {@link Opcodes#F_FULL} representing complete frame - * data.
- * - * @param type the type of this stack map frame. Must be - * {@link Opcodes#F_NEW} for expanded frames, or - * {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, - * {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or - * {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed - * frames. - * @param nLocal the number of local variables in the visited frame. - * @param local the local variable types in this frame. This array must not - * be modified. Primitive types are represented by - * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, - * {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, - * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or - * {@link Opcodes#UNINITIALIZED_THIS} (long and double are - * represented by a single element). Reference types are represented - * by String objects (representing internal names, or type - * descriptors for array types), and uninitialized types by Label - * objects (this label designates the NEW instruction that created - * this uninitialized value). - * @param nStack the number of operand stack elements in the visited frame. - * @param stack the operand stack types in this frame. This array must not - * be modified. Its content has the same format as the "local" array. - */ -void visitFrame( - int type, - int nLocal, - Object[] local, - int nStack, - Object[] stack); - -// ------------------------------------------------------------------------- -// Normal instructions -// ------------------------------------------------------------------------- - -/** - * Visits a zero operand instruction. - * - * @param opcode the opcode of the instruction to be visited. This opcode is - * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, - * ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, - * FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, LALOAD, FALOAD, - * DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE, - * DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP, - * DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, IADD, LADD, FADD, - * DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, - * FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, - * LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, - * I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, - * I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, - * FRETURN, DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, - * MONITORENTER, or MONITOREXIT. - */ -void visitInsn(int opcode); - -/** - * Visits an instruction with a single int operand. - * - * @param opcode the opcode of the instruction to be visited. This opcode is - * either BIPUSH, SIPUSH or NEWARRAY. - * @param operand the operand of the instruction to be visited.
When - * opcode is BIPUSH, operand value should be between Byte.MIN_VALUE - * and Byte.MAX_VALUE.
When opcode is SIPUSH, operand value - * should be between Short.MIN_VALUE and Short.MAX_VALUE.
When - * opcode is NEWARRAY, operand value should be one of - * {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR}, - * {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE}, - * {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT}, - * {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}. - */ -void visitIntInsn(int opcode, int operand); - -/** - * Visits a local variable instruction. A local variable instruction is an - * instruction that loads or stores the value of a local variable. - * - * @param opcode the opcode of the local variable instruction to be visited. - * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, - * LSTORE, FSTORE, DSTORE, ASTORE or RET. - * @param var the operand of the instruction to be visited. This operand is - * the index of a local variable. - */ -void visitVarInsn(int opcode, int var); - -/** - * Visits a type instruction. A type instruction is an instruction that - * takes a type descriptor as parameter. - * - * @param opcode the opcode of the type instruction to be visited. This - * opcode is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. - * @param desc the operand of the instruction to be visited. This operand is - * must be a fully qualified class name in internal form, or the type - * descriptor of an array type (see {@link Type Type}). - */ -void visitTypeInsn(int opcode, String desc); - -/** - * Visits a field instruction. A field instruction is an instruction that - * loads or stores the value of a field of an object. - * - * @param opcode the opcode of the type instruction to be visited. This - * opcode is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. - * @param owner the internal name of the field's owner class (see {@link - * Type#getInternalName() getInternalName}). - * @param name the field's name. - * @param desc the field's descriptor (see {@link Type Type}). - */ -void visitFieldInsn(int opcode, String owner, String name, String desc); - -/** - * Visits a method instruction. A method instruction is an instruction that - * invokes a method. - * - * @param opcode the opcode of the type instruction to be visited. This - * opcode is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or - * INVOKEINTERFACE. - * @param owner the internal name of the method's owner class (see {@link - * Type#getInternalName() getInternalName}). - * @param name the method's name. - * @param desc the method's descriptor (see {@link Type Type}). - */ -void visitMethodInsn(int opcode, String owner, String name, String desc); - -/** - * Visits a jump instruction. A jump instruction is an instruction that may - * jump to another instruction. - * - * @param opcode the opcode of the type instruction to be visited. This - * opcode is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, - * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, - * IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. - * @param label the operand of the instruction to be visited. This operand - * is a label that designates the instruction to which the jump - * instruction may jump. - */ -void visitJumpInsn(int opcode, Label label); - -/** - * Visits a label. A label designates the instruction that will be visited - * just after it. - * - * @param label a {@link Label Label} object. - */ -void visitLabel(Label label); - -// ------------------------------------------------------------------------- -// Special instructions -// ------------------------------------------------------------------------- - -/** - * Visits a LDC instruction. - * - * @param cst the constant to be loaded on the stack. This parameter must be - * a non null {@link Integer}, a {@link Float}, a {@link Long}, a - * {@link Double} a {@link String} (or a {@link Type} for - * .class constants, for classes whose version is 49.0 or - * more). - */ -void visitLdcInsn(Object cst); - -/** - * Visits an IINC instruction. - * - * @param var index of the local variable to be incremented. - * @param increment amount to increment the local variable by. - */ -void visitIincInsn(int var, int increment); - -/** - * Visits a TABLESWITCH instruction. - * - * @param min the minimum key value. - * @param max the maximum key value. - * @param dflt beginning of the default handler block. - * @param labels beginnings of the handler blocks. labels[i] is - * the beginning of the handler block for the min + i key. - */ -void visitTableSwitchInsn(int min, int max, Label dflt, Label labels[]); - -/** - * Visits a LOOKUPSWITCH instruction. - * - * @param dflt beginning of the default handler block. - * @param keys the values of the keys. - * @param labels beginnings of the handler blocks. labels[i] is - * the beginning of the handler block for the keys[i] key. - */ -void visitLookupSwitchInsn(Label dflt, int keys[], Label labels[]); - -/** - * Visits a MULTIANEWARRAY instruction. - * - * @param desc an array type descriptor (see {@link Type Type}). - * @param dims number of dimensions of the array to allocate. - */ -void visitMultiANewArrayInsn(String desc, int dims); - -// ------------------------------------------------------------------------- -// Exceptions table entries, debug information, max stack and max locals -// ------------------------------------------------------------------------- - -/** - * Visits a try catch block. - * - * @param start beginning of the exception handler's scope (inclusive). - * @param end end of the exception handler's scope (exclusive). - * @param handler beginning of the exception handler's code. - * @param type internal name of the type of exceptions handled by the - * handler, or null to catch any exceptions (for "finally" - * blocks). - * @throws IllegalArgumentException if one of the labels has already been - * visited by this visitor (by the {@link #visitLabel visitLabel} - * method). - */ -void visitTryCatchBlock(Label start, Label end, Label handler, String type); - -/** - * Visits a local variable declaration. - * - * @param name the name of a local variable. - * @param desc the type descriptor of this local variable. - * @param signature the type signature of this local variable. May be - * null if the local variable type does not use generic - * types. - * @param start the first instruction corresponding to the scope of this - * local variable (inclusive). - * @param end the last instruction corresponding to the scope of this local - * variable (exclusive). - * @param index the local variable's index. - * @throws IllegalArgumentException if one of the labels has not already - * been visited by this visitor (by the - * {@link #visitLabel visitLabel} method). - */ -void visitLocalVariable( - String name, - String desc, - String signature, - Label start, - Label end, - int index); - -/** - * Visits a line number declaration. - * - * @param line a line number. This number refers to the source file from - * which the class was compiled. - * @param start the first instruction corresponding to this line number. - * @throws IllegalArgumentException if start has not already been - * visited by this visitor (by the {@link #visitLabel visitLabel} - * method). - */ -void visitLineNumber(int line, Label start); - -/** - * Visits the maximum stack size and the maximum number of local variables - * of the method. - * - * @param maxStack maximum stack size of the method. - * @param maxLocals maximum number of local variables for the method. - */ -void visitMaxs(int maxStack, int maxLocals); - -/** - * Visits the end of the method. This method, which is the last one to be - * called, is used to inform the visitor that all the annotations and - * attributes of the method have been visited. - */ -void visitEnd(); -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * A visitor to visit a Java method. The methods of this interface must be + * called in the following order: [ visitAnnotationDefault ] ( + * visitAnnotation | visitParameterAnnotation | + * visitAttribute )* [ visitCode ( visitFrame | + * visitXInsn | visitLabel | visitTryCatchBlock | + * visitLocalVariable | visitLineNumber)* visitMaxs ] + * visitEnd. In addition, the visitXInsn + * and visitLabel methods must be called in the sequential order of + * the bytecode instructions of the visited code, visitTryCatchBlock + * must be called before the labels passed as arguments have been + * visited, and the visitLocalVariable and visitLineNumber + * methods must be called after the labels passed as arguments have been + * visited. + * + * @author Eric Bruneton + */ +public interface MethodVisitor{ + +// ------------------------------------------------------------------------- +// Annotations and non standard attributes +// ------------------------------------------------------------------------- + +/** + * Visits the default value of this annotation interface method. + * + * @return a visitor to the visit the actual default value of this + * annotation interface method, or null if this visitor + * is not interested in visiting this default value. The 'name' + * parameters passed to the methods of this annotation visitor are + * ignored. Moreover, exacly one visit method must be called on this + * annotation visitor, followed by visitEnd. + */ +AnnotationVisitor visitAnnotationDefault(); + +/** + * Visits an annotation of this method. + * + * @param desc the class descriptor of the annotation class. + * @param visible true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ +AnnotationVisitor visitAnnotation(String desc, boolean visible); + +/** + * Visits an annotation of a parameter this method. + * + * @param parameter the parameter index. + * @param desc the class descriptor of the annotation class. + * @param visible true if the annotation is visible at runtime. + * @return a visitor to visit the annotation values, or null if + * this visitor is not interested in visiting this annotation. + */ +AnnotationVisitor visitParameterAnnotation( + int parameter, + String desc, + boolean visible); + +/** + * Visits a non standard attribute of this method. + * + * @param attr an attribute. + */ +void visitAttribute(Attribute attr); + +/** + * Starts the visit of the method's code, if any (i.e. non abstract method). + */ +void visitCode(); + +/** + * Visits the current state of the local variables and operand stack + * elements. This method must(*) be called just before any + * instruction i that follows an unconditionnal branch instruction + * such as GOTO or THROW, that is the target of a jump instruction, or that + * starts an exception handler block. The visited types must describe the + * values of the local variables and of the operand stack elements just + * before i is executed.

(*) this is mandatory only + * for classes whose version is greater than or equal to + * {@link Opcodes#V1_6 V1_6}.

Packed frames are basically + * "deltas" from the state of the previous frame (very first frame is + * implicitly defined by the method's parameters and access flags):
    + *
  • {@link Opcodes#F_SAME} representing frame with exactly the same + * locals as the previous frame and with the empty stack.
  • {@link Opcodes#F_SAME1} + * representing frame with exactly the same locals as the previous frame and + * with single value on the stack (nStack is 1 and + * stack[0] contains value for the type of the stack item).
  • + *
  • {@link Opcodes#F_APPEND} representing frame with current locals are + * the same as the locals in the previous frame, except that additional + * locals are defined (nLocal is 1, 2 or 3 and + * local elements contains values representing added types).
  • + *
  • {@link Opcodes#F_CHOP} representing frame with current locals are + * the same as the locals in the previous frame, except that the last 1-3 + * locals are absent and with the empty stack (nLocals is 1, + * 2 or 3).
  • {@link Opcodes#F_FULL} representing complete frame + * data.
+ * + * @param type the type of this stack map frame. Must be + * {@link Opcodes#F_NEW} for expanded frames, or + * {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, + * {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or + * {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed + * frames. + * @param nLocal the number of local variables in the visited frame. + * @param local the local variable types in this frame. This array must not + * be modified. Primitive types are represented by + * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, + * {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or + * {@link Opcodes#UNINITIALIZED_THIS} (long and double are + * represented by a single element). Reference types are represented + * by String objects (representing internal names, or type + * descriptors for array types), and uninitialized types by Label + * objects (this label designates the NEW instruction that created + * this uninitialized value). + * @param nStack the number of operand stack elements in the visited frame. + * @param stack the operand stack types in this frame. This array must not + * be modified. Its content has the same format as the "local" array. + */ +void visitFrame( + int type, + int nLocal, + Object[] local, + int nStack, + Object[] stack); + +// ------------------------------------------------------------------------- +// Normal instructions +// ------------------------------------------------------------------------- + +/** + * Visits a zero operand instruction. + * + * @param opcode the opcode of the instruction to be visited. This opcode is + * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, + * ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, + * FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, LALOAD, FALOAD, + * DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE, + * DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP, + * DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, IADD, LADD, FADD, + * DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, + * FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, + * LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, + * I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, + * I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, + * FRETURN, DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, + * MONITORENTER, or MONITOREXIT. + */ +void visitInsn(int opcode); + +/** + * Visits an instruction with a single int operand. + * + * @param opcode the opcode of the instruction to be visited. This opcode is + * either BIPUSH, SIPUSH or NEWARRAY. + * @param operand the operand of the instruction to be visited.
When + * opcode is BIPUSH, operand value should be between Byte.MIN_VALUE + * and Byte.MAX_VALUE.
When opcode is SIPUSH, operand value + * should be between Short.MIN_VALUE and Short.MAX_VALUE.
When + * opcode is NEWARRAY, operand value should be one of + * {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR}, + * {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE}, + * {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT}, + * {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}. + */ +void visitIntInsn(int opcode, int operand); + +/** + * Visits a local variable instruction. A local variable instruction is an + * instruction that loads or stores the value of a local variable. + * + * @param opcode the opcode of the local variable instruction to be visited. + * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, + * LSTORE, FSTORE, DSTORE, ASTORE or RET. + * @param var the operand of the instruction to be visited. This operand is + * the index of a local variable. + */ +void visitVarInsn(int opcode, int var); + +/** + * Visits a type instruction. A type instruction is an instruction that + * takes a type descriptor as parameter. + * + * @param opcode the opcode of the type instruction to be visited. This + * opcode is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. + * @param desc the operand of the instruction to be visited. This operand is + * must be a fully qualified class name in internal form, or the type + * descriptor of an array type (see {@link Type Type}). + */ +void visitTypeInsn(int opcode, String desc); + +/** + * Visits a field instruction. A field instruction is an instruction that + * loads or stores the value of a field of an object. + * + * @param opcode the opcode of the type instruction to be visited. This + * opcode is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. + * @param owner the internal name of the field's owner class (see {@link + * Type#getInternalName() getInternalName}). + * @param name the field's name. + * @param desc the field's descriptor (see {@link Type Type}). + */ +void visitFieldInsn(int opcode, String owner, String name, String desc); + +/** + * Visits a method instruction. A method instruction is an instruction that + * invokes a method. + * + * @param opcode the opcode of the type instruction to be visited. This + * opcode is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or + * INVOKEINTERFACE. + * @param owner the internal name of the method's owner class (see {@link + * Type#getInternalName() getInternalName}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + */ +void visitMethodInsn(int opcode, String owner, String name, String desc); + +/** + * Visits a jump instruction. A jump instruction is an instruction that may + * jump to another instruction. + * + * @param opcode the opcode of the type instruction to be visited. This + * opcode is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, + * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, + * IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. + * @param label the operand of the instruction to be visited. This operand + * is a label that designates the instruction to which the jump + * instruction may jump. + */ +void visitJumpInsn(int opcode, Label label); + +/** + * Visits a label. A label designates the instruction that will be visited + * just after it. + * + * @param label a {@link Label Label} object. + */ +void visitLabel(Label label); + +// ------------------------------------------------------------------------- +// Special instructions +// ------------------------------------------------------------------------- + +/** + * Visits a LDC instruction. + * + * @param cst the constant to be loaded on the stack. This parameter must be + * a non null {@link Integer}, a {@link Float}, a {@link Long}, a + * {@link Double} a {@link String} (or a {@link Type} for + * .class constants, for classes whose version is 49.0 or + * more). + */ +void visitLdcInsn(Object cst); + +/** + * Visits an IINC instruction. + * + * @param var index of the local variable to be incremented. + * @param increment amount to increment the local variable by. + */ +void visitIincInsn(int var, int increment); + +/** + * Visits a TABLESWITCH instruction. + * + * @param min the minimum key value. + * @param max the maximum key value. + * @param dflt beginning of the default handler block. + * @param labels beginnings of the handler blocks. labels[i] is + * the beginning of the handler block for the min + i key. + */ +void visitTableSwitchInsn(int min, int max, Label dflt, Label labels[]); + +/** + * Visits a LOOKUPSWITCH instruction. + * + * @param dflt beginning of the default handler block. + * @param keys the values of the keys. + * @param labels beginnings of the handler blocks. labels[i] is + * the beginning of the handler block for the keys[i] key. + */ +void visitLookupSwitchInsn(Label dflt, int keys[], Label labels[]); + +/** + * Visits a MULTIANEWARRAY instruction. + * + * @param desc an array type descriptor (see {@link Type Type}). + * @param dims number of dimensions of the array to allocate. + */ +void visitMultiANewArrayInsn(String desc, int dims); + +// ------------------------------------------------------------------------- +// Exceptions table entries, debug information, max stack and max locals +// ------------------------------------------------------------------------- + +/** + * Visits a try catch block. + * + * @param start beginning of the exception handler's scope (inclusive). + * @param end end of the exception handler's scope (exclusive). + * @param handler beginning of the exception handler's code. + * @param type internal name of the type of exceptions handled by the + * handler, or null to catch any exceptions (for "finally" + * blocks). + * @throws IllegalArgumentException if one of the labels has already been + * visited by this visitor (by the {@link #visitLabel visitLabel} + * method). + */ +void visitTryCatchBlock(Label start, Label end, Label handler, String type); + +/** + * Visits a local variable declaration. + * + * @param name the name of a local variable. + * @param desc the type descriptor of this local variable. + * @param signature the type signature of this local variable. May be + * null if the local variable type does not use generic + * types. + * @param start the first instruction corresponding to the scope of this + * local variable (inclusive). + * @param end the last instruction corresponding to the scope of this local + * variable (exclusive). + * @param index the local variable's index. + * @throws IllegalArgumentException if one of the labels has not already + * been visited by this visitor (by the + * {@link #visitLabel visitLabel} method). + */ +void visitLocalVariable( + String name, + String desc, + String signature, + Label start, + Label end, + int index); + +/** + * Visits a line number declaration. + * + * @param line a line number. This number refers to the source file from + * which the class was compiled. + * @param start the first instruction corresponding to this line number. + * @throws IllegalArgumentException if start has not already been + * visited by this visitor (by the {@link #visitLabel visitLabel} + * method). + */ +void visitLineNumber(int line, Label start); + +/** + * Visits the maximum stack size and the maximum number of local variables + * of the method. + * + * @param maxStack maximum stack size of the method. + * @param maxLocals maximum number of local variables for the method. + */ +void visitMaxs(int maxStack, int maxLocals); + +/** + * Visits the end of the method. This method, which is the last one to be + * called, is used to inform the visitor that all the annotations and + * attributes of the method have been visited. + */ +void visitEnd(); +} diff --git a/src/jvm/clojure/asm/MethodWriter.java b/src/jvm/clojure/asm/MethodWriter.java index 1923973..3326279 100644 --- a/src/jvm/clojure/asm/MethodWriter.java +++ b/src/jvm/clojure/asm/MethodWriter.java @@ -1,3029 +1,3029 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * A {@link MethodVisitor} that generates methods in bytecode form. Each visit - * method of this class appends the bytecode corresponding to the visited - * instruction to a byte vector, in the order these methods are called. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -class MethodWriter implements MethodVisitor{ - -/** - * Pseudo access flag used to denote constructors. - */ -final static int ACC_CONSTRUCTOR = 262144; - -/** - * Frame has exactly the same locals as the previous stack map frame and - * number of stack items is zero. - */ -final static int SAME_FRAME = 0; // to 63 (0-3f) - -/** - * Frame has exactly the same locals as the previous stack map frame and - * number of stack items is 1 - */ -final static int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f) - -/** - * Reserved for future use - */ -final static int RESERVED = 128; - -/** - * Frame has exactly the same locals as the previous stack map frame and - * number of stack items is 1. Offset is bigger then 63; - */ -final static int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7 - -/** - * Frame where current locals are the same as the locals in the previous - * frame, except that the k last locals are absent. The value of k is given - * by the formula 251-frame_type. - */ -final static int CHOP_FRAME = 248; // to 250 (f8-fA) - -/** - * Frame has exactly the same locals as the previous stack map frame and - * number of stack items is zero. Offset is bigger then 63; - */ -final static int SAME_FRAME_EXTENDED = 251; // fb - -/** - * Frame where current locals are the same as the locals in the previous - * frame, except that k additional locals are defined. The value of k is - * given by the formula frame_type-251. - */ -final static int APPEND_FRAME = 252; // to 254 // fc-fe - -/** - * Full frame - */ -final static int FULL_FRAME = 255; // ff - -/** - * Indicates that the stack map frames must be recomputed from scratch. In - * this case the maximum stack size and number of local variables is also - * recomputed from scratch. - * - * @see #compute - */ -private final static int FRAMES = 0; - -/** - * Indicates that the maximum stack size and number of local variables must - * be automatically computed. - * - * @see #compute - */ -private final static int MAXS = 1; - -/** - * Indicates that nothing must be automatically computed. - * - * @see #compute - */ -private final static int NOTHING = 2; - -/** - * Next method writer (see {@link ClassWriter#firstMethod firstMethod}). - */ -MethodWriter next; - -/** - * The class writer to which this method must be added. - */ -ClassWriter cw; - -/** - * Access flags of this method. - */ -private int access; - -/** - * The index of the constant pool item that contains the name of this - * method. - */ -private int name; - -/** - * The index of the constant pool item that contains the descriptor of this - * method. - */ -private int desc; - -/** - * The descriptor of this method. - */ -private String descriptor; - -/** - * The signature of this method. - */ -String signature; - -/** - * If not zero, indicates that the code of this method must be copied from - * the ClassReader associated to this writer in cw.cr. More - * precisely, this field gives the index of the first byte to copied from - * cw.cr.b. - */ -int classReaderOffset; - -/** - * If not zero, indicates that the code of this method must be copied from - * the ClassReader associated to this writer in cw.cr. More - * precisely, this field gives the number of bytes to copied from - * cw.cr.b. - */ -int classReaderLength; - -/** - * Number of exceptions that can be thrown by this method. - */ -int exceptionCount; - -/** - * The exceptions that can be thrown by this method. More precisely, this - * array contains the indexes of the constant pool items that contain the - * internal names of these exception classes. - */ -int[] exceptions; - -/** - * The annotation default attribute of this method. May be null. - */ -private ByteVector annd; - -/** - * The runtime visible annotations of this method. May be null. - */ -private AnnotationWriter anns; - -/** - * The runtime invisible annotations of this method. May be null. - */ -private AnnotationWriter ianns; - -/** - * The runtime visible parameter annotations of this method. May be - * null. - */ -private AnnotationWriter[] panns; - -/** - * The runtime invisible parameter annotations of this method. May be - * null. - */ -private AnnotationWriter[] ipanns; - -/** - * The non standard attributes of the method. - */ -private Attribute attrs; - -/** - * The bytecode of this method. - */ -private ByteVector code = new ByteVector(); - -/** - * Maximum stack size of this method. - */ -private int maxStack; - -/** - * Maximum number of local variables for this method. - */ -private int maxLocals; - -/** - * Number of stack map frames in the StackMapTable attribute. - */ -private int frameCount; - -/** - * The StackMapTable attribute. - */ -private ByteVector stackMap; - -/** - * The offset of the last frame that was written in the StackMapTable - * attribute. - */ -private int previousFrameOffset; - -/** - * The last frame that was written in the StackMapTable attribute. - * - * @see #frame - */ -private int[] previousFrame; - -/** - * Index of the next element to be added in {@link #frame}. - */ -private int frameIndex; - -/** - * The current stack map frame. The first element contains the offset of the - * instruction to which the frame corresponds, the second element is the - * number of locals and the third one is the number of stack elements. The - * local variables start at index 3 and are followed by the operand stack - * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] = - * nStack, frame[3] = nLocal. All types are encoded as integers, with the - * same format as the one used in {@link Label}, but limited to BASE types. - */ -private int[] frame; - -/** - * Number of elements in the exception handler list. - */ -private int handlerCount; - -/** - * The first element in the exception handler list. - */ -private Handler firstHandler; - -/** - * The last element in the exception handler list. - */ -private Handler lastHandler; - -/** - * Number of entries in the LocalVariableTable attribute. - */ -private int localVarCount; - -/** - * The LocalVariableTable attribute. - */ -private ByteVector localVar; - -/** - * Number of entries in the LocalVariableTypeTable attribute. - */ -private int localVarTypeCount; - -/** - * The LocalVariableTypeTable attribute. - */ -private ByteVector localVarType; - -/** - * Number of entries in the LineNumberTable attribute. - */ -private int lineNumberCount; - -/** - * The LineNumberTable attribute. - */ -private ByteVector lineNumber; - -/** - * The non standard attributes of the method's code. - */ -private Attribute cattrs; - -/** - * Indicates if some jump instructions are too small and need to be resized. - */ -private boolean resize; - -/** - * Indicates if the instructions contain at least one JSR instruction. - */ -private boolean jsr; - -// ------------------------------------------------------------------------ - -/* - * Fields for the control flow graph analysis algorithm (used to compute the - * maximum stack size). A control flow graph contains one node per "basic - * block", and one edge per "jump" from one basic block to another. Each - * node (i.e., each basic block) is represented by the Label object that - * corresponds to the first instruction of this basic block. Each node also - * stores the list of its successors in the graph, as a linked list of Edge - * objects. - */ - -/** - * Indicates what must be automatically computed. - * - * @see FRAMES - * @see MAXS - * @see NOTHING - */ -private int compute; - -/** - * A list of labels. This list is the list of basic blocks in the method, - * i.e. a list of Label objects linked to each other by their - * {@link Label#successor} field, in the order they are visited by - * {@link visitLabel}, and starting with the first basic block. - */ -private Label labels; - -/** - * The previous basic block. - */ -private Label previousBlock; - -/** - * The current basic block. - */ -private Label currentBlock; - -/** - * The (relative) stack size after the last visited instruction. This size - * is relative to the beginning of the current basic block, i.e., the true - * stack size after the last visited instruction is equal to the - * {@link Label#inputStackTop beginStackSize} of the current basic block - * plus stackSize. - */ -private int stackSize; - -/** - * The (relative) maximum stack size after the last visited instruction. - * This size is relative to the beginning of the current basic block, i.e., - * the true maximum stack size after the last visited instruction is equal - * to the {@link Label#inputStackTop beginStackSize} of the current basic - * block plus stackSize. - */ -private int maxStackSize; - -// ------------------------------------------------------------------------ -// Constructor -// ------------------------------------------------------------------------ - -/** - * Constructs a new {@link MethodWriter}. - * - * @param cw the class writer in which the method must be added. - * @param access the method's access flags (see {@link Opcodes}). - * @param name the method's name. - * @param desc the method's descriptor (see {@link Type}). - * @param signature the method's signature. May be null. - * @param exceptions the internal names of the method's exceptions. May be - * null. - * @param computeMaxs true if the maximum stack size and number - * of local variables must be automatically computed. - * @param computeFrames true if the stack map tables must be - * recomputed from scratch. - */ -MethodWriter( - final ClassWriter cw, - final int access, - final String name, - final String desc, - final String signature, - final String[] exceptions, - final boolean computeMaxs, - final boolean computeFrames){ - if(cw.firstMethod == null) - { - cw.firstMethod = this; - } - else - { - cw.lastMethod.next = this; - } - cw.lastMethod = this; - this.cw = cw; - this.access = access; - this.name = cw.newUTF8(name); - this.desc = cw.newUTF8(desc); - this.descriptor = desc; - this.signature = signature; - if(exceptions != null && exceptions.length > 0) - { - exceptionCount = exceptions.length; - this.exceptions = new int[exceptionCount]; - for(int i = 0; i < exceptionCount; ++i) - { - this.exceptions[i] = cw.newClass(exceptions[i]); - } - } - this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING); - if(computeMaxs || computeFrames) - { - if(computeFrames && name.equals("")) - { - this.access |= ACC_CONSTRUCTOR; - } - // updates maxLocals - int size = getArgumentsAndReturnSizes(descriptor) >> 2; - if((access & Opcodes.ACC_STATIC) != 0) - { - --size; - } - maxLocals = size; - // creates and visits the label for the first basic block - labels = new Label(); - labels.status |= Label.PUSHED; - visitLabel(labels); - } -} - -// ------------------------------------------------------------------------ -// Implementation of the MethodVisitor interface -// ------------------------------------------------------------------------ - -public AnnotationVisitor visitAnnotationDefault(){ - annd = new ByteVector(); - return new AnnotationWriter(cw, false, annd, null, 0); -} - -public AnnotationVisitor visitAnnotation( - final String desc, - final boolean visible){ - ByteVector bv = new ByteVector(); - // write type, and reserve space for values count - bv.putShort(cw.newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); - if(visible) - { - aw.next = anns; - anns = aw; - } - else - { - aw.next = ianns; - ianns = aw; - } - return aw; -} - -public AnnotationVisitor visitParameterAnnotation( - final int parameter, - final String desc, - final boolean visible){ - ByteVector bv = new ByteVector(); - // write type, and reserve space for values count - bv.putShort(cw.newUTF8(desc)).putShort(0); - AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); - if(visible) - { - if(panns == null) - { - panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; - } - aw.next = panns[parameter]; - panns[parameter] = aw; - } - else - { - if(ipanns == null) - { - ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; - } - aw.next = ipanns[parameter]; - ipanns[parameter] = aw; - } - return aw; -} - -public void visitAttribute(final Attribute attr){ - if(attr.isCodeAttribute()) - { - attr.next = cattrs; - cattrs = attr; - } - else - { - attr.next = attrs; - attrs = attr; - } -} - -public void visitCode(){ -} - -public void visitFrame( - final int type, - final int nLocal, - final Object[] local, - final int nStack, - final Object[] stack){ - if(compute == FRAMES) - { - return; - } - - if(type == Opcodes.F_NEW) - { - startFrame(code.length, nLocal, nStack); - for(int i = 0; i < nLocal; ++i) - { - if(local[i] instanceof String) - { - frame[frameIndex++] = Frame.OBJECT - | cw.addType((String) local[i]); - } - else if(local[i] instanceof Integer) - { - frame[frameIndex++] = ((Integer) local[i]).intValue(); - } - else - { - frame[frameIndex++] = Frame.UNINITIALIZED - | cw.addUninitializedType("", - ((Label) local[i]).position); - } - } - for(int i = 0; i < nStack; ++i) - { - if(stack[i] instanceof String) - { - frame[frameIndex++] = Frame.OBJECT - | cw.addType((String) stack[i]); - } - else if(stack[i] instanceof Integer) - { - frame[frameIndex++] = ((Integer) stack[i]).intValue(); - } - else - { - frame[frameIndex++] = Frame.UNINITIALIZED - | cw.addUninitializedType("", - ((Label) stack[i]).position); - } - } - endFrame(); - } - else - { - int delta; - if(stackMap == null) - { - stackMap = new ByteVector(); - delta = code.length; - } - else - { - delta = code.length - previousFrameOffset - 1; - } - - switch(type) - { - case Opcodes.F_FULL: - stackMap.putByte(FULL_FRAME) - .putShort(delta) - .putShort(nLocal); - for(int i = 0; i < nLocal; ++i) - { - writeFrameType(local[i]); - } - stackMap.putShort(nStack); - for(int i = 0; i < nStack; ++i) - { - writeFrameType(stack[i]); - } - break; - case Opcodes.F_APPEND: - stackMap.putByte(SAME_FRAME_EXTENDED + nLocal) - .putShort(delta); - for(int i = 0; i < nLocal; ++i) - { - writeFrameType(local[i]); - } - break; - case Opcodes.F_CHOP: - stackMap.putByte(SAME_FRAME_EXTENDED - nLocal) - .putShort(delta); - break; - case Opcodes.F_SAME: - if(delta < 64) - { - stackMap.putByte(delta); - } - else - { - stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); - } - break; - case Opcodes.F_SAME1: - if(delta < 64) - { - stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); - } - else - { - stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) - .putShort(delta); - } - writeFrameType(stack[0]); - break; - } - - previousFrameOffset = code.length; - ++frameCount; - } -} - -public void visitInsn(final int opcode){ - // adds the instruction to the bytecode of the method - code.putByte(opcode); - // update currentBlock - // Label currentBlock = this.currentBlock; - if(currentBlock != null) - { - if(compute == FRAMES) - { - currentBlock.frame.execute(opcode, 0, null, null); - } - else - { - // updates current and max stack sizes - int size = stackSize + Frame.SIZE[opcode]; - if(size > maxStackSize) - { - maxStackSize = size; - } - stackSize = size; - } - // if opcode == ATHROW or xRETURN, ends current block (no successor) - if((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) - || opcode == Opcodes.ATHROW) - { - noSuccessor(); - } - } -} - -public void visitIntInsn(final int opcode, final int operand){ - // Label currentBlock = this.currentBlock; - if(currentBlock != null) - { - if(compute == FRAMES) - { - currentBlock.frame.execute(opcode, operand, null, null); - } - else if(opcode != Opcodes.NEWARRAY) - { - // updates current and max stack sizes only for NEWARRAY - // (stack size variation = 0 for BIPUSH or SIPUSH) - int size = stackSize + 1; - if(size > maxStackSize) - { - maxStackSize = size; - } - stackSize = size; - } - } - // adds the instruction to the bytecode of the method - if(opcode == Opcodes.SIPUSH) - { - code.put12(opcode, operand); - } - else - { // BIPUSH or NEWARRAY - code.put11(opcode, operand); - } -} - -public void visitVarInsn(final int opcode, final int var){ - // Label currentBlock = this.currentBlock; - if(currentBlock != null) - { - if(compute == FRAMES) - { - currentBlock.frame.execute(opcode, var, null, null); - } - else - { - // updates current and max stack sizes - if(opcode == Opcodes.RET) - { - // no stack change, but end of current block (no successor) - currentBlock.status |= Label.RET; - // save 'stackSize' here for future use - // (see {@link #findSubroutineSuccessors}) - currentBlock.inputStackTop = stackSize; - noSuccessor(); - } - else - { // xLOAD or xSTORE - int size = stackSize + Frame.SIZE[opcode]; - if(size > maxStackSize) - { - maxStackSize = size; - } - stackSize = size; - } - } - } - if(compute != NOTHING) - { - // updates max locals - int n; - if(opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD - || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) - { - n = var + 2; - } - else - { - n = var + 1; - } - if(n > maxLocals) - { - maxLocals = n; - } - } - // adds the instruction to the bytecode of the method - if(var < 4 && opcode != Opcodes.RET) - { - int opt; - if(opcode < Opcodes.ISTORE) - { - /* ILOAD_0 */ - opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; - } - else - { - /* ISTORE_0 */ - opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var; - } - code.putByte(opt); - } - else if(var >= 256) - { - code.putByte(196 /* WIDE */).put12(opcode, var); - } - else - { - code.put11(opcode, var); - } - if(opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) - { - visitLabel(new Label()); - } -} - -public void visitTypeInsn(final int opcode, final String desc){ - Item i = cw.newClassItem(desc); - // Label currentBlock = this.currentBlock; - if(currentBlock != null) - { - if(compute == FRAMES) - { - currentBlock.frame.execute(opcode, code.length, cw, i); - } - else if(opcode == Opcodes.NEW) - { - // updates current and max stack sizes only if opcode == NEW - // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF) - int size = stackSize + 1; - if(size > maxStackSize) - { - maxStackSize = size; - } - stackSize = size; - } - } - // adds the instruction to the bytecode of the method - code.put12(opcode, i.index); -} - -public void visitFieldInsn( - final int opcode, - final String owner, - final String name, - final String desc){ - Item i = cw.newFieldItem(owner, name, desc); - // Label currentBlock = this.currentBlock; - if(currentBlock != null) - { - if(compute == FRAMES) - { - currentBlock.frame.execute(opcode, 0, cw, i); - } - else - { - int size; - // computes the stack size variation - char c = desc.charAt(0); - switch(opcode) - { - case Opcodes.GETSTATIC: - size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); - break; - case Opcodes.PUTSTATIC: - size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); - break; - case Opcodes.GETFIELD: - size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); - break; - // case Constants.PUTFIELD: - default: - size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); - break; - } - // updates current and max stack sizes - if(size > maxStackSize) - { - maxStackSize = size; - } - stackSize = size; - } - } - // adds the instruction to the bytecode of the method - code.put12(opcode, i.index); -} - -public void visitMethodInsn( - final int opcode, - final String owner, - final String name, - final String desc){ - boolean itf = opcode == Opcodes.INVOKEINTERFACE; - Item i = cw.newMethodItem(owner, name, desc, itf); - int argSize = i.intVal; - // Label currentBlock = this.currentBlock; - if(currentBlock != null) - { - if(compute == FRAMES) - { - currentBlock.frame.execute(opcode, 0, cw, i); - } - else - { - /* - * computes the stack size variation. In order not to recompute - * several times this variation for the same Item, we use the - * intVal field of this item to store this variation, once it - * has been computed. More precisely this intVal field stores - * the sizes of the arguments and of the return value - * corresponding to desc. - */ - if(argSize == 0) - { - // the above sizes have not been computed yet, - // so we compute them... - argSize = getArgumentsAndReturnSizes(desc); - // ... and we save them in order - // not to recompute them in the future - i.intVal = argSize; - } - int size; - if(opcode == Opcodes.INVOKESTATIC) - { - size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; - } - else - { - size = stackSize - (argSize >> 2) + (argSize & 0x03); - } - // updates current and max stack sizes - if(size > maxStackSize) - { - maxStackSize = size; - } - stackSize = size; - } - } - // adds the instruction to the bytecode of the method - if(itf) - { - if(argSize == 0) - { - argSize = getArgumentsAndReturnSizes(desc); - i.intVal = argSize; - } - code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); - } - else - { - code.put12(opcode, i.index); - } -} - -public void visitJumpInsn(final int opcode, final Label label){ - Label nextInsn = null; - // Label currentBlock = this.currentBlock; - if(currentBlock != null) - { - if(compute == FRAMES) - { - currentBlock.frame.execute(opcode, 0, null, null); - // 'label' is the target of a jump instruction - label.getFirst().status |= Label.TARGET; - // adds 'label' as a successor of this basic block - addSuccessor(Edge.NORMAL, label); - if(opcode != Opcodes.GOTO) - { - // creates a Label for the next basic block - nextInsn = new Label(); - } - } - else - { - if(opcode == Opcodes.JSR) - { - jsr = true; - currentBlock.status |= Label.JSR; - addSuccessor(stackSize + 1, label); - // creates a Label for the next basic block - nextInsn = new Label(); - /* - * note that, by construction in this method, a JSR block - * has at least two successors in the control flow graph: - * the first one leads the next instruction after the JSR, - * while the second one leads to the JSR target. - */ - } - else - { - // updates current stack size (max stack size unchanged - // because stack size variation always negative in this - // case) - stackSize += Frame.SIZE[opcode]; - addSuccessor(stackSize, label); - } - } - } - // adds the instruction to the bytecode of the method - if((label.status & Label.RESOLVED) != 0 - && label.position - code.length < Short.MIN_VALUE) - { - /* - * case of a backward jump with an offset < -32768. In this case we - * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx - * with IFNOTxxx GOTO_W , where IFNOTxxx is the - * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where - * designates the instruction just after the GOTO_W. - */ - if(opcode == Opcodes.GOTO) - { - code.putByte(200); // GOTO_W - } - else if(opcode == Opcodes.JSR) - { - code.putByte(201); // JSR_W - } - else - { - // if the IF instruction is transformed into IFNOT GOTO_W the - // next instruction becomes the target of the IFNOT instruction - if(nextInsn != null) - { - nextInsn.status |= Label.TARGET; - } - code.putByte(opcode <= 166 - ? ((opcode + 1) ^ 1) - 1 - : opcode ^ 1); - code.putShort(8); // jump offset - code.putByte(200); // GOTO_W - } - label.put(this, code, code.length - 1, true); - } - else - { - /* - * case of a backward jump with an offset >= -32768, or of a forward - * jump with, of course, an unknown offset. In these cases we store - * the offset in 2 bytes (which will be increased in - * resizeInstructions, if needed). - */ - code.putByte(opcode); - label.put(this, code, code.length - 1, false); - } - if(currentBlock != null) - { - if(nextInsn != null) - { - // if the jump instruction is not a GOTO, the next instruction - // is also a successor of this instruction. Calling visitLabel - // adds the label of this next instruction as a successor of the - // current block, and starts a new basic block - visitLabel(nextInsn); - } - if(opcode == Opcodes.GOTO) - { - noSuccessor(); - } - } -} - -public void visitLabel(final Label label){ - // resolves previous forward references to label, if any - resize |= label.resolve(this, code.length, code.data); - // updates currentBlock - if((label.status & Label.DEBUG) != 0) - { - return; - } - if(compute == FRAMES) - { - if(currentBlock != null) - { - if(label.position == currentBlock.position) - { - // successive labels, do not start a new basic block - currentBlock.status |= (label.status & Label.TARGET); - label.frame = currentBlock.frame; - return; - } - // ends current block (with one new successor) - addSuccessor(Edge.NORMAL, label); - } - // begins a new current block - currentBlock = label; - if(label.frame == null) - { - label.frame = new Frame(); - label.frame.owner = label; - } - // updates the basic block list - if(previousBlock != null) - { - if(label.position == previousBlock.position) - { - previousBlock.status |= (label.status & Label.TARGET); - label.frame = previousBlock.frame; - currentBlock = previousBlock; - return; - } - previousBlock.successor = label; - } - previousBlock = label; - } - else if(compute == MAXS) - { - if(currentBlock != null) - { - // ends current block (with one new successor) - currentBlock.outputStackMax = maxStackSize; - addSuccessor(stackSize, label); - } - // begins a new current block - currentBlock = label; - // resets the relative current and max stack sizes - stackSize = 0; - maxStackSize = 0; - // updates the basic block list - if(previousBlock != null) - { - previousBlock.successor = label; - } - previousBlock = label; - } -} - -public void visitLdcInsn(final Object cst){ - Item i = cw.newConstItem(cst); - // Label currentBlock = this.currentBlock; - if(currentBlock != null) - { - if(compute == FRAMES) - { - currentBlock.frame.execute(Opcodes.LDC, 0, cw, i); - } - else - { - int size; - // computes the stack size variation - if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) - { - size = stackSize + 2; - } - else - { - size = stackSize + 1; - } - // updates current and max stack sizes - if(size > maxStackSize) - { - maxStackSize = size; - } - stackSize = size; - } - } - // adds the instruction to the bytecode of the method - int index = i.index; - if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) - { - code.put12(20 /* LDC2_W */, index); - } - else if(index >= 256) - { - code.put12(19 /* LDC_W */, index); - } - else - { - code.put11(Opcodes.LDC, index); - } -} - -public void visitIincInsn(final int var, final int increment){ - if(currentBlock != null) - { - if(compute == FRAMES) - { - currentBlock.frame.execute(Opcodes.IINC, var, null, null); - } - } - if(compute != NOTHING) - { - // updates max locals - int n = var + 1; - if(n > maxLocals) - { - maxLocals = n; - } - } - // adds the instruction to the bytecode of the method - if((var > 255) || (increment > 127) || (increment < -128)) - { - code.putByte(196 /* WIDE */) - .put12(Opcodes.IINC, var) - .putShort(increment); - } - else - { - code.putByte(Opcodes.IINC).put11(var, increment); - } -} - -public void visitTableSwitchInsn( - final int min, - final int max, - final Label dflt, - final Label labels[]){ - // adds the instruction to the bytecode of the method - int source = code.length; - code.putByte(Opcodes.TABLESWITCH); - code.length += (4 - code.length % 4) % 4; - dflt.put(this, code, source, true); - code.putInt(min).putInt(max); - for(int i = 0; i < labels.length; ++i) - { - labels[i].put(this, code, source, true); - } - // updates currentBlock - visitSwitchInsn(dflt, labels); -} - -public void visitLookupSwitchInsn( - final Label dflt, - final int keys[], - final Label labels[]){ - // adds the instruction to the bytecode of the method - int source = code.length; - code.putByte(Opcodes.LOOKUPSWITCH); - code.length += (4 - code.length % 4) % 4; - dflt.put(this, code, source, true); - code.putInt(labels.length); - for(int i = 0; i < labels.length; ++i) - { - code.putInt(keys[i]); - labels[i].put(this, code, source, true); - } - // updates currentBlock - visitSwitchInsn(dflt, labels); -} - -private void visitSwitchInsn(final Label dflt, final Label[] labels){ - // Label currentBlock = this.currentBlock; - if(currentBlock != null) - { - if(compute == FRAMES) - { - currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); - // adds current block successors - addSuccessor(Edge.NORMAL, dflt); - dflt.getFirst().status |= Label.TARGET; - for(int i = 0; i < labels.length; ++i) - { - addSuccessor(Edge.NORMAL, labels[i]); - labels[i].getFirst().status |= Label.TARGET; - } - } - else - { - // updates current stack size (max stack size unchanged) - --stackSize; - // adds current block successors - addSuccessor(stackSize, dflt); - for(int i = 0; i < labels.length; ++i) - { - addSuccessor(stackSize, labels[i]); - } - } - // ends current block - noSuccessor(); - } -} - -public void visitMultiANewArrayInsn(final String desc, final int dims){ - Item i = cw.newClassItem(desc); - // Label currentBlock = this.currentBlock; - if(currentBlock != null) - { - if(compute == FRAMES) - { - currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i); - } - else - { - // updates current stack size (max stack size unchanged because - // stack size variation always negative or null) - stackSize += 1 - dims; - } - } - // adds the instruction to the bytecode of the method - code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); -} - -public void visitTryCatchBlock( - final Label start, - final Label end, - final Label handler, - final String type){ - ++handlerCount; - Handler h = new Handler(); - h.start = start; - h.end = end; - h.handler = handler; - h.desc = type; - h.type = type != null ? cw.newClass(type) : 0; - if(lastHandler == null) - { - firstHandler = h; - } - else - { - lastHandler.next = h; - } - lastHandler = h; -} - -public void visitLocalVariable( - final String name, - final String desc, - final String signature, - final Label start, - final Label end, - final int index){ - if(signature != null) - { - if(localVarType == null) - { - localVarType = new ByteVector(); - } - ++localVarTypeCount; - localVarType.putShort(start.position) - .putShort(end.position - start.position) - .putShort(cw.newUTF8(name)) - .putShort(cw.newUTF8(signature)) - .putShort(index); - } - if(localVar == null) - { - localVar = new ByteVector(); - } - ++localVarCount; - localVar.putShort(start.position) - .putShort(end.position - start.position) - .putShort(cw.newUTF8(name)) - .putShort(cw.newUTF8(desc)) - .putShort(index); - if(compute != NOTHING) - { - // updates max locals - char c = desc.charAt(0); - int n = index + (c == 'J' || c == 'D' ? 2 : 1); - if(n > maxLocals) - { - maxLocals = n; - } - } -} - -public void visitLineNumber(final int line, final Label start){ - if(lineNumber == null) - { - lineNumber = new ByteVector(); - } - ++lineNumberCount; - lineNumber.putShort(start.position); - lineNumber.putShort(line); -} - -public void visitMaxs(final int maxStack, final int maxLocals){ - if(compute == FRAMES) - { - // completes the control flow graph with exception handler blocks - Handler handler = firstHandler; - while(handler != null) - { - Label l = handler.start.getFirst(); - Label h = handler.handler.getFirst(); - Label e = handler.end.getFirst(); - // computes the kind of the edges to 'h' - String t = handler.desc == null - ? "java/lang/Throwable" - : handler.desc; - int kind = Frame.OBJECT | cw.addType(t); - // h is an exception handler - h.status |= Label.TARGET; - // adds 'h' as a successor of labels between 'start' and 'end' - while(l != e) - { - // creates an edge to 'h' - Edge b = new Edge(); - b.info = kind; - b.successor = h; - // adds it to the successors of 'l' - b.next = l.successors; - l.successors = b; - // goes to the next label - l = l.successor; - } - handler = handler.next; - } - - // creates and visits the first (implicit) frame - Frame f = labels.frame; - Type[] args = Type.getArgumentTypes(descriptor); - f.initInputFrame(cw, access, args, this.maxLocals); - visitFrame(f); - - /* - * fix point algorithm: mark the first basic block as 'changed' - * (i.e. put it in the 'changed' list) and, while there are changed - * basic blocks, choose one, mark it as unchanged, and update its - * successors (which can be changed in the process). - */ - int max = 0; - Label changed = labels; - while(changed != null) - { - // removes a basic block from the list of changed basic blocks - Label l = changed; - changed = changed.next; - l.next = null; - f = l.frame; - // a reacheable jump target must be stored in the stack map - if((l.status & Label.TARGET) != 0) - { - l.status |= Label.STORE; - } - // all visited labels are reacheable, by definition - l.status |= Label.REACHABLE; - // updates the (absolute) maximum stack size - int blockMax = f.inputStack.length + l.outputStackMax; - if(blockMax > max) - { - max = blockMax; - } - // updates the successors of the current basic block - Edge e = l.successors; - while(e != null) - { - Label n = e.successor.getFirst(); - boolean change = f.merge(cw, n.frame, e.info); - if(change && n.next == null) - { - // if n has changed and is not already in the 'changed' - // list, adds it to this list - n.next = changed; - changed = n; - } - e = e.next; - } - } - this.maxStack = max; - - // visits all the frames that must be stored in the stack map - Label l = labels; - while(l != null) - { - f = l.frame; - if((l.status & Label.STORE) != 0) - { - visitFrame(f); - } - if((l.status & Label.REACHABLE) == 0) - { - // finds start and end of dead basic block - Label k = l.successor; - int start = l.position; - int end = (k == null ? code.length : k.position) - 1; - // if non empty basic block - if(end >= start) - { - // replaces instructions with NOP ... NOP ATHROW - for(int i = start; i < end; ++i) - { - code.data[i] = Opcodes.NOP; - } - code.data[end] = (byte) Opcodes.ATHROW; - // emits a frame for this unreachable block - startFrame(start, 0, 1); - frame[frameIndex++] = Frame.OBJECT - | cw.addType("java/lang/Throwable"); - endFrame(); - } - } - l = l.successor; - } - } - else if(compute == MAXS) - { - // completes the control flow graph with exception handler blocks - Handler handler = firstHandler; - while(handler != null) - { - Label l = handler.start; - Label h = handler.handler; - Label e = handler.end; - // adds 'h' as a successor of labels between 'start' and 'end' - while(l != e) - { - // creates an edge to 'h' - Edge b = new Edge(); - b.info = Edge.EXCEPTION; - b.successor = h; - // adds it to the successors of 'l' - if((l.status & Label.JSR) != 0) - { - // if l is a JSR block, adds b after the first two edges - // to preserve the hypothesis about JSR block successors - // order (see {@link #visitJumpInsn}) - b.next = l.successors.next.next; - l.successors.next.next = b; - } - else - { - b.next = l.successors; - l.successors = b; - } - // goes to the next label - l = l.successor; - } - handler = handler.next; - } - - if(jsr) - { - // completes the control flow graph with the RET successors - /* - * first step: finds the subroutines. This step determines, for - * each basic block, to which subroutine(s) it belongs, and - * stores this set as a bit set in the {@link Label#status} - * field. Subroutines are numbered with powers of two, from - * 0x1000 to 0x80000000 (so there must be at most 20 subroutines - * in a method). - */ - // finds the basic blocks that belong to the "main" subroutine - int id = 0x1000; - findSubroutine(labels, id); - // finds the basic blocks that belong to the real subroutines - Label l = labels; - while(l != null) - { - if((l.status & Label.JSR) != 0) - { - // the subroutine is defined by l's TARGET, not by l - Label subroutine = l.successors.next.successor; - // if this subroutine does not have an id yet... - if((subroutine.status & ~0xFFF) == 0) - { - // ...assigns it a new id and finds its basic blocks - id = id << 1; - findSubroutine(subroutine, id); - } - } - l = l.successor; - } - // second step: finds the successors of RET blocks - findSubroutineSuccessors(0x1000, new Label[10], 0); - } - - /* - * control flow analysis algorithm: while the block stack is not - * empty, pop a block from this stack, update the max stack size, - * compute the true (non relative) begin stack size of the - * successors of this block, and push these successors onto the - * stack (unless they have already been pushed onto the stack). - * Note: by hypothesis, the {@link Label#inputStackTop} of the - * blocks in the block stack are the true (non relative) beginning - * stack sizes of these blocks. - */ - int max = 0; - Label stack = labels; - while(stack != null) - { - // pops a block from the stack - Label l = stack; - stack = stack.next; - // computes the true (non relative) max stack size of this block - int start = l.inputStackTop; - int blockMax = start + l.outputStackMax; - // updates the global max stack size - if(blockMax > max) - { - max = blockMax; - } - // analyses the successors of the block - Edge b = l.successors; - if((l.status & Label.JSR) != 0) - { - // ignores the first edge of JSR blocks (virtual successor) - b = b.next; - } - while(b != null) - { - l = b.successor; - // if this successor has not already been pushed... - if((l.status & Label.PUSHED) == 0) - { - // computes its true beginning stack size... - l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start - + b.info; - // ...and pushes it onto the stack - l.status |= Label.PUSHED; - l.next = stack; - stack = l; - } - b = b.next; - } - } - this.maxStack = max; - } - else - { - this.maxStack = maxStack; - this.maxLocals = maxLocals; - } -} - -public void visitEnd(){ -} - -// ------------------------------------------------------------------------ -// Utility methods: control flow analysis algorithm -// ------------------------------------------------------------------------ - -/** - * Computes the size of the arguments and of the return value of a method. - * - * @param desc the descriptor of a method. - * @return the size of the arguments of the method (plus one for the - * implicit this argument), argSize, and the size of its return - * value, retSize, packed into a single int i = - * (argSize << 2) | retSize (argSize is therefore equal - * to i >> 2, and retSize to i & 0x03). - */ -static int getArgumentsAndReturnSizes(final String desc){ - int n = 1; - int c = 1; - while(true) - { - char car = desc.charAt(c++); - if(car == ')') - { - car = desc.charAt(c); - return n << 2 - | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); - } - else if(car == 'L') - { - while(desc.charAt(c++) != ';') - { - } - n += 1; - } - else if(car == '[') - { - while((car = desc.charAt(c)) == '[') - { - ++c; - } - if(car == 'D' || car == 'J') - { - n -= 1; - } - } - else if(car == 'D' || car == 'J') - { - n += 2; - } - else - { - n += 1; - } - } -} - -/** - * Adds a successor to the {@link #currentBlock currentBlock} block. - * - * @param info information about the control flow edge to be added. - * @param successor the successor block to be added to the current block. - */ -private void addSuccessor(final int info, final Label successor){ - // creates and initializes an Edge object... - Edge b = new Edge(); - b.info = info; - b.successor = successor; - // ...and adds it to the successor list of the currentBlock block - b.next = currentBlock.successors; - currentBlock.successors = b; -} - -/** - * Ends the current basic block. This method must be used in the case where - * the current basic block does not have any successor. - */ -private void noSuccessor(){ - if(compute == FRAMES) - { - Label l = new Label(); - l.frame = new Frame(); - l.frame.owner = l; - l.resolve(this, code.length, code.data); - previousBlock.successor = l; - previousBlock = l; - } - else - { - currentBlock.outputStackMax = maxStackSize; - } - currentBlock = null; -} - -/** - * Finds the basic blocks that belong to a given subroutine, and marks these - * blocks as belonging to this subroutine (by using {@link Label#status} as - * a bit set (see {@link #visitMaxs}). This recursive method follows the - * control flow graph to find all the blocks that are reachable from the - * given block WITHOUT following any JSR target. - * - * @param block a block that belongs to the subroutine - * @param id the id of this subroutine - */ -private void findSubroutine(final Label block, final int id){ - // if 'block' is already marked as belonging to subroutine 'id', returns - if((block.status & id) != 0) - { - return; - } - // marks 'block' as belonging to subroutine 'id' - block.status |= id; - // calls this method recursively on each successor, except JSR targets - Edge e = block.successors; - while(e != null) - { - // if 'block' is a JSR block, then 'block.successors.next' leads - // to the JSR target (see {@link #visitJumpInsn}) and must therefore - // not be followed - if((block.status & Label.JSR) == 0 || e != block.successors.next) - { - findSubroutine(e.successor, id); - } - e = e.next; - } -} - -/** - * Finds the successors of the RET blocks of the specified subroutine, and - * of any nested subroutine it calls. - * - * @param id id of the subroutine whose RET block successors must be found. - * @param JSRs the JSR blocks that were followed to reach this subroutine. - * @param nJSRs number of JSR blocks in the JSRs array. - */ -private void findSubroutineSuccessors( - final int id, - final Label[] JSRs, - final int nJSRs){ - // iterates over all the basic blocks... - Label l = labels; - while(l != null) - { - // for those that belong to subroutine 'id'... - if((l.status & id) != 0) - { - if((l.status & Label.JSR) != 0) - { - // finds the subroutine to which 'l' leads by following the - // second edge of l.successors (see {@link #visitJumpInsn}) - int nId = l.successors.next.successor.status & ~0xFFF; - if(nId != id) - { - // calls this method recursively with l pushed onto the - // JSRs stack to find the successors of the RET blocks - // of this nested subroutine 'nId' - JSRs[nJSRs] = l; - findSubroutineSuccessors(nId, JSRs, nJSRs + 1); - } - } - else if((l.status & Label.RET) != 0) - { - /* - * finds the JSR block in the JSRs stack that corresponds to - * this RET block, and updates the successors of this RET - * block accordingly. This corresponding JSR is the one that - * leads to the subroutine to which the RET block belongs. - * But the RET block can belong to several subroutines (if a - * nested subroutine returns to its parent subroutine - * implicitely, without a RET). So, in fact, the JSR that - * corresponds to this RET is the first block in the JSRs - * stack, starting from the bottom of the stack, that leads - * to a subroutine to which the RET block belongs. - */ - for(int i = 0; i < nJSRs; ++i) - { - int JSRstatus = JSRs[i].successors.next.successor.status; - if(((JSRstatus & ~0xFFF) & (l.status & ~0xFFF)) != 0) - { - Edge e = new Edge(); - e.info = l.inputStackTop; - e.successor = JSRs[i].successors.successor; - e.next = l.successors; - l.successors = e; - break; - } - } - } - } - l = l.successor; - } -} - -// ------------------------------------------------------------------------ -// Utility methods: stack map frames -// ------------------------------------------------------------------------ - -/** - * Visits a frame that has been computed from scratch. - * - * @param f the frame that must be visited. - */ -private void visitFrame(final Frame f){ - int i, t; - int nTop = 0; - int nLocal = 0; - int nStack = 0; - int[] locals = f.inputLocals; - int[] stacks = f.inputStack; - // computes the number of locals (ignores TOP types that are just after - // a LONG or a DOUBLE, and all trailing TOP types) - for(i = 0; i < locals.length; ++i) - { - t = locals[i]; - if(t == Frame.TOP) - { - ++nTop; - } - else - { - nLocal += nTop + 1; - nTop = 0; - } - if(t == Frame.LONG || t == Frame.DOUBLE) - { - ++i; - } - } - // computes the stack size (ignores TOP types that are just after - // a LONG or a DOUBLE) - for(i = 0; i < stacks.length; ++i) - { - t = stacks[i]; - ++nStack; - if(t == Frame.LONG || t == Frame.DOUBLE) - { - ++i; - } - } - // visits the frame and its content - startFrame(f.owner.position, nLocal, nStack); - for(i = 0; nLocal > 0; ++i, --nLocal) - { - t = locals[i]; - frame[frameIndex++] = t; - if(t == Frame.LONG || t == Frame.DOUBLE) - { - ++i; - } - } - for(i = 0; i < stacks.length; ++i) - { - t = stacks[i]; - frame[frameIndex++] = t; - if(t == Frame.LONG || t == Frame.DOUBLE) - { - ++i; - } - } - endFrame(); -} - -/** - * Starts the visit of a stack map frame. - * - * @param offset the offset of the instruction to which the frame - * corresponds. - * @param nLocal the number of local variables in the frame. - * @param nStack the number of stack elements in the frame. - */ -private void startFrame(final int offset, final int nLocal, final int nStack){ - int n = 3 + nLocal + nStack; - if(frame == null || frame.length < n) - { - frame = new int[n]; - } - frame[0] = offset; - frame[1] = nLocal; - frame[2] = nStack; - frameIndex = 3; -} - -/** - * Checks if the visit of the current frame {@link #frame} is finished, and - * if yes, write it in the StackMapTable attribute. - */ -private void endFrame(){ - if(previousFrame != null) - { // do not write the first frame - if(stackMap == null) - { - stackMap = new ByteVector(); - } - writeFrame(); - ++frameCount; - } - previousFrame = frame; - frame = null; -} - -/** - * Compress and writes the current frame {@link #frame} in the StackMapTable - * attribute. - */ -private void writeFrame(){ - int clocalsSize = frame[1]; - int cstackSize = frame[2]; - if((cw.version & 0xFFFF) < Opcodes.V1_6) - { - stackMap.putShort(frame[0]).putShort(clocalsSize); - writeFrameTypes(3, 3 + clocalsSize); - stackMap.putShort(cstackSize); - writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); - return; - } - int localsSize = previousFrame[1]; - int type = FULL_FRAME; - int k = 0; - int delta; - if(frameCount == 0) - { - delta = frame[0]; - } - else - { - delta = frame[0] - previousFrame[0] - 1; - } - if(cstackSize == 0) - { - k = clocalsSize - localsSize; - switch(k) - { - case-3: - case-2: - case-1: - type = CHOP_FRAME; - localsSize = clocalsSize; - break; - case 0: - type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; - break; - case 1: - case 2: - case 3: - type = APPEND_FRAME; - break; - } - } - else if(clocalsSize == localsSize && cstackSize == 1) - { - type = delta < 63 - ? SAME_LOCALS_1_STACK_ITEM_FRAME - : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; - } - if(type != FULL_FRAME) - { - // verify if locals are the same - int l = 3; - for(int j = 0; j < localsSize; j++) - { - if(frame[l] != previousFrame[l]) - { - type = FULL_FRAME; - break; - } - l++; - } - } - switch(type) - { - case SAME_FRAME: - stackMap.putByte(delta); - break; - case SAME_LOCALS_1_STACK_ITEM_FRAME: - stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); - writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); - break; - case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: - stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) - .putShort(delta); - writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); - break; - case SAME_FRAME_EXTENDED: - stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); - break; - case CHOP_FRAME: - stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); - break; - case APPEND_FRAME: - stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); - writeFrameTypes(3 + localsSize, 3 + clocalsSize); - break; - // case FULL_FRAME: - default: - stackMap.putByte(FULL_FRAME) - .putShort(delta) - .putShort(clocalsSize); - writeFrameTypes(3, 3 + clocalsSize); - stackMap.putShort(cstackSize); - writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); - } -} - -/** - * Writes some types of the current frame {@link #frame} into the - * StackMapTableAttribute. This method converts types from the format used - * in {@link Label} to the format used in StackMapTable attributes. In - * particular, it converts type table indexes to constant pool indexes. - * - * @param start index of the first type in {@link #frame} to write. - * @param end index of last type in {@link #frame} to write (exclusive). - */ -private void writeFrameTypes(final int start, final int end){ - for(int i = start; i < end; ++i) - { - int t = frame[i]; - int d = t & Frame.DIM; - if(d == 0) - { - int v = t & Frame.BASE_VALUE; - switch(t & Frame.BASE_KIND) - { - case Frame.OBJECT: - stackMap.putByte(7) - .putShort(cw.newClass(cw.typeTable[v].strVal1)); - break; - case Frame.UNINITIALIZED: - stackMap.putByte(8).putShort(cw.typeTable[v].intVal); - break; - default: - stackMap.putByte(v); - } - } - else - { - StringBuffer buf = new StringBuffer(); - d >>= 28; - while(d-- > 0) - { - buf.append('['); - } - if((t & Frame.BASE_KIND) == Frame.OBJECT) - { - buf.append('L'); - buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1); - buf.append(';'); - } - else - { - switch(t & 0xF) - { - case 1: - buf.append('I'); - break; - case 2: - buf.append('F'); - break; - case 3: - buf.append('D'); - break; - case 9: - buf.append('Z'); - break; - case 10: - buf.append('B'); - break; - case 11: - buf.append('C'); - break; - case 12: - buf.append('S'); - break; - default: - buf.append('J'); - } - } - stackMap.putByte(7).putShort(cw.newClass(buf.toString())); - } - } -} - -private void writeFrameType(final Object type){ - if(type instanceof String) - { - stackMap.putByte(7).putShort(cw.newClass((String) type)); - } - else if(type instanceof Integer) - { - stackMap.putByte(((Integer) type).intValue()); - } - else - { - stackMap.putByte(8).putShort(((Label) type).position); - } -} - -// ------------------------------------------------------------------------ -// Utility methods: dump bytecode array -// ------------------------------------------------------------------------ - -/** - * Returns the size of the bytecode of this method. - * - * @return the size of the bytecode of this method. - */ -final int getSize(){ - if(classReaderOffset != 0) - { - return 6 + classReaderLength; - } - if(resize) - { - // replaces the temporary jump opcodes introduced by Label.resolve. - resizeInstructions(); - } - int size = 8; - if(code.length > 0) - { - cw.newUTF8("Code"); - size += 18 + code.length + 8 * handlerCount; - if(localVar != null) - { - cw.newUTF8("LocalVariableTable"); - size += 8 + localVar.length; - } - if(localVarType != null) - { - cw.newUTF8("LocalVariableTypeTable"); - size += 8 + localVarType.length; - } - if(lineNumber != null) - { - cw.newUTF8("LineNumberTable"); - size += 8 + lineNumber.length; - } - if(stackMap != null) - { - boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; - cw.newUTF8(zip ? "StackMapTable" : "StackMap"); - size += 8 + stackMap.length; - } - if(cattrs != null) - { - size += cattrs.getSize(cw, - code.data, - code.length, - maxStack, - maxLocals); - } - } - if(exceptionCount > 0) - { - cw.newUTF8("Exceptions"); - size += 8 + 2 * exceptionCount; - } - if((access & Opcodes.ACC_SYNTHETIC) != 0 - && (cw.version & 0xffff) < Opcodes.V1_5) - { - cw.newUTF8("Synthetic"); - size += 6; - } - if((access & Opcodes.ACC_DEPRECATED) != 0) - { - cw.newUTF8("Deprecated"); - size += 6; - } - if(signature != null) - { - cw.newUTF8("Signature"); - cw.newUTF8(signature); - size += 8; - } - if(annd != null) - { - cw.newUTF8("AnnotationDefault"); - size += 6 + annd.length; - } - if(anns != null) - { - cw.newUTF8("RuntimeVisibleAnnotations"); - size += 8 + anns.getSize(); - } - if(ianns != null) - { - cw.newUTF8("RuntimeInvisibleAnnotations"); - size += 8 + ianns.getSize(); - } - if(panns != null) - { - cw.newUTF8("RuntimeVisibleParameterAnnotations"); - size += 7 + 2 * panns.length; - for(int i = panns.length - 1; i >= 0; --i) - { - size += panns[i] == null ? 0 : panns[i].getSize(); - } - } - if(ipanns != null) - { - cw.newUTF8("RuntimeInvisibleParameterAnnotations"); - size += 7 + 2 * ipanns.length; - for(int i = ipanns.length - 1; i >= 0; --i) - { - size += ipanns[i] == null ? 0 : ipanns[i].getSize(); - } - } - if(attrs != null) - { - size += attrs.getSize(cw, null, 0, -1, -1); - } - return size; -} - -/** - * Puts the bytecode of this method in the given byte vector. - * - * @param out the byte vector into which the bytecode of this method must be - * copied. - */ -final void put(final ByteVector out){ - out.putShort(access).putShort(name).putShort(desc); - if(classReaderOffset != 0) - { - out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); - return; - } - int attributeCount = 0; - if(code.length > 0) - { - ++attributeCount; - } - if(exceptionCount > 0) - { - ++attributeCount; - } - if((access & Opcodes.ACC_SYNTHETIC) != 0 - && (cw.version & 0xffff) < Opcodes.V1_5) - { - ++attributeCount; - } - if((access & Opcodes.ACC_DEPRECATED) != 0) - { - ++attributeCount; - } - if(signature != null) - { - ++attributeCount; - } - if(annd != null) - { - ++attributeCount; - } - if(anns != null) - { - ++attributeCount; - } - if(ianns != null) - { - ++attributeCount; - } - if(panns != null) - { - ++attributeCount; - } - if(ipanns != null) - { - ++attributeCount; - } - if(attrs != null) - { - attributeCount += attrs.getCount(); - } - out.putShort(attributeCount); - if(code.length > 0) - { - int size = 12 + code.length + 8 * handlerCount; - if(localVar != null) - { - size += 8 + localVar.length; - } - if(localVarType != null) - { - size += 8 + localVarType.length; - } - if(lineNumber != null) - { - size += 8 + lineNumber.length; - } - if(stackMap != null) - { - size += 8 + stackMap.length; - } - if(cattrs != null) - { - size += cattrs.getSize(cw, - code.data, - code.length, - maxStack, - maxLocals); - } - out.putShort(cw.newUTF8("Code")).putInt(size); - out.putShort(maxStack).putShort(maxLocals); - out.putInt(code.length).putByteArray(code.data, 0, code.length); - out.putShort(handlerCount); - if(handlerCount > 0) - { - Handler h = firstHandler; - while(h != null) - { - out.putShort(h.start.position) - .putShort(h.end.position) - .putShort(h.handler.position) - .putShort(h.type); - h = h.next; - } - } - attributeCount = 0; - if(localVar != null) - { - ++attributeCount; - } - if(localVarType != null) - { - ++attributeCount; - } - if(lineNumber != null) - { - ++attributeCount; - } - if(stackMap != null) - { - ++attributeCount; - } - if(cattrs != null) - { - attributeCount += cattrs.getCount(); - } - out.putShort(attributeCount); - if(localVar != null) - { - out.putShort(cw.newUTF8("LocalVariableTable")); - out.putInt(localVar.length + 2).putShort(localVarCount); - out.putByteArray(localVar.data, 0, localVar.length); - } - if(localVarType != null) - { - out.putShort(cw.newUTF8("LocalVariableTypeTable")); - out.putInt(localVarType.length + 2).putShort(localVarTypeCount); - out.putByteArray(localVarType.data, 0, localVarType.length); - } - if(lineNumber != null) - { - out.putShort(cw.newUTF8("LineNumberTable")); - out.putInt(lineNumber.length + 2).putShort(lineNumberCount); - out.putByteArray(lineNumber.data, 0, lineNumber.length); - } - if(stackMap != null) - { - boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; - out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap")); - out.putInt(stackMap.length + 2).putShort(frameCount); - out.putByteArray(stackMap.data, 0, stackMap.length); - } - if(cattrs != null) - { - cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); - } - } - if(exceptionCount > 0) - { - out.putShort(cw.newUTF8("Exceptions")) - .putInt(2 * exceptionCount + 2); - out.putShort(exceptionCount); - for(int i = 0; i < exceptionCount; ++i) - { - out.putShort(exceptions[i]); - } - } - if((access & Opcodes.ACC_SYNTHETIC) != 0 - && (cw.version & 0xffff) < Opcodes.V1_5) - { - out.putShort(cw.newUTF8("Synthetic")).putInt(0); - } - if((access & Opcodes.ACC_DEPRECATED) != 0) - { - out.putShort(cw.newUTF8("Deprecated")).putInt(0); - } - if(signature != null) - { - out.putShort(cw.newUTF8("Signature")) - .putInt(2) - .putShort(cw.newUTF8(signature)); - } - if(annd != null) - { - out.putShort(cw.newUTF8("AnnotationDefault")); - out.putInt(annd.length); - out.putByteArray(annd.data, 0, annd.length); - } - if(anns != null) - { - out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); - anns.put(out); - } - if(ianns != null) - { - out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); - ianns.put(out); - } - if(panns != null) - { - out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); - AnnotationWriter.put(panns, out); - } - if(ipanns != null) - { - out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); - AnnotationWriter.put(ipanns, out); - } - if(attrs != null) - { - attrs.put(cw, null, 0, -1, -1, out); - } -} - -// ------------------------------------------------------------------------ -// Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) -// ------------------------------------------------------------------------ - -/** - * Resizes and replaces the temporary instructions inserted by - * {@link Label#resolve} for wide forward jumps, while keeping jump offsets - * and instruction addresses consistent. This may require to resize other - * existing instructions, or even to introduce new instructions: for - * example, increasing the size of an instruction by 2 at the middle of a - * method can increases the offset of an IFEQ instruction from 32766 to - * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W - * 32765. This, in turn, may require to increase the size of another jump - * instruction, and so on... All these operations are handled automatically - * by this method.

This method must be called after all the method - * that is being built has been visited. In particular, the - * {@link Label Label} objects used to construct the method are no longer - * valid after this method has been called. - */ -private void resizeInstructions(){ - byte[] b = code.data; // bytecode of the method - int u, v, label; // indexes in b - int i, j; // loop indexes - /* - * 1st step: As explained above, resizing an instruction may require to - * resize another one, which may require to resize yet another one, and - * so on. The first step of the algorithm consists in finding all the - * instructions that need to be resized, without modifying the code. - * This is done by the following "fix point" algorithm: - * - * Parse the code to find the jump instructions whose offset will need - * more than 2 bytes to be stored (the future offset is computed from - * the current offset and from the number of bytes that will be inserted - * or removed between the source and target instructions). For each such - * instruction, adds an entry in (a copy of) the indexes and sizes - * arrays (if this has not already been done in a previous iteration!). - * - * If at least one entry has been added during the previous step, go - * back to the beginning, otherwise stop. - * - * In fact the real algorithm is complicated by the fact that the size - * of TABLESWITCH and LOOKUPSWITCH instructions depends on their - * position in the bytecode (because of padding). In order to ensure the - * convergence of the algorithm, the number of bytes to be added or - * removed from these instructions is over estimated during the previous - * loop, and computed exactly only after the loop is finished (this - * requires another pass to parse the bytecode of the method). - */ - int[] allIndexes = new int[0]; // copy of indexes - int[] allSizes = new int[0]; // copy of sizes - boolean[] resize; // instructions to be resized - int newOffset; // future offset of a jump instruction - - resize = new boolean[code.length]; - - // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done - int state = 3; - do - { - if(state == 3) - { - state = 2; - } - u = 0; - while(u < b.length) - { - int opcode = b[u] & 0xFF; // opcode of current instruction - int insert = 0; // bytes to be added after this instruction - - switch(ClassWriter.TYPE[opcode]) - { - case ClassWriter.NOARG_INSN: - case ClassWriter.IMPLVAR_INSN: - u += 1; - break; - case ClassWriter.LABEL_INSN: - if(opcode > 201) - { - // converts temporary opcodes 202 to 217, 218 and - // 219 to IFEQ ... JSR (inclusive), IFNULL and - // IFNONNULL - opcode = opcode < 218 ? opcode - 49 : opcode - 20; - label = u + readUnsignedShort(b, u + 1); - } - else - { - label = u + readShort(b, u + 1); - } - newOffset = getNewOffset(allIndexes, allSizes, u, label); - if(newOffset < Short.MIN_VALUE - || newOffset > Short.MAX_VALUE) - { - if(!resize[u]) - { - if(opcode == Opcodes.GOTO - || opcode == Opcodes.JSR) - { - // two additional bytes will be required to - // replace this GOTO or JSR instruction with - // a GOTO_W or a JSR_W - insert = 2; - } - else - { - // five additional bytes will be required to - // replace this IFxxx instruction with - // IFNOTxxx GOTO_W , where IFNOTxxx - // is the "opposite" opcode of IFxxx (i.e., - // IFNE for IFEQ) and where designates - // the instruction just after the GOTO_W. - insert = 5; - } - resize[u] = true; - } - } - u += 3; - break; - case ClassWriter.LABELW_INSN: - u += 5; - break; - case ClassWriter.TABL_INSN: - if(state == 1) - { - // true number of bytes to be added (or removed) - // from this instruction = (future number of padding - // bytes - current number of padding byte) - - // previously over estimated variation = - // = ((3 - newOffset%4) - (3 - u%4)) - u%4 - // = (-newOffset%4 + u%4) - u%4 - // = -(newOffset & 3) - newOffset = getNewOffset(allIndexes, allSizes, 0, u); - insert = -(newOffset & 3); - } - else if(!resize[u]) - { - // over estimation of the number of bytes to be - // added to this instruction = 3 - current number - // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3 - insert = u & 3; - resize[u] = true; - } - // skips instruction - u = u + 4 - (u & 3); - u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12; - break; - case ClassWriter.LOOK_INSN: - if(state == 1) - { - // like TABL_INSN - newOffset = getNewOffset(allIndexes, allSizes, 0, u); - insert = -(newOffset & 3); - } - else if(!resize[u]) - { - // like TABL_INSN - insert = u & 3; - resize[u] = true; - } - // skips instruction - u = u + 4 - (u & 3); - u += 8 * readInt(b, u + 4) + 8; - break; - case ClassWriter.WIDE_INSN: - opcode = b[u + 1] & 0xFF; - if(opcode == Opcodes.IINC) - { - u += 6; - } - else - { - u += 4; - } - break; - case ClassWriter.VAR_INSN: - case ClassWriter.SBYTE_INSN: - case ClassWriter.LDC_INSN: - u += 2; - break; - case ClassWriter.SHORT_INSN: - case ClassWriter.LDCW_INSN: - case ClassWriter.FIELDORMETH_INSN: - case ClassWriter.TYPE_INSN: - case ClassWriter.IINC_INSN: - u += 3; - break; - case ClassWriter.ITFMETH_INSN: - u += 5; - break; - // case ClassWriter.MANA_INSN: - default: - u += 4; - break; - } - if(insert != 0) - { - // adds a new (u, insert) entry in the allIndexes and - // allSizes arrays - int[] newIndexes = new int[allIndexes.length + 1]; - int[] newSizes = new int[allSizes.length + 1]; - System.arraycopy(allIndexes, - 0, - newIndexes, - 0, - allIndexes.length); - System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length); - newIndexes[allIndexes.length] = u; - newSizes[allSizes.length] = insert; - allIndexes = newIndexes; - allSizes = newSizes; - if(insert > 0) - { - state = 3; - } - } - } - if(state < 3) - { - --state; - } - } while(state != 0); - - // 2nd step: - // copies the bytecode of the method into a new bytevector, updates the - // offsets, and inserts (or removes) bytes as requested. - - ByteVector newCode = new ByteVector(code.length); - - u = 0; - while(u < code.length) - { - int opcode = b[u] & 0xFF; - switch(ClassWriter.TYPE[opcode]) - { - case ClassWriter.NOARG_INSN: - case ClassWriter.IMPLVAR_INSN: - newCode.putByte(opcode); - u += 1; - break; - case ClassWriter.LABEL_INSN: - if(opcode > 201) - { - // changes temporary opcodes 202 to 217 (inclusive), 218 - // and 219 to IFEQ ... JSR (inclusive), IFNULL and - // IFNONNULL - opcode = opcode < 218 ? opcode - 49 : opcode - 20; - label = u + readUnsignedShort(b, u + 1); - } - else - { - label = u + readShort(b, u + 1); - } - newOffset = getNewOffset(allIndexes, allSizes, u, label); - if(resize[u]) - { - // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx - // with IFNOTxxx GOTO_W , where IFNOTxxx is - // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) - // and where designates the instruction just after - // the GOTO_W. - if(opcode == Opcodes.GOTO) - { - newCode.putByte(200); // GOTO_W - } - else if(opcode == Opcodes.JSR) - { - newCode.putByte(201); // JSR_W - } - else - { - newCode.putByte(opcode <= 166 - ? ((opcode + 1) ^ 1) - 1 - : opcode ^ 1); - newCode.putShort(8); // jump offset - newCode.putByte(200); // GOTO_W - // newOffset now computed from start of GOTO_W - newOffset -= 3; - } - newCode.putInt(newOffset); - } - else - { - newCode.putByte(opcode); - newCode.putShort(newOffset); - } - u += 3; - break; - case ClassWriter.LABELW_INSN: - label = u + readInt(b, u + 1); - newOffset = getNewOffset(allIndexes, allSizes, u, label); - newCode.putByte(opcode); - newCode.putInt(newOffset); - u += 5; - break; - case ClassWriter.TABL_INSN: - // skips 0 to 3 padding bytes - v = u; - u = u + 4 - (v & 3); - // reads and copies instruction - newCode.putByte(Opcodes.TABLESWITCH); - newCode.length += (4 - newCode.length % 4) % 4; - label = v + readInt(b, u); - u += 4; - newOffset = getNewOffset(allIndexes, allSizes, v, label); - newCode.putInt(newOffset); - j = readInt(b, u); - u += 4; - newCode.putInt(j); - j = readInt(b, u) - j + 1; - u += 4; - newCode.putInt(readInt(b, u - 4)); - for(; j > 0; --j) - { - label = v + readInt(b, u); - u += 4; - newOffset = getNewOffset(allIndexes, allSizes, v, label); - newCode.putInt(newOffset); - } - break; - case ClassWriter.LOOK_INSN: - // skips 0 to 3 padding bytes - v = u; - u = u + 4 - (v & 3); - // reads and copies instruction - newCode.putByte(Opcodes.LOOKUPSWITCH); - newCode.length += (4 - newCode.length % 4) % 4; - label = v + readInt(b, u); - u += 4; - newOffset = getNewOffset(allIndexes, allSizes, v, label); - newCode.putInt(newOffset); - j = readInt(b, u); - u += 4; - newCode.putInt(j); - for(; j > 0; --j) - { - newCode.putInt(readInt(b, u)); - u += 4; - label = v + readInt(b, u); - u += 4; - newOffset = getNewOffset(allIndexes, allSizes, v, label); - newCode.putInt(newOffset); - } - break; - case ClassWriter.WIDE_INSN: - opcode = b[u + 1] & 0xFF; - if(opcode == Opcodes.IINC) - { - newCode.putByteArray(b, u, 6); - u += 6; - } - else - { - newCode.putByteArray(b, u, 4); - u += 4; - } - break; - case ClassWriter.VAR_INSN: - case ClassWriter.SBYTE_INSN: - case ClassWriter.LDC_INSN: - newCode.putByteArray(b, u, 2); - u += 2; - break; - case ClassWriter.SHORT_INSN: - case ClassWriter.LDCW_INSN: - case ClassWriter.FIELDORMETH_INSN: - case ClassWriter.TYPE_INSN: - case ClassWriter.IINC_INSN: - newCode.putByteArray(b, u, 3); - u += 3; - break; - case ClassWriter.ITFMETH_INSN: - newCode.putByteArray(b, u, 5); - u += 5; - break; - // case MANA_INSN: - default: - newCode.putByteArray(b, u, 4); - u += 4; - break; - } - } - - // recomputes the stack map frames - if(frameCount > 0) - { - if(compute == FRAMES) - { - frameCount = 0; - stackMap = null; - previousFrame = null; - frame = null; - Frame f = new Frame(); - f.owner = labels; - Type[] args = Type.getArgumentTypes(descriptor); - f.initInputFrame(cw, access, args, maxLocals); - visitFrame(f); - Label l = labels; - while(l != null) - { - /* - * here we need the original label position. getNewOffset - * must therefore never have been called for this label. - */ - u = l.position - 3; - if((l.status & Label.STORE) != 0 || (u >= 0 && resize[u])) - { - getNewOffset(allIndexes, allSizes, l); - // TODO update offsets in UNINITIALIZED values - visitFrame(l.frame); - } - l = l.successor; - } - } - else - { - /* - * Resizing an existing stack map frame table is really hard. - * Not only the table must be parsed to update the offets, but - * new frames may be needed for jump instructions that were - * inserted by this method. And updating the offsets or - * inserting frames can change the format of the following - * frames, in case of packed frames. In practice the whole table - * must be recomputed. For this the frames are marked as - * potentially invalid. This will cause the whole class to be - * reread and rewritten with the COMPUTE_FRAMES option (see the - * ClassWriter.toByteArray method). This is not very efficient - * but is much easier and requires much less code than any other - * method I can think of. - */ - cw.invalidFrames = true; - } - } - // updates the exception handler block labels - Handler h = firstHandler; - while(h != null) - { - getNewOffset(allIndexes, allSizes, h.start); - getNewOffset(allIndexes, allSizes, h.end); - getNewOffset(allIndexes, allSizes, h.handler); - h = h.next; - } - // updates the instructions addresses in the - // local var and line number tables - for(i = 0; i < 2; ++i) - { - ByteVector bv = i == 0 ? localVar : localVarType; - if(bv != null) - { - b = bv.data; - u = 0; - while(u < bv.length) - { - label = readUnsignedShort(b, u); - newOffset = getNewOffset(allIndexes, allSizes, 0, label); - writeShort(b, u, newOffset); - label += readUnsignedShort(b, u + 2); - newOffset = getNewOffset(allIndexes, allSizes, 0, label) - - newOffset; - writeShort(b, u + 2, newOffset); - u += 10; - } - } - } - if(lineNumber != null) - { - b = lineNumber.data; - u = 0; - while(u < lineNumber.length) - { - writeShort(b, u, getNewOffset(allIndexes, - allSizes, - 0, - readUnsignedShort(b, u))); - u += 4; - } - } - // updates the labels of the other attributes - Attribute attr = cattrs; - while(attr != null) - { - Label[] labels = attr.getLabels(); - if(labels != null) - { - for(i = labels.length - 1; i >= 0; --i) - { - getNewOffset(allIndexes, allSizes, labels[i]); - } - } - attr = attr.next; - } - - // replaces old bytecodes with new ones - code = newCode; -} - -/** - * Reads an unsigned short value in the given byte array. - * - * @param b a byte array. - * @param index the start index of the value to be read. - * @return the read value. - */ -static int readUnsignedShort(final byte[] b, final int index){ - return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); -} - -/** - * Reads a signed short value in the given byte array. - * - * @param b a byte array. - * @param index the start index of the value to be read. - * @return the read value. - */ -static short readShort(final byte[] b, final int index){ - return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); -} - -/** - * Reads a signed int value in the given byte array. - * - * @param b a byte array. - * @param index the start index of the value to be read. - * @return the read value. - */ -static int readInt(final byte[] b, final int index){ - return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) - | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); -} - -/** - * Writes a short value in the given byte array. - * - * @param b a byte array. - * @param index where the first byte of the short value must be written. - * @param s the value to be written in the given byte array. - */ -static void writeShort(final byte[] b, final int index, final int s){ - b[index] = (byte) (s >>> 8); - b[index + 1] = (byte) s; -} - -/** - * Computes the future value of a bytecode offset.

Note: it is possible - * to have several entries for the same instruction in the indexes - * and sizes: two entries (index=a,size=b) and (index=a,size=b') - * are equivalent to a single entry (index=a,size=b+b'). - * - * @param indexes current positions of the instructions to be resized. Each - * instruction must be designated by the index of its last - * byte, plus one (or, in other words, by the index of the first - * byte of the next instruction). - * @param sizes the number of bytes to be added to the above - * instructions. More precisely, for each i < len, - * sizes[i] bytes will be added at the end of the - * instruction designated by indexes[i] or, if - * sizes[i] is negative, the last |sizes[i]| - * bytes of the instruction will be removed (the instruction size - * must not become negative or null). - * @param begin index of the first byte of the source instruction. - * @param end index of the first byte of the target instruction. - * @return the future value of the given bytecode offset. - */ -static int getNewOffset( - final int[] indexes, - final int[] sizes, - final int begin, - final int end){ - int offset = end - begin; - for(int i = 0; i < indexes.length; ++i) - { - if(begin < indexes[i] && indexes[i] <= end) - { - // forward jump - offset += sizes[i]; - } - else if(end < indexes[i] && indexes[i] <= begin) - { - // backward jump - offset -= sizes[i]; - } - } - return offset; -} - -/** - * Updates the offset of the given label. - * - * @param indexes current positions of the instructions to be resized. Each - * instruction must be designated by the index of its last - * byte, plus one (or, in other words, by the index of the first - * byte of the next instruction). - * @param sizes the number of bytes to be added to the above - * instructions. More precisely, for each i < len, - * sizes[i] bytes will be added at the end of the - * instruction designated by indexes[i] or, if - * sizes[i] is negative, the last |sizes[i]| - * bytes of the instruction will be removed (the instruction size - * must not become negative or null). - * @param label the label whose offset must be updated. - */ -static void getNewOffset( - final int[] indexes, - final int[] sizes, - final Label label){ - if((label.status & Label.RESIZED) == 0) - { - label.position = getNewOffset(indexes, sizes, 0, label.position); - label.status |= Label.RESIZED; - } -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * A {@link MethodVisitor} that generates methods in bytecode form. Each visit + * method of this class appends the bytecode corresponding to the visited + * instruction to a byte vector, in the order these methods are called. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +class MethodWriter implements MethodVisitor{ + +/** + * Pseudo access flag used to denote constructors. + */ +final static int ACC_CONSTRUCTOR = 262144; + +/** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is zero. + */ +final static int SAME_FRAME = 0; // to 63 (0-3f) + +/** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is 1 + */ +final static int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f) + +/** + * Reserved for future use + */ +final static int RESERVED = 128; + +/** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is 1. Offset is bigger then 63; + */ +final static int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7 + +/** + * Frame where current locals are the same as the locals in the previous + * frame, except that the k last locals are absent. The value of k is given + * by the formula 251-frame_type. + */ +final static int CHOP_FRAME = 248; // to 250 (f8-fA) + +/** + * Frame has exactly the same locals as the previous stack map frame and + * number of stack items is zero. Offset is bigger then 63; + */ +final static int SAME_FRAME_EXTENDED = 251; // fb + +/** + * Frame where current locals are the same as the locals in the previous + * frame, except that k additional locals are defined. The value of k is + * given by the formula frame_type-251. + */ +final static int APPEND_FRAME = 252; // to 254 // fc-fe + +/** + * Full frame + */ +final static int FULL_FRAME = 255; // ff + +/** + * Indicates that the stack map frames must be recomputed from scratch. In + * this case the maximum stack size and number of local variables is also + * recomputed from scratch. + * + * @see #compute + */ +private final static int FRAMES = 0; + +/** + * Indicates that the maximum stack size and number of local variables must + * be automatically computed. + * + * @see #compute + */ +private final static int MAXS = 1; + +/** + * Indicates that nothing must be automatically computed. + * + * @see #compute + */ +private final static int NOTHING = 2; + +/** + * Next method writer (see {@link ClassWriter#firstMethod firstMethod}). + */ +MethodWriter next; + +/** + * The class writer to which this method must be added. + */ +ClassWriter cw; + +/** + * Access flags of this method. + */ +private int access; + +/** + * The index of the constant pool item that contains the name of this + * method. + */ +private int name; + +/** + * The index of the constant pool item that contains the descriptor of this + * method. + */ +private int desc; + +/** + * The descriptor of this method. + */ +private String descriptor; + +/** + * The signature of this method. + */ +String signature; + +/** + * If not zero, indicates that the code of this method must be copied from + * the ClassReader associated to this writer in cw.cr. More + * precisely, this field gives the index of the first byte to copied from + * cw.cr.b. + */ +int classReaderOffset; + +/** + * If not zero, indicates that the code of this method must be copied from + * the ClassReader associated to this writer in cw.cr. More + * precisely, this field gives the number of bytes to copied from + * cw.cr.b. + */ +int classReaderLength; + +/** + * Number of exceptions that can be thrown by this method. + */ +int exceptionCount; + +/** + * The exceptions that can be thrown by this method. More precisely, this + * array contains the indexes of the constant pool items that contain the + * internal names of these exception classes. + */ +int[] exceptions; + +/** + * The annotation default attribute of this method. May be null. + */ +private ByteVector annd; + +/** + * The runtime visible annotations of this method. May be null. + */ +private AnnotationWriter anns; + +/** + * The runtime invisible annotations of this method. May be null. + */ +private AnnotationWriter ianns; + +/** + * The runtime visible parameter annotations of this method. May be + * null. + */ +private AnnotationWriter[] panns; + +/** + * The runtime invisible parameter annotations of this method. May be + * null. + */ +private AnnotationWriter[] ipanns; + +/** + * The non standard attributes of the method. + */ +private Attribute attrs; + +/** + * The bytecode of this method. + */ +private ByteVector code = new ByteVector(); + +/** + * Maximum stack size of this method. + */ +private int maxStack; + +/** + * Maximum number of local variables for this method. + */ +private int maxLocals; + +/** + * Number of stack map frames in the StackMapTable attribute. + */ +private int frameCount; + +/** + * The StackMapTable attribute. + */ +private ByteVector stackMap; + +/** + * The offset of the last frame that was written in the StackMapTable + * attribute. + */ +private int previousFrameOffset; + +/** + * The last frame that was written in the StackMapTable attribute. + * + * @see #frame + */ +private int[] previousFrame; + +/** + * Index of the next element to be added in {@link #frame}. + */ +private int frameIndex; + +/** + * The current stack map frame. The first element contains the offset of the + * instruction to which the frame corresponds, the second element is the + * number of locals and the third one is the number of stack elements. The + * local variables start at index 3 and are followed by the operand stack + * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] = + * nStack, frame[3] = nLocal. All types are encoded as integers, with the + * same format as the one used in {@link Label}, but limited to BASE types. + */ +private int[] frame; + +/** + * Number of elements in the exception handler list. + */ +private int handlerCount; + +/** + * The first element in the exception handler list. + */ +private Handler firstHandler; + +/** + * The last element in the exception handler list. + */ +private Handler lastHandler; + +/** + * Number of entries in the LocalVariableTable attribute. + */ +private int localVarCount; + +/** + * The LocalVariableTable attribute. + */ +private ByteVector localVar; + +/** + * Number of entries in the LocalVariableTypeTable attribute. + */ +private int localVarTypeCount; + +/** + * The LocalVariableTypeTable attribute. + */ +private ByteVector localVarType; + +/** + * Number of entries in the LineNumberTable attribute. + */ +private int lineNumberCount; + +/** + * The LineNumberTable attribute. + */ +private ByteVector lineNumber; + +/** + * The non standard attributes of the method's code. + */ +private Attribute cattrs; + +/** + * Indicates if some jump instructions are too small and need to be resized. + */ +private boolean resize; + +/** + * Indicates if the instructions contain at least one JSR instruction. + */ +private boolean jsr; + +// ------------------------------------------------------------------------ + +/* + * Fields for the control flow graph analysis algorithm (used to compute the + * maximum stack size). A control flow graph contains one node per "basic + * block", and one edge per "jump" from one basic block to another. Each + * node (i.e., each basic block) is represented by the Label object that + * corresponds to the first instruction of this basic block. Each node also + * stores the list of its successors in the graph, as a linked list of Edge + * objects. + */ + +/** + * Indicates what must be automatically computed. + * + * @see FRAMES + * @see MAXS + * @see NOTHING + */ +private int compute; + +/** + * A list of labels. This list is the list of basic blocks in the method, + * i.e. a list of Label objects linked to each other by their + * {@link Label#successor} field, in the order they are visited by + * {@link visitLabel}, and starting with the first basic block. + */ +private Label labels; + +/** + * The previous basic block. + */ +private Label previousBlock; + +/** + * The current basic block. + */ +private Label currentBlock; + +/** + * The (relative) stack size after the last visited instruction. This size + * is relative to the beginning of the current basic block, i.e., the true + * stack size after the last visited instruction is equal to the + * {@link Label#inputStackTop beginStackSize} of the current basic block + * plus stackSize. + */ +private int stackSize; + +/** + * The (relative) maximum stack size after the last visited instruction. + * This size is relative to the beginning of the current basic block, i.e., + * the true maximum stack size after the last visited instruction is equal + * to the {@link Label#inputStackTop beginStackSize} of the current basic + * block plus stackSize. + */ +private int maxStackSize; + +// ------------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------------ + +/** + * Constructs a new {@link MethodWriter}. + * + * @param cw the class writer in which the method must be added. + * @param access the method's access flags (see {@link Opcodes}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type}). + * @param signature the method's signature. May be null. + * @param exceptions the internal names of the method's exceptions. May be + * null. + * @param computeMaxs true if the maximum stack size and number + * of local variables must be automatically computed. + * @param computeFrames true if the stack map tables must be + * recomputed from scratch. + */ +MethodWriter( + final ClassWriter cw, + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions, + final boolean computeMaxs, + final boolean computeFrames){ + if(cw.firstMethod == null) + { + cw.firstMethod = this; + } + else + { + cw.lastMethod.next = this; + } + cw.lastMethod = this; + this.cw = cw; + this.access = access; + this.name = cw.newUTF8(name); + this.desc = cw.newUTF8(desc); + this.descriptor = desc; + this.signature = signature; + if(exceptions != null && exceptions.length > 0) + { + exceptionCount = exceptions.length; + this.exceptions = new int[exceptionCount]; + for(int i = 0; i < exceptionCount; ++i) + { + this.exceptions[i] = cw.newClass(exceptions[i]); + } + } + this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING); + if(computeMaxs || computeFrames) + { + if(computeFrames && name.equals("")) + { + this.access |= ACC_CONSTRUCTOR; + } + // updates maxLocals + int size = getArgumentsAndReturnSizes(descriptor) >> 2; + if((access & Opcodes.ACC_STATIC) != 0) + { + --size; + } + maxLocals = size; + // creates and visits the label for the first basic block + labels = new Label(); + labels.status |= Label.PUSHED; + visitLabel(labels); + } +} + +// ------------------------------------------------------------------------ +// Implementation of the MethodVisitor interface +// ------------------------------------------------------------------------ + +public AnnotationVisitor visitAnnotationDefault(){ + annd = new ByteVector(); + return new AnnotationWriter(cw, false, annd, null, 0); +} + +public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible){ + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if(visible) + { + aw.next = anns; + anns = aw; + } + else + { + aw.next = ianns; + ianns = aw; + } + return aw; +} + +public AnnotationVisitor visitParameterAnnotation( + final int parameter, + final String desc, + final boolean visible){ + ByteVector bv = new ByteVector(); + // write type, and reserve space for values count + bv.putShort(cw.newUTF8(desc)).putShort(0); + AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); + if(visible) + { + if(panns == null) + { + panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + } + aw.next = panns[parameter]; + panns[parameter] = aw; + } + else + { + if(ipanns == null) + { + ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; + } + aw.next = ipanns[parameter]; + ipanns[parameter] = aw; + } + return aw; +} + +public void visitAttribute(final Attribute attr){ + if(attr.isCodeAttribute()) + { + attr.next = cattrs; + cattrs = attr; + } + else + { + attr.next = attrs; + attrs = attr; + } +} + +public void visitCode(){ +} + +public void visitFrame( + final int type, + final int nLocal, + final Object[] local, + final int nStack, + final Object[] stack){ + if(compute == FRAMES) + { + return; + } + + if(type == Opcodes.F_NEW) + { + startFrame(code.length, nLocal, nStack); + for(int i = 0; i < nLocal; ++i) + { + if(local[i] instanceof String) + { + frame[frameIndex++] = Frame.OBJECT + | cw.addType((String) local[i]); + } + else if(local[i] instanceof Integer) + { + frame[frameIndex++] = ((Integer) local[i]).intValue(); + } + else + { + frame[frameIndex++] = Frame.UNINITIALIZED + | cw.addUninitializedType("", + ((Label) local[i]).position); + } + } + for(int i = 0; i < nStack; ++i) + { + if(stack[i] instanceof String) + { + frame[frameIndex++] = Frame.OBJECT + | cw.addType((String) stack[i]); + } + else if(stack[i] instanceof Integer) + { + frame[frameIndex++] = ((Integer) stack[i]).intValue(); + } + else + { + frame[frameIndex++] = Frame.UNINITIALIZED + | cw.addUninitializedType("", + ((Label) stack[i]).position); + } + } + endFrame(); + } + else + { + int delta; + if(stackMap == null) + { + stackMap = new ByteVector(); + delta = code.length; + } + else + { + delta = code.length - previousFrameOffset - 1; + } + + switch(type) + { + case Opcodes.F_FULL: + stackMap.putByte(FULL_FRAME) + .putShort(delta) + .putShort(nLocal); + for(int i = 0; i < nLocal; ++i) + { + writeFrameType(local[i]); + } + stackMap.putShort(nStack); + for(int i = 0; i < nStack; ++i) + { + writeFrameType(stack[i]); + } + break; + case Opcodes.F_APPEND: + stackMap.putByte(SAME_FRAME_EXTENDED + nLocal) + .putShort(delta); + for(int i = 0; i < nLocal; ++i) + { + writeFrameType(local[i]); + } + break; + case Opcodes.F_CHOP: + stackMap.putByte(SAME_FRAME_EXTENDED - nLocal) + .putShort(delta); + break; + case Opcodes.F_SAME: + if(delta < 64) + { + stackMap.putByte(delta); + } + else + { + stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); + } + break; + case Opcodes.F_SAME1: + if(delta < 64) + { + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); + } + else + { + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) + .putShort(delta); + } + writeFrameType(stack[0]); + break; + } + + previousFrameOffset = code.length; + ++frameCount; + } +} + +public void visitInsn(final int opcode){ + // adds the instruction to the bytecode of the method + code.putByte(opcode); + // update currentBlock + // Label currentBlock = this.currentBlock; + if(currentBlock != null) + { + if(compute == FRAMES) + { + currentBlock.frame.execute(opcode, 0, null, null); + } + else + { + // updates current and max stack sizes + int size = stackSize + Frame.SIZE[opcode]; + if(size > maxStackSize) + { + maxStackSize = size; + } + stackSize = size; + } + // if opcode == ATHROW or xRETURN, ends current block (no successor) + if((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) + || opcode == Opcodes.ATHROW) + { + noSuccessor(); + } + } +} + +public void visitIntInsn(final int opcode, final int operand){ + // Label currentBlock = this.currentBlock; + if(currentBlock != null) + { + if(compute == FRAMES) + { + currentBlock.frame.execute(opcode, operand, null, null); + } + else if(opcode != Opcodes.NEWARRAY) + { + // updates current and max stack sizes only for NEWARRAY + // (stack size variation = 0 for BIPUSH or SIPUSH) + int size = stackSize + 1; + if(size > maxStackSize) + { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + if(opcode == Opcodes.SIPUSH) + { + code.put12(opcode, operand); + } + else + { // BIPUSH or NEWARRAY + code.put11(opcode, operand); + } +} + +public void visitVarInsn(final int opcode, final int var){ + // Label currentBlock = this.currentBlock; + if(currentBlock != null) + { + if(compute == FRAMES) + { + currentBlock.frame.execute(opcode, var, null, null); + } + else + { + // updates current and max stack sizes + if(opcode == Opcodes.RET) + { + // no stack change, but end of current block (no successor) + currentBlock.status |= Label.RET; + // save 'stackSize' here for future use + // (see {@link #findSubroutineSuccessors}) + currentBlock.inputStackTop = stackSize; + noSuccessor(); + } + else + { // xLOAD or xSTORE + int size = stackSize + Frame.SIZE[opcode]; + if(size > maxStackSize) + { + maxStackSize = size; + } + stackSize = size; + } + } + } + if(compute != NOTHING) + { + // updates max locals + int n; + if(opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD + || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) + { + n = var + 2; + } + else + { + n = var + 1; + } + if(n > maxLocals) + { + maxLocals = n; + } + } + // adds the instruction to the bytecode of the method + if(var < 4 && opcode != Opcodes.RET) + { + int opt; + if(opcode < Opcodes.ISTORE) + { + /* ILOAD_0 */ + opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; + } + else + { + /* ISTORE_0 */ + opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var; + } + code.putByte(opt); + } + else if(var >= 256) + { + code.putByte(196 /* WIDE */).put12(opcode, var); + } + else + { + code.put11(opcode, var); + } + if(opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) + { + visitLabel(new Label()); + } +} + +public void visitTypeInsn(final int opcode, final String desc){ + Item i = cw.newClassItem(desc); + // Label currentBlock = this.currentBlock; + if(currentBlock != null) + { + if(compute == FRAMES) + { + currentBlock.frame.execute(opcode, code.length, cw, i); + } + else if(opcode == Opcodes.NEW) + { + // updates current and max stack sizes only if opcode == NEW + // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF) + int size = stackSize + 1; + if(size > maxStackSize) + { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + code.put12(opcode, i.index); +} + +public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc){ + Item i = cw.newFieldItem(owner, name, desc); + // Label currentBlock = this.currentBlock; + if(currentBlock != null) + { + if(compute == FRAMES) + { + currentBlock.frame.execute(opcode, 0, cw, i); + } + else + { + int size; + // computes the stack size variation + char c = desc.charAt(0); + switch(opcode) + { + case Opcodes.GETSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); + break; + case Opcodes.PUTSTATIC: + size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); + break; + case Opcodes.GETFIELD: + size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); + break; + // case Constants.PUTFIELD: + default: + size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); + break; + } + // updates current and max stack sizes + if(size > maxStackSize) + { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + code.put12(opcode, i.index); +} + +public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc){ + boolean itf = opcode == Opcodes.INVOKEINTERFACE; + Item i = cw.newMethodItem(owner, name, desc, itf); + int argSize = i.intVal; + // Label currentBlock = this.currentBlock; + if(currentBlock != null) + { + if(compute == FRAMES) + { + currentBlock.frame.execute(opcode, 0, cw, i); + } + else + { + /* + * computes the stack size variation. In order not to recompute + * several times this variation for the same Item, we use the + * intVal field of this item to store this variation, once it + * has been computed. More precisely this intVal field stores + * the sizes of the arguments and of the return value + * corresponding to desc. + */ + if(argSize == 0) + { + // the above sizes have not been computed yet, + // so we compute them... + argSize = getArgumentsAndReturnSizes(desc); + // ... and we save them in order + // not to recompute them in the future + i.intVal = argSize; + } + int size; + if(opcode == Opcodes.INVOKESTATIC) + { + size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; + } + else + { + size = stackSize - (argSize >> 2) + (argSize & 0x03); + } + // updates current and max stack sizes + if(size > maxStackSize) + { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + if(itf) + { + if(argSize == 0) + { + argSize = getArgumentsAndReturnSizes(desc); + i.intVal = argSize; + } + code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); + } + else + { + code.put12(opcode, i.index); + } +} + +public void visitJumpInsn(final int opcode, final Label label){ + Label nextInsn = null; + // Label currentBlock = this.currentBlock; + if(currentBlock != null) + { + if(compute == FRAMES) + { + currentBlock.frame.execute(opcode, 0, null, null); + // 'label' is the target of a jump instruction + label.getFirst().status |= Label.TARGET; + // adds 'label' as a successor of this basic block + addSuccessor(Edge.NORMAL, label); + if(opcode != Opcodes.GOTO) + { + // creates a Label for the next basic block + nextInsn = new Label(); + } + } + else + { + if(opcode == Opcodes.JSR) + { + jsr = true; + currentBlock.status |= Label.JSR; + addSuccessor(stackSize + 1, label); + // creates a Label for the next basic block + nextInsn = new Label(); + /* + * note that, by construction in this method, a JSR block + * has at least two successors in the control flow graph: + * the first one leads the next instruction after the JSR, + * while the second one leads to the JSR target. + */ + } + else + { + // updates current stack size (max stack size unchanged + // because stack size variation always negative in this + // case) + stackSize += Frame.SIZE[opcode]; + addSuccessor(stackSize, label); + } + } + } + // adds the instruction to the bytecode of the method + if((label.status & Label.RESOLVED) != 0 + && label.position - code.length < Short.MIN_VALUE) + { + /* + * case of a backward jump with an offset < -32768. In this case we + * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx + * with IFNOTxxx GOTO_W , where IFNOTxxx is the + * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where + * designates the instruction just after the GOTO_W. + */ + if(opcode == Opcodes.GOTO) + { + code.putByte(200); // GOTO_W + } + else if(opcode == Opcodes.JSR) + { + code.putByte(201); // JSR_W + } + else + { + // if the IF instruction is transformed into IFNOT GOTO_W the + // next instruction becomes the target of the IFNOT instruction + if(nextInsn != null) + { + nextInsn.status |= Label.TARGET; + } + code.putByte(opcode <= 166 + ? ((opcode + 1) ^ 1) - 1 + : opcode ^ 1); + code.putShort(8); // jump offset + code.putByte(200); // GOTO_W + } + label.put(this, code, code.length - 1, true); + } + else + { + /* + * case of a backward jump with an offset >= -32768, or of a forward + * jump with, of course, an unknown offset. In these cases we store + * the offset in 2 bytes (which will be increased in + * resizeInstructions, if needed). + */ + code.putByte(opcode); + label.put(this, code, code.length - 1, false); + } + if(currentBlock != null) + { + if(nextInsn != null) + { + // if the jump instruction is not a GOTO, the next instruction + // is also a successor of this instruction. Calling visitLabel + // adds the label of this next instruction as a successor of the + // current block, and starts a new basic block + visitLabel(nextInsn); + } + if(opcode == Opcodes.GOTO) + { + noSuccessor(); + } + } +} + +public void visitLabel(final Label label){ + // resolves previous forward references to label, if any + resize |= label.resolve(this, code.length, code.data); + // updates currentBlock + if((label.status & Label.DEBUG) != 0) + { + return; + } + if(compute == FRAMES) + { + if(currentBlock != null) + { + if(label.position == currentBlock.position) + { + // successive labels, do not start a new basic block + currentBlock.status |= (label.status & Label.TARGET); + label.frame = currentBlock.frame; + return; + } + // ends current block (with one new successor) + addSuccessor(Edge.NORMAL, label); + } + // begins a new current block + currentBlock = label; + if(label.frame == null) + { + label.frame = new Frame(); + label.frame.owner = label; + } + // updates the basic block list + if(previousBlock != null) + { + if(label.position == previousBlock.position) + { + previousBlock.status |= (label.status & Label.TARGET); + label.frame = previousBlock.frame; + currentBlock = previousBlock; + return; + } + previousBlock.successor = label; + } + previousBlock = label; + } + else if(compute == MAXS) + { + if(currentBlock != null) + { + // ends current block (with one new successor) + currentBlock.outputStackMax = maxStackSize; + addSuccessor(stackSize, label); + } + // begins a new current block + currentBlock = label; + // resets the relative current and max stack sizes + stackSize = 0; + maxStackSize = 0; + // updates the basic block list + if(previousBlock != null) + { + previousBlock.successor = label; + } + previousBlock = label; + } +} + +public void visitLdcInsn(final Object cst){ + Item i = cw.newConstItem(cst); + // Label currentBlock = this.currentBlock; + if(currentBlock != null) + { + if(compute == FRAMES) + { + currentBlock.frame.execute(Opcodes.LDC, 0, cw, i); + } + else + { + int size; + // computes the stack size variation + if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) + { + size = stackSize + 2; + } + else + { + size = stackSize + 1; + } + // updates current and max stack sizes + if(size > maxStackSize) + { + maxStackSize = size; + } + stackSize = size; + } + } + // adds the instruction to the bytecode of the method + int index = i.index; + if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) + { + code.put12(20 /* LDC2_W */, index); + } + else if(index >= 256) + { + code.put12(19 /* LDC_W */, index); + } + else + { + code.put11(Opcodes.LDC, index); + } +} + +public void visitIincInsn(final int var, final int increment){ + if(currentBlock != null) + { + if(compute == FRAMES) + { + currentBlock.frame.execute(Opcodes.IINC, var, null, null); + } + } + if(compute != NOTHING) + { + // updates max locals + int n = var + 1; + if(n > maxLocals) + { + maxLocals = n; + } + } + // adds the instruction to the bytecode of the method + if((var > 255) || (increment > 127) || (increment < -128)) + { + code.putByte(196 /* WIDE */) + .put12(Opcodes.IINC, var) + .putShort(increment); + } + else + { + code.putByte(Opcodes.IINC).put11(var, increment); + } +} + +public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label labels[]){ + // adds the instruction to the bytecode of the method + int source = code.length; + code.putByte(Opcodes.TABLESWITCH); + code.length += (4 - code.length % 4) % 4; + dflt.put(this, code, source, true); + code.putInt(min).putInt(max); + for(int i = 0; i < labels.length; ++i) + { + labels[i].put(this, code, source, true); + } + // updates currentBlock + visitSwitchInsn(dflt, labels); +} + +public void visitLookupSwitchInsn( + final Label dflt, + final int keys[], + final Label labels[]){ + // adds the instruction to the bytecode of the method + int source = code.length; + code.putByte(Opcodes.LOOKUPSWITCH); + code.length += (4 - code.length % 4) % 4; + dflt.put(this, code, source, true); + code.putInt(labels.length); + for(int i = 0; i < labels.length; ++i) + { + code.putInt(keys[i]); + labels[i].put(this, code, source, true); + } + // updates currentBlock + visitSwitchInsn(dflt, labels); +} + +private void visitSwitchInsn(final Label dflt, final Label[] labels){ + // Label currentBlock = this.currentBlock; + if(currentBlock != null) + { + if(compute == FRAMES) + { + currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); + // adds current block successors + addSuccessor(Edge.NORMAL, dflt); + dflt.getFirst().status |= Label.TARGET; + for(int i = 0; i < labels.length; ++i) + { + addSuccessor(Edge.NORMAL, labels[i]); + labels[i].getFirst().status |= Label.TARGET; + } + } + else + { + // updates current stack size (max stack size unchanged) + --stackSize; + // adds current block successors + addSuccessor(stackSize, dflt); + for(int i = 0; i < labels.length; ++i) + { + addSuccessor(stackSize, labels[i]); + } + } + // ends current block + noSuccessor(); + } +} + +public void visitMultiANewArrayInsn(final String desc, final int dims){ + Item i = cw.newClassItem(desc); + // Label currentBlock = this.currentBlock; + if(currentBlock != null) + { + if(compute == FRAMES) + { + currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i); + } + else + { + // updates current stack size (max stack size unchanged because + // stack size variation always negative or null) + stackSize += 1 - dims; + } + } + // adds the instruction to the bytecode of the method + code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); +} + +public void visitTryCatchBlock( + final Label start, + final Label end, + final Label handler, + final String type){ + ++handlerCount; + Handler h = new Handler(); + h.start = start; + h.end = end; + h.handler = handler; + h.desc = type; + h.type = type != null ? cw.newClass(type) : 0; + if(lastHandler == null) + { + firstHandler = h; + } + else + { + lastHandler.next = h; + } + lastHandler = h; +} + +public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index){ + if(signature != null) + { + if(localVarType == null) + { + localVarType = new ByteVector(); + } + ++localVarTypeCount; + localVarType.putShort(start.position) + .putShort(end.position - start.position) + .putShort(cw.newUTF8(name)) + .putShort(cw.newUTF8(signature)) + .putShort(index); + } + if(localVar == null) + { + localVar = new ByteVector(); + } + ++localVarCount; + localVar.putShort(start.position) + .putShort(end.position - start.position) + .putShort(cw.newUTF8(name)) + .putShort(cw.newUTF8(desc)) + .putShort(index); + if(compute != NOTHING) + { + // updates max locals + char c = desc.charAt(0); + int n = index + (c == 'J' || c == 'D' ? 2 : 1); + if(n > maxLocals) + { + maxLocals = n; + } + } +} + +public void visitLineNumber(final int line, final Label start){ + if(lineNumber == null) + { + lineNumber = new ByteVector(); + } + ++lineNumberCount; + lineNumber.putShort(start.position); + lineNumber.putShort(line); +} + +public void visitMaxs(final int maxStack, final int maxLocals){ + if(compute == FRAMES) + { + // completes the control flow graph with exception handler blocks + Handler handler = firstHandler; + while(handler != null) + { + Label l = handler.start.getFirst(); + Label h = handler.handler.getFirst(); + Label e = handler.end.getFirst(); + // computes the kind of the edges to 'h' + String t = handler.desc == null + ? "java/lang/Throwable" + : handler.desc; + int kind = Frame.OBJECT | cw.addType(t); + // h is an exception handler + h.status |= Label.TARGET; + // adds 'h' as a successor of labels between 'start' and 'end' + while(l != e) + { + // creates an edge to 'h' + Edge b = new Edge(); + b.info = kind; + b.successor = h; + // adds it to the successors of 'l' + b.next = l.successors; + l.successors = b; + // goes to the next label + l = l.successor; + } + handler = handler.next; + } + + // creates and visits the first (implicit) frame + Frame f = labels.frame; + Type[] args = Type.getArgumentTypes(descriptor); + f.initInputFrame(cw, access, args, this.maxLocals); + visitFrame(f); + + /* + * fix point algorithm: mark the first basic block as 'changed' + * (i.e. put it in the 'changed' list) and, while there are changed + * basic blocks, choose one, mark it as unchanged, and update its + * successors (which can be changed in the process). + */ + int max = 0; + Label changed = labels; + while(changed != null) + { + // removes a basic block from the list of changed basic blocks + Label l = changed; + changed = changed.next; + l.next = null; + f = l.frame; + // a reacheable jump target must be stored in the stack map + if((l.status & Label.TARGET) != 0) + { + l.status |= Label.STORE; + } + // all visited labels are reacheable, by definition + l.status |= Label.REACHABLE; + // updates the (absolute) maximum stack size + int blockMax = f.inputStack.length + l.outputStackMax; + if(blockMax > max) + { + max = blockMax; + } + // updates the successors of the current basic block + Edge e = l.successors; + while(e != null) + { + Label n = e.successor.getFirst(); + boolean change = f.merge(cw, n.frame, e.info); + if(change && n.next == null) + { + // if n has changed and is not already in the 'changed' + // list, adds it to this list + n.next = changed; + changed = n; + } + e = e.next; + } + } + this.maxStack = max; + + // visits all the frames that must be stored in the stack map + Label l = labels; + while(l != null) + { + f = l.frame; + if((l.status & Label.STORE) != 0) + { + visitFrame(f); + } + if((l.status & Label.REACHABLE) == 0) + { + // finds start and end of dead basic block + Label k = l.successor; + int start = l.position; + int end = (k == null ? code.length : k.position) - 1; + // if non empty basic block + if(end >= start) + { + // replaces instructions with NOP ... NOP ATHROW + for(int i = start; i < end; ++i) + { + code.data[i] = Opcodes.NOP; + } + code.data[end] = (byte) Opcodes.ATHROW; + // emits a frame for this unreachable block + startFrame(start, 0, 1); + frame[frameIndex++] = Frame.OBJECT + | cw.addType("java/lang/Throwable"); + endFrame(); + } + } + l = l.successor; + } + } + else if(compute == MAXS) + { + // completes the control flow graph with exception handler blocks + Handler handler = firstHandler; + while(handler != null) + { + Label l = handler.start; + Label h = handler.handler; + Label e = handler.end; + // adds 'h' as a successor of labels between 'start' and 'end' + while(l != e) + { + // creates an edge to 'h' + Edge b = new Edge(); + b.info = Edge.EXCEPTION; + b.successor = h; + // adds it to the successors of 'l' + if((l.status & Label.JSR) != 0) + { + // if l is a JSR block, adds b after the first two edges + // to preserve the hypothesis about JSR block successors + // order (see {@link #visitJumpInsn}) + b.next = l.successors.next.next; + l.successors.next.next = b; + } + else + { + b.next = l.successors; + l.successors = b; + } + // goes to the next label + l = l.successor; + } + handler = handler.next; + } + + if(jsr) + { + // completes the control flow graph with the RET successors + /* + * first step: finds the subroutines. This step determines, for + * each basic block, to which subroutine(s) it belongs, and + * stores this set as a bit set in the {@link Label#status} + * field. Subroutines are numbered with powers of two, from + * 0x1000 to 0x80000000 (so there must be at most 20 subroutines + * in a method). + */ + // finds the basic blocks that belong to the "main" subroutine + int id = 0x1000; + findSubroutine(labels, id); + // finds the basic blocks that belong to the real subroutines + Label l = labels; + while(l != null) + { + if((l.status & Label.JSR) != 0) + { + // the subroutine is defined by l's TARGET, not by l + Label subroutine = l.successors.next.successor; + // if this subroutine does not have an id yet... + if((subroutine.status & ~0xFFF) == 0) + { + // ...assigns it a new id and finds its basic blocks + id = id << 1; + findSubroutine(subroutine, id); + } + } + l = l.successor; + } + // second step: finds the successors of RET blocks + findSubroutineSuccessors(0x1000, new Label[10], 0); + } + + /* + * control flow analysis algorithm: while the block stack is not + * empty, pop a block from this stack, update the max stack size, + * compute the true (non relative) begin stack size of the + * successors of this block, and push these successors onto the + * stack (unless they have already been pushed onto the stack). + * Note: by hypothesis, the {@link Label#inputStackTop} of the + * blocks in the block stack are the true (non relative) beginning + * stack sizes of these blocks. + */ + int max = 0; + Label stack = labels; + while(stack != null) + { + // pops a block from the stack + Label l = stack; + stack = stack.next; + // computes the true (non relative) max stack size of this block + int start = l.inputStackTop; + int blockMax = start + l.outputStackMax; + // updates the global max stack size + if(blockMax > max) + { + max = blockMax; + } + // analyses the successors of the block + Edge b = l.successors; + if((l.status & Label.JSR) != 0) + { + // ignores the first edge of JSR blocks (virtual successor) + b = b.next; + } + while(b != null) + { + l = b.successor; + // if this successor has not already been pushed... + if((l.status & Label.PUSHED) == 0) + { + // computes its true beginning stack size... + l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start + + b.info; + // ...and pushes it onto the stack + l.status |= Label.PUSHED; + l.next = stack; + stack = l; + } + b = b.next; + } + } + this.maxStack = max; + } + else + { + this.maxStack = maxStack; + this.maxLocals = maxLocals; + } +} + +public void visitEnd(){ +} + +// ------------------------------------------------------------------------ +// Utility methods: control flow analysis algorithm +// ------------------------------------------------------------------------ + +/** + * Computes the size of the arguments and of the return value of a method. + * + * @param desc the descriptor of a method. + * @return the size of the arguments of the method (plus one for the + * implicit this argument), argSize, and the size of its return + * value, retSize, packed into a single int i = + * (argSize << 2) | retSize (argSize is therefore equal + * to i >> 2, and retSize to i & 0x03). + */ +static int getArgumentsAndReturnSizes(final String desc){ + int n = 1; + int c = 1; + while(true) + { + char car = desc.charAt(c++); + if(car == ')') + { + car = desc.charAt(c); + return n << 2 + | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); + } + else if(car == 'L') + { + while(desc.charAt(c++) != ';') + { + } + n += 1; + } + else if(car == '[') + { + while((car = desc.charAt(c)) == '[') + { + ++c; + } + if(car == 'D' || car == 'J') + { + n -= 1; + } + } + else if(car == 'D' || car == 'J') + { + n += 2; + } + else + { + n += 1; + } + } +} + +/** + * Adds a successor to the {@link #currentBlock currentBlock} block. + * + * @param info information about the control flow edge to be added. + * @param successor the successor block to be added to the current block. + */ +private void addSuccessor(final int info, final Label successor){ + // creates and initializes an Edge object... + Edge b = new Edge(); + b.info = info; + b.successor = successor; + // ...and adds it to the successor list of the currentBlock block + b.next = currentBlock.successors; + currentBlock.successors = b; +} + +/** + * Ends the current basic block. This method must be used in the case where + * the current basic block does not have any successor. + */ +private void noSuccessor(){ + if(compute == FRAMES) + { + Label l = new Label(); + l.frame = new Frame(); + l.frame.owner = l; + l.resolve(this, code.length, code.data); + previousBlock.successor = l; + previousBlock = l; + } + else + { + currentBlock.outputStackMax = maxStackSize; + } + currentBlock = null; +} + +/** + * Finds the basic blocks that belong to a given subroutine, and marks these + * blocks as belonging to this subroutine (by using {@link Label#status} as + * a bit set (see {@link #visitMaxs}). This recursive method follows the + * control flow graph to find all the blocks that are reachable from the + * given block WITHOUT following any JSR target. + * + * @param block a block that belongs to the subroutine + * @param id the id of this subroutine + */ +private void findSubroutine(final Label block, final int id){ + // if 'block' is already marked as belonging to subroutine 'id', returns + if((block.status & id) != 0) + { + return; + } + // marks 'block' as belonging to subroutine 'id' + block.status |= id; + // calls this method recursively on each successor, except JSR targets + Edge e = block.successors; + while(e != null) + { + // if 'block' is a JSR block, then 'block.successors.next' leads + // to the JSR target (see {@link #visitJumpInsn}) and must therefore + // not be followed + if((block.status & Label.JSR) == 0 || e != block.successors.next) + { + findSubroutine(e.successor, id); + } + e = e.next; + } +} + +/** + * Finds the successors of the RET blocks of the specified subroutine, and + * of any nested subroutine it calls. + * + * @param id id of the subroutine whose RET block successors must be found. + * @param JSRs the JSR blocks that were followed to reach this subroutine. + * @param nJSRs number of JSR blocks in the JSRs array. + */ +private void findSubroutineSuccessors( + final int id, + final Label[] JSRs, + final int nJSRs){ + // iterates over all the basic blocks... + Label l = labels; + while(l != null) + { + // for those that belong to subroutine 'id'... + if((l.status & id) != 0) + { + if((l.status & Label.JSR) != 0) + { + // finds the subroutine to which 'l' leads by following the + // second edge of l.successors (see {@link #visitJumpInsn}) + int nId = l.successors.next.successor.status & ~0xFFF; + if(nId != id) + { + // calls this method recursively with l pushed onto the + // JSRs stack to find the successors of the RET blocks + // of this nested subroutine 'nId' + JSRs[nJSRs] = l; + findSubroutineSuccessors(nId, JSRs, nJSRs + 1); + } + } + else if((l.status & Label.RET) != 0) + { + /* + * finds the JSR block in the JSRs stack that corresponds to + * this RET block, and updates the successors of this RET + * block accordingly. This corresponding JSR is the one that + * leads to the subroutine to which the RET block belongs. + * But the RET block can belong to several subroutines (if a + * nested subroutine returns to its parent subroutine + * implicitely, without a RET). So, in fact, the JSR that + * corresponds to this RET is the first block in the JSRs + * stack, starting from the bottom of the stack, that leads + * to a subroutine to which the RET block belongs. + */ + for(int i = 0; i < nJSRs; ++i) + { + int JSRstatus = JSRs[i].successors.next.successor.status; + if(((JSRstatus & ~0xFFF) & (l.status & ~0xFFF)) != 0) + { + Edge e = new Edge(); + e.info = l.inputStackTop; + e.successor = JSRs[i].successors.successor; + e.next = l.successors; + l.successors = e; + break; + } + } + } + } + l = l.successor; + } +} + +// ------------------------------------------------------------------------ +// Utility methods: stack map frames +// ------------------------------------------------------------------------ + +/** + * Visits a frame that has been computed from scratch. + * + * @param f the frame that must be visited. + */ +private void visitFrame(final Frame f){ + int i, t; + int nTop = 0; + int nLocal = 0; + int nStack = 0; + int[] locals = f.inputLocals; + int[] stacks = f.inputStack; + // computes the number of locals (ignores TOP types that are just after + // a LONG or a DOUBLE, and all trailing TOP types) + for(i = 0; i < locals.length; ++i) + { + t = locals[i]; + if(t == Frame.TOP) + { + ++nTop; + } + else + { + nLocal += nTop + 1; + nTop = 0; + } + if(t == Frame.LONG || t == Frame.DOUBLE) + { + ++i; + } + } + // computes the stack size (ignores TOP types that are just after + // a LONG or a DOUBLE) + for(i = 0; i < stacks.length; ++i) + { + t = stacks[i]; + ++nStack; + if(t == Frame.LONG || t == Frame.DOUBLE) + { + ++i; + } + } + // visits the frame and its content + startFrame(f.owner.position, nLocal, nStack); + for(i = 0; nLocal > 0; ++i, --nLocal) + { + t = locals[i]; + frame[frameIndex++] = t; + if(t == Frame.LONG || t == Frame.DOUBLE) + { + ++i; + } + } + for(i = 0; i < stacks.length; ++i) + { + t = stacks[i]; + frame[frameIndex++] = t; + if(t == Frame.LONG || t == Frame.DOUBLE) + { + ++i; + } + } + endFrame(); +} + +/** + * Starts the visit of a stack map frame. + * + * @param offset the offset of the instruction to which the frame + * corresponds. + * @param nLocal the number of local variables in the frame. + * @param nStack the number of stack elements in the frame. + */ +private void startFrame(final int offset, final int nLocal, final int nStack){ + int n = 3 + nLocal + nStack; + if(frame == null || frame.length < n) + { + frame = new int[n]; + } + frame[0] = offset; + frame[1] = nLocal; + frame[2] = nStack; + frameIndex = 3; +} + +/** + * Checks if the visit of the current frame {@link #frame} is finished, and + * if yes, write it in the StackMapTable attribute. + */ +private void endFrame(){ + if(previousFrame != null) + { // do not write the first frame + if(stackMap == null) + { + stackMap = new ByteVector(); + } + writeFrame(); + ++frameCount; + } + previousFrame = frame; + frame = null; +} + +/** + * Compress and writes the current frame {@link #frame} in the StackMapTable + * attribute. + */ +private void writeFrame(){ + int clocalsSize = frame[1]; + int cstackSize = frame[2]; + if((cw.version & 0xFFFF) < Opcodes.V1_6) + { + stackMap.putShort(frame[0]).putShort(clocalsSize); + writeFrameTypes(3, 3 + clocalsSize); + stackMap.putShort(cstackSize); + writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); + return; + } + int localsSize = previousFrame[1]; + int type = FULL_FRAME; + int k = 0; + int delta; + if(frameCount == 0) + { + delta = frame[0]; + } + else + { + delta = frame[0] - previousFrame[0] - 1; + } + if(cstackSize == 0) + { + k = clocalsSize - localsSize; + switch(k) + { + case-3: + case-2: + case-1: + type = CHOP_FRAME; + localsSize = clocalsSize; + break; + case 0: + type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; + break; + case 1: + case 2: + case 3: + type = APPEND_FRAME; + break; + } + } + else if(clocalsSize == localsSize && cstackSize == 1) + { + type = delta < 63 + ? SAME_LOCALS_1_STACK_ITEM_FRAME + : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; + } + if(type != FULL_FRAME) + { + // verify if locals are the same + int l = 3; + for(int j = 0; j < localsSize; j++) + { + if(frame[l] != previousFrame[l]) + { + type = FULL_FRAME; + break; + } + l++; + } + } + switch(type) + { + case SAME_FRAME: + stackMap.putByte(delta); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME: + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); + writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: + stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) + .putShort(delta); + writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); + break; + case SAME_FRAME_EXTENDED: + stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); + break; + case CHOP_FRAME: + stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); + break; + case APPEND_FRAME: + stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); + writeFrameTypes(3 + localsSize, 3 + clocalsSize); + break; + // case FULL_FRAME: + default: + stackMap.putByte(FULL_FRAME) + .putShort(delta) + .putShort(clocalsSize); + writeFrameTypes(3, 3 + clocalsSize); + stackMap.putShort(cstackSize); + writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); + } +} + +/** + * Writes some types of the current frame {@link #frame} into the + * StackMapTableAttribute. This method converts types from the format used + * in {@link Label} to the format used in StackMapTable attributes. In + * particular, it converts type table indexes to constant pool indexes. + * + * @param start index of the first type in {@link #frame} to write. + * @param end index of last type in {@link #frame} to write (exclusive). + */ +private void writeFrameTypes(final int start, final int end){ + for(int i = start; i < end; ++i) + { + int t = frame[i]; + int d = t & Frame.DIM; + if(d == 0) + { + int v = t & Frame.BASE_VALUE; + switch(t & Frame.BASE_KIND) + { + case Frame.OBJECT: + stackMap.putByte(7) + .putShort(cw.newClass(cw.typeTable[v].strVal1)); + break; + case Frame.UNINITIALIZED: + stackMap.putByte(8).putShort(cw.typeTable[v].intVal); + break; + default: + stackMap.putByte(v); + } + } + else + { + StringBuffer buf = new StringBuffer(); + d >>= 28; + while(d-- > 0) + { + buf.append('['); + } + if((t & Frame.BASE_KIND) == Frame.OBJECT) + { + buf.append('L'); + buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1); + buf.append(';'); + } + else + { + switch(t & 0xF) + { + case 1: + buf.append('I'); + break; + case 2: + buf.append('F'); + break; + case 3: + buf.append('D'); + break; + case 9: + buf.append('Z'); + break; + case 10: + buf.append('B'); + break; + case 11: + buf.append('C'); + break; + case 12: + buf.append('S'); + break; + default: + buf.append('J'); + } + } + stackMap.putByte(7).putShort(cw.newClass(buf.toString())); + } + } +} + +private void writeFrameType(final Object type){ + if(type instanceof String) + { + stackMap.putByte(7).putShort(cw.newClass((String) type)); + } + else if(type instanceof Integer) + { + stackMap.putByte(((Integer) type).intValue()); + } + else + { + stackMap.putByte(8).putShort(((Label) type).position); + } +} + +// ------------------------------------------------------------------------ +// Utility methods: dump bytecode array +// ------------------------------------------------------------------------ + +/** + * Returns the size of the bytecode of this method. + * + * @return the size of the bytecode of this method. + */ +final int getSize(){ + if(classReaderOffset != 0) + { + return 6 + classReaderLength; + } + if(resize) + { + // replaces the temporary jump opcodes introduced by Label.resolve. + resizeInstructions(); + } + int size = 8; + if(code.length > 0) + { + cw.newUTF8("Code"); + size += 18 + code.length + 8 * handlerCount; + if(localVar != null) + { + cw.newUTF8("LocalVariableTable"); + size += 8 + localVar.length; + } + if(localVarType != null) + { + cw.newUTF8("LocalVariableTypeTable"); + size += 8 + localVarType.length; + } + if(lineNumber != null) + { + cw.newUTF8("LineNumberTable"); + size += 8 + lineNumber.length; + } + if(stackMap != null) + { + boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; + cw.newUTF8(zip ? "StackMapTable" : "StackMap"); + size += 8 + stackMap.length; + } + if(cattrs != null) + { + size += cattrs.getSize(cw, + code.data, + code.length, + maxStack, + maxLocals); + } + } + if(exceptionCount > 0) + { + cw.newUTF8("Exceptions"); + size += 8 + 2 * exceptionCount; + } + if((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + cw.newUTF8("Synthetic"); + size += 6; + } + if((access & Opcodes.ACC_DEPRECATED) != 0) + { + cw.newUTF8("Deprecated"); + size += 6; + } + if(signature != null) + { + cw.newUTF8("Signature"); + cw.newUTF8(signature); + size += 8; + } + if(annd != null) + { + cw.newUTF8("AnnotationDefault"); + size += 6 + annd.length; + } + if(anns != null) + { + cw.newUTF8("RuntimeVisibleAnnotations"); + size += 8 + anns.getSize(); + } + if(ianns != null) + { + cw.newUTF8("RuntimeInvisibleAnnotations"); + size += 8 + ianns.getSize(); + } + if(panns != null) + { + cw.newUTF8("RuntimeVisibleParameterAnnotations"); + size += 7 + 2 * panns.length; + for(int i = panns.length - 1; i >= 0; --i) + { + size += panns[i] == null ? 0 : panns[i].getSize(); + } + } + if(ipanns != null) + { + cw.newUTF8("RuntimeInvisibleParameterAnnotations"); + size += 7 + 2 * ipanns.length; + for(int i = ipanns.length - 1; i >= 0; --i) + { + size += ipanns[i] == null ? 0 : ipanns[i].getSize(); + } + } + if(attrs != null) + { + size += attrs.getSize(cw, null, 0, -1, -1); + } + return size; +} + +/** + * Puts the bytecode of this method in the given byte vector. + * + * @param out the byte vector into which the bytecode of this method must be + * copied. + */ +final void put(final ByteVector out){ + out.putShort(access).putShort(name).putShort(desc); + if(classReaderOffset != 0) + { + out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); + return; + } + int attributeCount = 0; + if(code.length > 0) + { + ++attributeCount; + } + if(exceptionCount > 0) + { + ++attributeCount; + } + if((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + ++attributeCount; + } + if((access & Opcodes.ACC_DEPRECATED) != 0) + { + ++attributeCount; + } + if(signature != null) + { + ++attributeCount; + } + if(annd != null) + { + ++attributeCount; + } + if(anns != null) + { + ++attributeCount; + } + if(ianns != null) + { + ++attributeCount; + } + if(panns != null) + { + ++attributeCount; + } + if(ipanns != null) + { + ++attributeCount; + } + if(attrs != null) + { + attributeCount += attrs.getCount(); + } + out.putShort(attributeCount); + if(code.length > 0) + { + int size = 12 + code.length + 8 * handlerCount; + if(localVar != null) + { + size += 8 + localVar.length; + } + if(localVarType != null) + { + size += 8 + localVarType.length; + } + if(lineNumber != null) + { + size += 8 + lineNumber.length; + } + if(stackMap != null) + { + size += 8 + stackMap.length; + } + if(cattrs != null) + { + size += cattrs.getSize(cw, + code.data, + code.length, + maxStack, + maxLocals); + } + out.putShort(cw.newUTF8("Code")).putInt(size); + out.putShort(maxStack).putShort(maxLocals); + out.putInt(code.length).putByteArray(code.data, 0, code.length); + out.putShort(handlerCount); + if(handlerCount > 0) + { + Handler h = firstHandler; + while(h != null) + { + out.putShort(h.start.position) + .putShort(h.end.position) + .putShort(h.handler.position) + .putShort(h.type); + h = h.next; + } + } + attributeCount = 0; + if(localVar != null) + { + ++attributeCount; + } + if(localVarType != null) + { + ++attributeCount; + } + if(lineNumber != null) + { + ++attributeCount; + } + if(stackMap != null) + { + ++attributeCount; + } + if(cattrs != null) + { + attributeCount += cattrs.getCount(); + } + out.putShort(attributeCount); + if(localVar != null) + { + out.putShort(cw.newUTF8("LocalVariableTable")); + out.putInt(localVar.length + 2).putShort(localVarCount); + out.putByteArray(localVar.data, 0, localVar.length); + } + if(localVarType != null) + { + out.putShort(cw.newUTF8("LocalVariableTypeTable")); + out.putInt(localVarType.length + 2).putShort(localVarTypeCount); + out.putByteArray(localVarType.data, 0, localVarType.length); + } + if(lineNumber != null) + { + out.putShort(cw.newUTF8("LineNumberTable")); + out.putInt(lineNumber.length + 2).putShort(lineNumberCount); + out.putByteArray(lineNumber.data, 0, lineNumber.length); + } + if(stackMap != null) + { + boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; + out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap")); + out.putInt(stackMap.length + 2).putShort(frameCount); + out.putByteArray(stackMap.data, 0, stackMap.length); + } + if(cattrs != null) + { + cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); + } + } + if(exceptionCount > 0) + { + out.putShort(cw.newUTF8("Exceptions")) + .putInt(2 * exceptionCount + 2); + out.putShort(exceptionCount); + for(int i = 0; i < exceptionCount; ++i) + { + out.putShort(exceptions[i]); + } + } + if((access & Opcodes.ACC_SYNTHETIC) != 0 + && (cw.version & 0xffff) < Opcodes.V1_5) + { + out.putShort(cw.newUTF8("Synthetic")).putInt(0); + } + if((access & Opcodes.ACC_DEPRECATED) != 0) + { + out.putShort(cw.newUTF8("Deprecated")).putInt(0); + } + if(signature != null) + { + out.putShort(cw.newUTF8("Signature")) + .putInt(2) + .putShort(cw.newUTF8(signature)); + } + if(annd != null) + { + out.putShort(cw.newUTF8("AnnotationDefault")); + out.putInt(annd.length); + out.putByteArray(annd.data, 0, annd.length); + } + if(anns != null) + { + out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); + anns.put(out); + } + if(ianns != null) + { + out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); + ianns.put(out); + } + if(panns != null) + { + out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); + AnnotationWriter.put(panns, out); + } + if(ipanns != null) + { + out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); + AnnotationWriter.put(ipanns, out); + } + if(attrs != null) + { + attrs.put(cw, null, 0, -1, -1, out); + } +} + +// ------------------------------------------------------------------------ +// Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) +// ------------------------------------------------------------------------ + +/** + * Resizes and replaces the temporary instructions inserted by + * {@link Label#resolve} for wide forward jumps, while keeping jump offsets + * and instruction addresses consistent. This may require to resize other + * existing instructions, or even to introduce new instructions: for + * example, increasing the size of an instruction by 2 at the middle of a + * method can increases the offset of an IFEQ instruction from 32766 to + * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W + * 32765. This, in turn, may require to increase the size of another jump + * instruction, and so on... All these operations are handled automatically + * by this method.

This method must be called after all the method + * that is being built has been visited. In particular, the + * {@link Label Label} objects used to construct the method are no longer + * valid after this method has been called. + */ +private void resizeInstructions(){ + byte[] b = code.data; // bytecode of the method + int u, v, label; // indexes in b + int i, j; // loop indexes + /* + * 1st step: As explained above, resizing an instruction may require to + * resize another one, which may require to resize yet another one, and + * so on. The first step of the algorithm consists in finding all the + * instructions that need to be resized, without modifying the code. + * This is done by the following "fix point" algorithm: + * + * Parse the code to find the jump instructions whose offset will need + * more than 2 bytes to be stored (the future offset is computed from + * the current offset and from the number of bytes that will be inserted + * or removed between the source and target instructions). For each such + * instruction, adds an entry in (a copy of) the indexes and sizes + * arrays (if this has not already been done in a previous iteration!). + * + * If at least one entry has been added during the previous step, go + * back to the beginning, otherwise stop. + * + * In fact the real algorithm is complicated by the fact that the size + * of TABLESWITCH and LOOKUPSWITCH instructions depends on their + * position in the bytecode (because of padding). In order to ensure the + * convergence of the algorithm, the number of bytes to be added or + * removed from these instructions is over estimated during the previous + * loop, and computed exactly only after the loop is finished (this + * requires another pass to parse the bytecode of the method). + */ + int[] allIndexes = new int[0]; // copy of indexes + int[] allSizes = new int[0]; // copy of sizes + boolean[] resize; // instructions to be resized + int newOffset; // future offset of a jump instruction + + resize = new boolean[code.length]; + + // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done + int state = 3; + do + { + if(state == 3) + { + state = 2; + } + u = 0; + while(u < b.length) + { + int opcode = b[u] & 0xFF; // opcode of current instruction + int insert = 0; // bytes to be added after this instruction + + switch(ClassWriter.TYPE[opcode]) + { + case ClassWriter.NOARG_INSN: + case ClassWriter.IMPLVAR_INSN: + u += 1; + break; + case ClassWriter.LABEL_INSN: + if(opcode > 201) + { + // converts temporary opcodes 202 to 217, 218 and + // 219 to IFEQ ... JSR (inclusive), IFNULL and + // IFNONNULL + opcode = opcode < 218 ? opcode - 49 : opcode - 20; + label = u + readUnsignedShort(b, u + 1); + } + else + { + label = u + readShort(b, u + 1); + } + newOffset = getNewOffset(allIndexes, allSizes, u, label); + if(newOffset < Short.MIN_VALUE + || newOffset > Short.MAX_VALUE) + { + if(!resize[u]) + { + if(opcode == Opcodes.GOTO + || opcode == Opcodes.JSR) + { + // two additional bytes will be required to + // replace this GOTO or JSR instruction with + // a GOTO_W or a JSR_W + insert = 2; + } + else + { + // five additional bytes will be required to + // replace this IFxxx instruction with + // IFNOTxxx GOTO_W , where IFNOTxxx + // is the "opposite" opcode of IFxxx (i.e., + // IFNE for IFEQ) and where designates + // the instruction just after the GOTO_W. + insert = 5; + } + resize[u] = true; + } + } + u += 3; + break; + case ClassWriter.LABELW_INSN: + u += 5; + break; + case ClassWriter.TABL_INSN: + if(state == 1) + { + // true number of bytes to be added (or removed) + // from this instruction = (future number of padding + // bytes - current number of padding byte) - + // previously over estimated variation = + // = ((3 - newOffset%4) - (3 - u%4)) - u%4 + // = (-newOffset%4 + u%4) - u%4 + // = -(newOffset & 3) + newOffset = getNewOffset(allIndexes, allSizes, 0, u); + insert = -(newOffset & 3); + } + else if(!resize[u]) + { + // over estimation of the number of bytes to be + // added to this instruction = 3 - current number + // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3 + insert = u & 3; + resize[u] = true; + } + // skips instruction + u = u + 4 - (u & 3); + u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12; + break; + case ClassWriter.LOOK_INSN: + if(state == 1) + { + // like TABL_INSN + newOffset = getNewOffset(allIndexes, allSizes, 0, u); + insert = -(newOffset & 3); + } + else if(!resize[u]) + { + // like TABL_INSN + insert = u & 3; + resize[u] = true; + } + // skips instruction + u = u + 4 - (u & 3); + u += 8 * readInt(b, u + 4) + 8; + break; + case ClassWriter.WIDE_INSN: + opcode = b[u + 1] & 0xFF; + if(opcode == Opcodes.IINC) + { + u += 6; + } + else + { + u += 4; + } + break; + case ClassWriter.VAR_INSN: + case ClassWriter.SBYTE_INSN: + case ClassWriter.LDC_INSN: + u += 2; + break; + case ClassWriter.SHORT_INSN: + case ClassWriter.LDCW_INSN: + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.TYPE_INSN: + case ClassWriter.IINC_INSN: + u += 3; + break; + case ClassWriter.ITFMETH_INSN: + u += 5; + break; + // case ClassWriter.MANA_INSN: + default: + u += 4; + break; + } + if(insert != 0) + { + // adds a new (u, insert) entry in the allIndexes and + // allSizes arrays + int[] newIndexes = new int[allIndexes.length + 1]; + int[] newSizes = new int[allSizes.length + 1]; + System.arraycopy(allIndexes, + 0, + newIndexes, + 0, + allIndexes.length); + System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length); + newIndexes[allIndexes.length] = u; + newSizes[allSizes.length] = insert; + allIndexes = newIndexes; + allSizes = newSizes; + if(insert > 0) + { + state = 3; + } + } + } + if(state < 3) + { + --state; + } + } while(state != 0); + + // 2nd step: + // copies the bytecode of the method into a new bytevector, updates the + // offsets, and inserts (or removes) bytes as requested. + + ByteVector newCode = new ByteVector(code.length); + + u = 0; + while(u < code.length) + { + int opcode = b[u] & 0xFF; + switch(ClassWriter.TYPE[opcode]) + { + case ClassWriter.NOARG_INSN: + case ClassWriter.IMPLVAR_INSN: + newCode.putByte(opcode); + u += 1; + break; + case ClassWriter.LABEL_INSN: + if(opcode > 201) + { + // changes temporary opcodes 202 to 217 (inclusive), 218 + // and 219 to IFEQ ... JSR (inclusive), IFNULL and + // IFNONNULL + opcode = opcode < 218 ? opcode - 49 : opcode - 20; + label = u + readUnsignedShort(b, u + 1); + } + else + { + label = u + readShort(b, u + 1); + } + newOffset = getNewOffset(allIndexes, allSizes, u, label); + if(resize[u]) + { + // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx + // with IFNOTxxx GOTO_W , where IFNOTxxx is + // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) + // and where designates the instruction just after + // the GOTO_W. + if(opcode == Opcodes.GOTO) + { + newCode.putByte(200); // GOTO_W + } + else if(opcode == Opcodes.JSR) + { + newCode.putByte(201); // JSR_W + } + else + { + newCode.putByte(opcode <= 166 + ? ((opcode + 1) ^ 1) - 1 + : opcode ^ 1); + newCode.putShort(8); // jump offset + newCode.putByte(200); // GOTO_W + // newOffset now computed from start of GOTO_W + newOffset -= 3; + } + newCode.putInt(newOffset); + } + else + { + newCode.putByte(opcode); + newCode.putShort(newOffset); + } + u += 3; + break; + case ClassWriter.LABELW_INSN: + label = u + readInt(b, u + 1); + newOffset = getNewOffset(allIndexes, allSizes, u, label); + newCode.putByte(opcode); + newCode.putInt(newOffset); + u += 5; + break; + case ClassWriter.TABL_INSN: + // skips 0 to 3 padding bytes + v = u; + u = u + 4 - (v & 3); + // reads and copies instruction + newCode.putByte(Opcodes.TABLESWITCH); + newCode.length += (4 - newCode.length % 4) % 4; + label = v + readInt(b, u); + u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + j = readInt(b, u); + u += 4; + newCode.putInt(j); + j = readInt(b, u) - j + 1; + u += 4; + newCode.putInt(readInt(b, u - 4)); + for(; j > 0; --j) + { + label = v + readInt(b, u); + u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + } + break; + case ClassWriter.LOOK_INSN: + // skips 0 to 3 padding bytes + v = u; + u = u + 4 - (v & 3); + // reads and copies instruction + newCode.putByte(Opcodes.LOOKUPSWITCH); + newCode.length += (4 - newCode.length % 4) % 4; + label = v + readInt(b, u); + u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + j = readInt(b, u); + u += 4; + newCode.putInt(j); + for(; j > 0; --j) + { + newCode.putInt(readInt(b, u)); + u += 4; + label = v + readInt(b, u); + u += 4; + newOffset = getNewOffset(allIndexes, allSizes, v, label); + newCode.putInt(newOffset); + } + break; + case ClassWriter.WIDE_INSN: + opcode = b[u + 1] & 0xFF; + if(opcode == Opcodes.IINC) + { + newCode.putByteArray(b, u, 6); + u += 6; + } + else + { + newCode.putByteArray(b, u, 4); + u += 4; + } + break; + case ClassWriter.VAR_INSN: + case ClassWriter.SBYTE_INSN: + case ClassWriter.LDC_INSN: + newCode.putByteArray(b, u, 2); + u += 2; + break; + case ClassWriter.SHORT_INSN: + case ClassWriter.LDCW_INSN: + case ClassWriter.FIELDORMETH_INSN: + case ClassWriter.TYPE_INSN: + case ClassWriter.IINC_INSN: + newCode.putByteArray(b, u, 3); + u += 3; + break; + case ClassWriter.ITFMETH_INSN: + newCode.putByteArray(b, u, 5); + u += 5; + break; + // case MANA_INSN: + default: + newCode.putByteArray(b, u, 4); + u += 4; + break; + } + } + + // recomputes the stack map frames + if(frameCount > 0) + { + if(compute == FRAMES) + { + frameCount = 0; + stackMap = null; + previousFrame = null; + frame = null; + Frame f = new Frame(); + f.owner = labels; + Type[] args = Type.getArgumentTypes(descriptor); + f.initInputFrame(cw, access, args, maxLocals); + visitFrame(f); + Label l = labels; + while(l != null) + { + /* + * here we need the original label position. getNewOffset + * must therefore never have been called for this label. + */ + u = l.position - 3; + if((l.status & Label.STORE) != 0 || (u >= 0 && resize[u])) + { + getNewOffset(allIndexes, allSizes, l); + // TODO update offsets in UNINITIALIZED values + visitFrame(l.frame); + } + l = l.successor; + } + } + else + { + /* + * Resizing an existing stack map frame table is really hard. + * Not only the table must be parsed to update the offets, but + * new frames may be needed for jump instructions that were + * inserted by this method. And updating the offsets or + * inserting frames can change the format of the following + * frames, in case of packed frames. In practice the whole table + * must be recomputed. For this the frames are marked as + * potentially invalid. This will cause the whole class to be + * reread and rewritten with the COMPUTE_FRAMES option (see the + * ClassWriter.toByteArray method). This is not very efficient + * but is much easier and requires much less code than any other + * method I can think of. + */ + cw.invalidFrames = true; + } + } + // updates the exception handler block labels + Handler h = firstHandler; + while(h != null) + { + getNewOffset(allIndexes, allSizes, h.start); + getNewOffset(allIndexes, allSizes, h.end); + getNewOffset(allIndexes, allSizes, h.handler); + h = h.next; + } + // updates the instructions addresses in the + // local var and line number tables + for(i = 0; i < 2; ++i) + { + ByteVector bv = i == 0 ? localVar : localVarType; + if(bv != null) + { + b = bv.data; + u = 0; + while(u < bv.length) + { + label = readUnsignedShort(b, u); + newOffset = getNewOffset(allIndexes, allSizes, 0, label); + writeShort(b, u, newOffset); + label += readUnsignedShort(b, u + 2); + newOffset = getNewOffset(allIndexes, allSizes, 0, label) + - newOffset; + writeShort(b, u + 2, newOffset); + u += 10; + } + } + } + if(lineNumber != null) + { + b = lineNumber.data; + u = 0; + while(u < lineNumber.length) + { + writeShort(b, u, getNewOffset(allIndexes, + allSizes, + 0, + readUnsignedShort(b, u))); + u += 4; + } + } + // updates the labels of the other attributes + Attribute attr = cattrs; + while(attr != null) + { + Label[] labels = attr.getLabels(); + if(labels != null) + { + for(i = labels.length - 1; i >= 0; --i) + { + getNewOffset(allIndexes, allSizes, labels[i]); + } + } + attr = attr.next; + } + + // replaces old bytecodes with new ones + code = newCode; +} + +/** + * Reads an unsigned short value in the given byte array. + * + * @param b a byte array. + * @param index the start index of the value to be read. + * @return the read value. + */ +static int readUnsignedShort(final byte[] b, final int index){ + return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); +} + +/** + * Reads a signed short value in the given byte array. + * + * @param b a byte array. + * @param index the start index of the value to be read. + * @return the read value. + */ +static short readShort(final byte[] b, final int index){ + return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); +} + +/** + * Reads a signed int value in the given byte array. + * + * @param b a byte array. + * @param index the start index of the value to be read. + * @return the read value. + */ +static int readInt(final byte[] b, final int index){ + return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) + | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); +} + +/** + * Writes a short value in the given byte array. + * + * @param b a byte array. + * @param index where the first byte of the short value must be written. + * @param s the value to be written in the given byte array. + */ +static void writeShort(final byte[] b, final int index, final int s){ + b[index] = (byte) (s >>> 8); + b[index + 1] = (byte) s; +} + +/** + * Computes the future value of a bytecode offset.

Note: it is possible + * to have several entries for the same instruction in the indexes + * and sizes: two entries (index=a,size=b) and (index=a,size=b') + * are equivalent to a single entry (index=a,size=b+b'). + * + * @param indexes current positions of the instructions to be resized. Each + * instruction must be designated by the index of its last + * byte, plus one (or, in other words, by the index of the first + * byte of the next instruction). + * @param sizes the number of bytes to be added to the above + * instructions. More precisely, for each i < len, + * sizes[i] bytes will be added at the end of the + * instruction designated by indexes[i] or, if + * sizes[i] is negative, the last |sizes[i]| + * bytes of the instruction will be removed (the instruction size + * must not become negative or null). + * @param begin index of the first byte of the source instruction. + * @param end index of the first byte of the target instruction. + * @return the future value of the given bytecode offset. + */ +static int getNewOffset( + final int[] indexes, + final int[] sizes, + final int begin, + final int end){ + int offset = end - begin; + for(int i = 0; i < indexes.length; ++i) + { + if(begin < indexes[i] && indexes[i] <= end) + { + // forward jump + offset += sizes[i]; + } + else if(end < indexes[i] && indexes[i] <= begin) + { + // backward jump + offset -= sizes[i]; + } + } + return offset; +} + +/** + * Updates the offset of the given label. + * + * @param indexes current positions of the instructions to be resized. Each + * instruction must be designated by the index of its last + * byte, plus one (or, in other words, by the index of the first + * byte of the next instruction). + * @param sizes the number of bytes to be added to the above + * instructions. More precisely, for each i < len, + * sizes[i] bytes will be added at the end of the + * instruction designated by indexes[i] or, if + * sizes[i] is negative, the last |sizes[i]| + * bytes of the instruction will be removed (the instruction size + * must not become negative or null). + * @param label the label whose offset must be updated. + */ +static void getNewOffset( + final int[] indexes, + final int[] sizes, + final Label label){ + if((label.status & Label.RESIZED) == 0) + { + label.position = getNewOffset(indexes, sizes, 0, label.position); + label.status |= Label.RESIZED; + } +} +} diff --git a/src/jvm/clojure/asm/Opcodes.java b/src/jvm/clojure/asm/Opcodes.java index 9c42750..371b5dc 100644 --- a/src/jvm/clojure/asm/Opcodes.java +++ b/src/jvm/clojure/asm/Opcodes.java @@ -1,341 +1,341 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -/** - * Defines the JVM opcodes, access flags and array type codes. This interface - * does not define all the JVM opcodes because some opcodes are automatically - * handled. For example, the xLOAD and xSTORE opcodes are automatically replaced - * by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n - * opcodes are therefore not defined in this interface. Likewise for LDC, - * automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and - * JSR_W. - * - * @author Eric Bruneton - * @author Eugene Kuleshov - */ -public interface Opcodes{ - -// versions - -int V1_1 = 3 << 16 | 45; -int V1_2 = 0 << 16 | 46; -int V1_3 = 0 << 16 | 47; -int V1_4 = 0 << 16 | 48; -int V1_5 = 0 << 16 | 49; -int V1_6 = 0 << 16 | 50; - -// access flags - -int ACC_PUBLIC = 0x0001; // class, field, method -int ACC_PRIVATE = 0x0002; // class, field, method -int ACC_PROTECTED = 0x0004; // class, field, method -int ACC_STATIC = 0x0008; // field, method -int ACC_FINAL = 0x0010; // class, field, method -int ACC_SUPER = 0x0020; // class -int ACC_SYNCHRONIZED = 0x0020; // method -int ACC_VOLATILE = 0x0040; // field -int ACC_BRIDGE = 0x0040; // method -int ACC_VARARGS = 0x0080; // method -int ACC_TRANSIENT = 0x0080; // field -int ACC_NATIVE = 0x0100; // method -int ACC_INTERFACE = 0x0200; // class -int ACC_ABSTRACT = 0x0400; // class, method -int ACC_STRICT = 0x0800; // method -int ACC_SYNTHETIC = 0x1000; // class, field, method -int ACC_ANNOTATION = 0x2000; // class -int ACC_ENUM = 0x4000; // class(?) field inner - -// ASM specific pseudo access flags - -int ACC_DEPRECATED = 131072; // class, field, method - -// types for NEWARRAY - -int T_BOOLEAN = 4; -int T_CHAR = 5; -int T_FLOAT = 6; -int T_DOUBLE = 7; -int T_BYTE = 8; -int T_SHORT = 9; -int T_INT = 10; -int T_LONG = 11; - -// stack map frame types - -/** - * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}. - */ -int F_NEW = -1; - -/** - * Represents a compressed frame with complete frame data. - */ -int F_FULL = 0; - -/** - * Represents a compressed frame where locals are the same as the locals in - * the previous frame, except that additional 1-3 locals are defined, and - * with an empty stack. - */ -int F_APPEND = 1; - -/** - * Represents a compressed frame where locals are the same as the locals in - * the previous frame, except that the last 1-3 locals are absent and with - * an empty stack. - */ -int F_CHOP = 2; - -/** - * Represents a compressed frame with exactly the same locals as the - * previous frame and with an empty stack. - */ -int F_SAME = 3; - -/** - * Represents a compressed frame with exactly the same locals as the - * previous frame and with a single value on the stack. - */ -int F_SAME1 = 4; - -Integer TOP = new Integer(0); -Integer INTEGER = new Integer(1); -Integer FLOAT = new Integer(2); -Integer DOUBLE = new Integer(3); -Integer LONG = new Integer(4); -Integer NULL = new Integer(5); -Integer UNINITIALIZED_THIS = new Integer(6); - -// opcodes // visit method (- = idem) - -int NOP = 0; // visitInsn -int ACONST_NULL = 1; // - -int ICONST_M1 = 2; // - -int ICONST_0 = 3; // - -int ICONST_1 = 4; // - -int ICONST_2 = 5; // - -int ICONST_3 = 6; // - -int ICONST_4 = 7; // - -int ICONST_5 = 8; // - -int LCONST_0 = 9; // - -int LCONST_1 = 10; // - -int FCONST_0 = 11; // - -int FCONST_1 = 12; // - -int FCONST_2 = 13; // - -int DCONST_0 = 14; // - -int DCONST_1 = 15; // - -int BIPUSH = 16; // visitIntInsn -int SIPUSH = 17; // - -int LDC = 18; // visitLdcInsn -// int LDC_W = 19; // - -// int LDC2_W = 20; // - -int ILOAD = 21; // visitVarInsn -int LLOAD = 22; // - -int FLOAD = 23; // - -int DLOAD = 24; // - -int ALOAD = 25; // - -// int ILOAD_0 = 26; // - -// int ILOAD_1 = 27; // - -// int ILOAD_2 = 28; // - -// int ILOAD_3 = 29; // - -// int LLOAD_0 = 30; // - -// int LLOAD_1 = 31; // - -// int LLOAD_2 = 32; // - -// int LLOAD_3 = 33; // - -// int FLOAD_0 = 34; // - -// int FLOAD_1 = 35; // - -// int FLOAD_2 = 36; // - -// int FLOAD_3 = 37; // - -// int DLOAD_0 = 38; // - -// int DLOAD_1 = 39; // - -// int DLOAD_2 = 40; // - -// int DLOAD_3 = 41; // - -// int ALOAD_0 = 42; // - -// int ALOAD_1 = 43; // - -// int ALOAD_2 = 44; // - -// int ALOAD_3 = 45; // - -int IALOAD = 46; // visitInsn -int LALOAD = 47; // - -int FALOAD = 48; // - -int DALOAD = 49; // - -int AALOAD = 50; // - -int BALOAD = 51; // - -int CALOAD = 52; // - -int SALOAD = 53; // - -int ISTORE = 54; // visitVarInsn -int LSTORE = 55; // - -int FSTORE = 56; // - -int DSTORE = 57; // - -int ASTORE = 58; // - -// int ISTORE_0 = 59; // - -// int ISTORE_1 = 60; // - -// int ISTORE_2 = 61; // - -// int ISTORE_3 = 62; // - -// int LSTORE_0 = 63; // - -// int LSTORE_1 = 64; // - -// int LSTORE_2 = 65; // - -// int LSTORE_3 = 66; // - -// int FSTORE_0 = 67; // - -// int FSTORE_1 = 68; // - -// int FSTORE_2 = 69; // - -// int FSTORE_3 = 70; // - -// int DSTORE_0 = 71; // - -// int DSTORE_1 = 72; // - -// int DSTORE_2 = 73; // - -// int DSTORE_3 = 74; // - -// int ASTORE_0 = 75; // - -// int ASTORE_1 = 76; // - -// int ASTORE_2 = 77; // - -// int ASTORE_3 = 78; // - -int IASTORE = 79; // visitInsn -int LASTORE = 80; // - -int FASTORE = 81; // - -int DASTORE = 82; // - -int AASTORE = 83; // - -int BASTORE = 84; // - -int CASTORE = 85; // - -int SASTORE = 86; // - -int POP = 87; // - -int POP2 = 88; // - -int DUP = 89; // - -int DUP_X1 = 90; // - -int DUP_X2 = 91; // - -int DUP2 = 92; // - -int DUP2_X1 = 93; // - -int DUP2_X2 = 94; // - -int SWAP = 95; // - -int IADD = 96; // - -int LADD = 97; // - -int FADD = 98; // - -int DADD = 99; // - -int ISUB = 100; // - -int LSUB = 101; // - -int FSUB = 102; // - -int DSUB = 103; // - -int IMUL = 104; // - -int LMUL = 105; // - -int FMUL = 106; // - -int DMUL = 107; // - -int IDIV = 108; // - -int LDIV = 109; // - -int FDIV = 110; // - -int DDIV = 111; // - -int IREM = 112; // - -int LREM = 113; // - -int FREM = 114; // - -int DREM = 115; // - -int INEG = 116; // - -int LNEG = 117; // - -int FNEG = 118; // - -int DNEG = 119; // - -int ISHL = 120; // - -int LSHL = 121; // - -int ISHR = 122; // - -int LSHR = 123; // - -int IUSHR = 124; // - -int LUSHR = 125; // - -int IAND = 126; // - -int LAND = 127; // - -int IOR = 128; // - -int LOR = 129; // - -int IXOR = 130; // - -int LXOR = 131; // - -int IINC = 132; // visitIincInsn -int I2L = 133; // visitInsn -int I2F = 134; // - -int I2D = 135; // - -int L2I = 136; // - -int L2F = 137; // - -int L2D = 138; // - -int F2I = 139; // - -int F2L = 140; // - -int F2D = 141; // - -int D2I = 142; // - -int D2L = 143; // - -int D2F = 144; // - -int I2B = 145; // - -int I2C = 146; // - -int I2S = 147; // - -int LCMP = 148; // - -int FCMPL = 149; // - -int FCMPG = 150; // - -int DCMPL = 151; // - -int DCMPG = 152; // - -int IFEQ = 153; // visitJumpInsn -int IFNE = 154; // - -int IFLT = 155; // - -int IFGE = 156; // - -int IFGT = 157; // - -int IFLE = 158; // - -int IF_ICMPEQ = 159; // - -int IF_ICMPNE = 160; // - -int IF_ICMPLT = 161; // - -int IF_ICMPGE = 162; // - -int IF_ICMPGT = 163; // - -int IF_ICMPLE = 164; // - -int IF_ACMPEQ = 165; // - -int IF_ACMPNE = 166; // - -int GOTO = 167; // - -int JSR = 168; // - -int RET = 169; // visitVarInsn -int TABLESWITCH = 170; // visiTableSwitchInsn -int LOOKUPSWITCH = 171; // visitLookupSwitch -int IRETURN = 172; // visitInsn -int LRETURN = 173; // - -int FRETURN = 174; // - -int DRETURN = 175; // - -int ARETURN = 176; // - -int RETURN = 177; // - -int GETSTATIC = 178; // visitFieldInsn -int PUTSTATIC = 179; // - -int GETFIELD = 180; // - -int PUTFIELD = 181; // - -int INVOKEVIRTUAL = 182; // visitMethodInsn -int INVOKESPECIAL = 183; // - -int INVOKESTATIC = 184; // - -int INVOKEINTERFACE = 185; // - -// int UNUSED = 186; // NOT VISITED -int NEW = 187; // visitTypeInsn -int NEWARRAY = 188; // visitIntInsn -int ANEWARRAY = 189; // visitTypeInsn -int ARRAYLENGTH = 190; // visitInsn -int ATHROW = 191; // - -int CHECKCAST = 192; // visitTypeInsn -int INSTANCEOF = 193; // - -int MONITORENTER = 194; // visitInsn -int MONITOREXIT = 195; // - -// int WIDE = 196; // NOT VISITED -int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn -int IFNULL = 198; // visitJumpInsn -int IFNONNULL = 199; // - -// int GOTO_W = 200; // - -// int JSR_W = 201; // - -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +/** + * Defines the JVM opcodes, access flags and array type codes. This interface + * does not define all the JVM opcodes because some opcodes are automatically + * handled. For example, the xLOAD and xSTORE opcodes are automatically replaced + * by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n + * opcodes are therefore not defined in this interface. Likewise for LDC, + * automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and + * JSR_W. + * + * @author Eric Bruneton + * @author Eugene Kuleshov + */ +public interface Opcodes{ + +// versions + +int V1_1 = 3 << 16 | 45; +int V1_2 = 0 << 16 | 46; +int V1_3 = 0 << 16 | 47; +int V1_4 = 0 << 16 | 48; +int V1_5 = 0 << 16 | 49; +int V1_6 = 0 << 16 | 50; + +// access flags + +int ACC_PUBLIC = 0x0001; // class, field, method +int ACC_PRIVATE = 0x0002; // class, field, method +int ACC_PROTECTED = 0x0004; // class, field, method +int ACC_STATIC = 0x0008; // field, method +int ACC_FINAL = 0x0010; // class, field, method +int ACC_SUPER = 0x0020; // class +int ACC_SYNCHRONIZED = 0x0020; // method +int ACC_VOLATILE = 0x0040; // field +int ACC_BRIDGE = 0x0040; // method +int ACC_VARARGS = 0x0080; // method +int ACC_TRANSIENT = 0x0080; // field +int ACC_NATIVE = 0x0100; // method +int ACC_INTERFACE = 0x0200; // class +int ACC_ABSTRACT = 0x0400; // class, method +int ACC_STRICT = 0x0800; // method +int ACC_SYNTHETIC = 0x1000; // class, field, method +int ACC_ANNOTATION = 0x2000; // class +int ACC_ENUM = 0x4000; // class(?) field inner + +// ASM specific pseudo access flags + +int ACC_DEPRECATED = 131072; // class, field, method + +// types for NEWARRAY + +int T_BOOLEAN = 4; +int T_CHAR = 5; +int T_FLOAT = 6; +int T_DOUBLE = 7; +int T_BYTE = 8; +int T_SHORT = 9; +int T_INT = 10; +int T_LONG = 11; + +// stack map frame types + +/** + * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}. + */ +int F_NEW = -1; + +/** + * Represents a compressed frame with complete frame data. + */ +int F_FULL = 0; + +/** + * Represents a compressed frame where locals are the same as the locals in + * the previous frame, except that additional 1-3 locals are defined, and + * with an empty stack. + */ +int F_APPEND = 1; + +/** + * Represents a compressed frame where locals are the same as the locals in + * the previous frame, except that the last 1-3 locals are absent and with + * an empty stack. + */ +int F_CHOP = 2; + +/** + * Represents a compressed frame with exactly the same locals as the + * previous frame and with an empty stack. + */ +int F_SAME = 3; + +/** + * Represents a compressed frame with exactly the same locals as the + * previous frame and with a single value on the stack. + */ +int F_SAME1 = 4; + +Integer TOP = new Integer(0); +Integer INTEGER = new Integer(1); +Integer FLOAT = new Integer(2); +Integer DOUBLE = new Integer(3); +Integer LONG = new Integer(4); +Integer NULL = new Integer(5); +Integer UNINITIALIZED_THIS = new Integer(6); + +// opcodes // visit method (- = idem) + +int NOP = 0; // visitInsn +int ACONST_NULL = 1; // - +int ICONST_M1 = 2; // - +int ICONST_0 = 3; // - +int ICONST_1 = 4; // - +int ICONST_2 = 5; // - +int ICONST_3 = 6; // - +int ICONST_4 = 7; // - +int ICONST_5 = 8; // - +int LCONST_0 = 9; // - +int LCONST_1 = 10; // - +int FCONST_0 = 11; // - +int FCONST_1 = 12; // - +int FCONST_2 = 13; // - +int DCONST_0 = 14; // - +int DCONST_1 = 15; // - +int BIPUSH = 16; // visitIntInsn +int SIPUSH = 17; // - +int LDC = 18; // visitLdcInsn +// int LDC_W = 19; // - +// int LDC2_W = 20; // - +int ILOAD = 21; // visitVarInsn +int LLOAD = 22; // - +int FLOAD = 23; // - +int DLOAD = 24; // - +int ALOAD = 25; // - +// int ILOAD_0 = 26; // - +// int ILOAD_1 = 27; // - +// int ILOAD_2 = 28; // - +// int ILOAD_3 = 29; // - +// int LLOAD_0 = 30; // - +// int LLOAD_1 = 31; // - +// int LLOAD_2 = 32; // - +// int LLOAD_3 = 33; // - +// int FLOAD_0 = 34; // - +// int FLOAD_1 = 35; // - +// int FLOAD_2 = 36; // - +// int FLOAD_3 = 37; // - +// int DLOAD_0 = 38; // - +// int DLOAD_1 = 39; // - +// int DLOAD_2 = 40; // - +// int DLOAD_3 = 41; // - +// int ALOAD_0 = 42; // - +// int ALOAD_1 = 43; // - +// int ALOAD_2 = 44; // - +// int ALOAD_3 = 45; // - +int IALOAD = 46; // visitInsn +int LALOAD = 47; // - +int FALOAD = 48; // - +int DALOAD = 49; // - +int AALOAD = 50; // - +int BALOAD = 51; // - +int CALOAD = 52; // - +int SALOAD = 53; // - +int ISTORE = 54; // visitVarInsn +int LSTORE = 55; // - +int FSTORE = 56; // - +int DSTORE = 57; // - +int ASTORE = 58; // - +// int ISTORE_0 = 59; // - +// int ISTORE_1 = 60; // - +// int ISTORE_2 = 61; // - +// int ISTORE_3 = 62; // - +// int LSTORE_0 = 63; // - +// int LSTORE_1 = 64; // - +// int LSTORE_2 = 65; // - +// int LSTORE_3 = 66; // - +// int FSTORE_0 = 67; // - +// int FSTORE_1 = 68; // - +// int FSTORE_2 = 69; // - +// int FSTORE_3 = 70; // - +// int DSTORE_0 = 71; // - +// int DSTORE_1 = 72; // - +// int DSTORE_2 = 73; // - +// int DSTORE_3 = 74; // - +// int ASTORE_0 = 75; // - +// int ASTORE_1 = 76; // - +// int ASTORE_2 = 77; // - +// int ASTORE_3 = 78; // - +int IASTORE = 79; // visitInsn +int LASTORE = 80; // - +int FASTORE = 81; // - +int DASTORE = 82; // - +int AASTORE = 83; // - +int BASTORE = 84; // - +int CASTORE = 85; // - +int SASTORE = 86; // - +int POP = 87; // - +int POP2 = 88; // - +int DUP = 89; // - +int DUP_X1 = 90; // - +int DUP_X2 = 91; // - +int DUP2 = 92; // - +int DUP2_X1 = 93; // - +int DUP2_X2 = 94; // - +int SWAP = 95; // - +int IADD = 96; // - +int LADD = 97; // - +int FADD = 98; // - +int DADD = 99; // - +int ISUB = 100; // - +int LSUB = 101; // - +int FSUB = 102; // - +int DSUB = 103; // - +int IMUL = 104; // - +int LMUL = 105; // - +int FMUL = 106; // - +int DMUL = 107; // - +int IDIV = 108; // - +int LDIV = 109; // - +int FDIV = 110; // - +int DDIV = 111; // - +int IREM = 112; // - +int LREM = 113; // - +int FREM = 114; // - +int DREM = 115; // - +int INEG = 116; // - +int LNEG = 117; // - +int FNEG = 118; // - +int DNEG = 119; // - +int ISHL = 120; // - +int LSHL = 121; // - +int ISHR = 122; // - +int LSHR = 123; // - +int IUSHR = 124; // - +int LUSHR = 125; // - +int IAND = 126; // - +int LAND = 127; // - +int IOR = 128; // - +int LOR = 129; // - +int IXOR = 130; // - +int LXOR = 131; // - +int IINC = 132; // visitIincInsn +int I2L = 133; // visitInsn +int I2F = 134; // - +int I2D = 135; // - +int L2I = 136; // - +int L2F = 137; // - +int L2D = 138; // - +int F2I = 139; // - +int F2L = 140; // - +int F2D = 141; // - +int D2I = 142; // - +int D2L = 143; // - +int D2F = 144; // - +int I2B = 145; // - +int I2C = 146; // - +int I2S = 147; // - +int LCMP = 148; // - +int FCMPL = 149; // - +int FCMPG = 150; // - +int DCMPL = 151; // - +int DCMPG = 152; // - +int IFEQ = 153; // visitJumpInsn +int IFNE = 154; // - +int IFLT = 155; // - +int IFGE = 156; // - +int IFGT = 157; // - +int IFLE = 158; // - +int IF_ICMPEQ = 159; // - +int IF_ICMPNE = 160; // - +int IF_ICMPLT = 161; // - +int IF_ICMPGE = 162; // - +int IF_ICMPGT = 163; // - +int IF_ICMPLE = 164; // - +int IF_ACMPEQ = 165; // - +int IF_ACMPNE = 166; // - +int GOTO = 167; // - +int JSR = 168; // - +int RET = 169; // visitVarInsn +int TABLESWITCH = 170; // visiTableSwitchInsn +int LOOKUPSWITCH = 171; // visitLookupSwitch +int IRETURN = 172; // visitInsn +int LRETURN = 173; // - +int FRETURN = 174; // - +int DRETURN = 175; // - +int ARETURN = 176; // - +int RETURN = 177; // - +int GETSTATIC = 178; // visitFieldInsn +int PUTSTATIC = 179; // - +int GETFIELD = 180; // - +int PUTFIELD = 181; // - +int INVOKEVIRTUAL = 182; // visitMethodInsn +int INVOKESPECIAL = 183; // - +int INVOKESTATIC = 184; // - +int INVOKEINTERFACE = 185; // - +// int UNUSED = 186; // NOT VISITED +int NEW = 187; // visitTypeInsn +int NEWARRAY = 188; // visitIntInsn +int ANEWARRAY = 189; // visitTypeInsn +int ARRAYLENGTH = 190; // visitInsn +int ATHROW = 191; // - +int CHECKCAST = 192; // visitTypeInsn +int INSTANCEOF = 193; // - +int MONITORENTER = 194; // visitInsn +int MONITOREXIT = 195; // - +// int WIDE = 196; // NOT VISITED +int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn +int IFNULL = 198; // visitJumpInsn +int IFNONNULL = 199; // - +// int GOTO_W = 200; // - +// int JSR_W = 201; // - +} diff --git a/src/jvm/clojure/asm/Type.java b/src/jvm/clojure/asm/Type.java index 4cde14a..d593f14 100644 --- a/src/jvm/clojure/asm/Type.java +++ b/src/jvm/clojure/asm/Type.java @@ -1,872 +1,872 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; - -/** - * A Java type. This class can be used to make it easier to manipulate type and - * method descriptors. - * - * @author Eric Bruneton - * @author Chris Nokleberg - */ -public class Type{ - -/** - * The sort of the void type. See {@link #getSort getSort}. - */ -public final static int VOID = 0; - -/** - * The sort of the boolean type. See {@link #getSort getSort}. - */ -public final static int BOOLEAN = 1; - -/** - * The sort of the char type. See {@link #getSort getSort}. - */ -public final static int CHAR = 2; - -/** - * The sort of the byte type. See {@link #getSort getSort}. - */ -public final static int BYTE = 3; - -/** - * The sort of the short type. See {@link #getSort getSort}. - */ -public final static int SHORT = 4; - -/** - * The sort of the int type. See {@link #getSort getSort}. - */ -public final static int INT = 5; - -/** - * The sort of the float type. See {@link #getSort getSort}. - */ -public final static int FLOAT = 6; - -/** - * The sort of the long type. See {@link #getSort getSort}. - */ -public final static int LONG = 7; - -/** - * The sort of the double type. See {@link #getSort getSort}. - */ -public final static int DOUBLE = 8; - -/** - * The sort of array reference types. See {@link #getSort getSort}. - */ -public final static int ARRAY = 9; - -/** - * The sort of object reference type. See {@link #getSort getSort}. - */ -public final static int OBJECT = 10; - -/** - * The void type. - */ -public final static Type VOID_TYPE = new Type(VOID); - -/** - * The boolean type. - */ -public final static Type BOOLEAN_TYPE = new Type(BOOLEAN); - -/** - * The char type. - */ -public final static Type CHAR_TYPE = new Type(CHAR); - -/** - * The byte type. - */ -public final static Type BYTE_TYPE = new Type(BYTE); - -/** - * The short type. - */ -public final static Type SHORT_TYPE = new Type(SHORT); - -/** - * The int type. - */ -public final static Type INT_TYPE = new Type(INT); - -/** - * The float type. - */ -public final static Type FLOAT_TYPE = new Type(FLOAT); - -/** - * The long type. - */ -public final static Type LONG_TYPE = new Type(LONG); - -/** - * The double type. - */ -public final static Type DOUBLE_TYPE = new Type(DOUBLE); - -// ------------------------------------------------------------------------ -// Fields -// ------------------------------------------------------------------------ - -/** - * The sort of this Java type. - */ -private final int sort; - -/** - * A buffer containing the descriptor of this Java type. This field is only - * used for reference types. - */ -private char[] buf; - -/** - * The offset of the descriptor of this Java type in {@link #buf buf}. This - * field is only used for reference types. - */ -private int off; - -/** - * The length of the descriptor of this Java type. - */ -private int len; - -// ------------------------------------------------------------------------ -// Constructors -// ------------------------------------------------------------------------ - -/** - * Constructs a primitive type. - * - * @param sort the sort of the primitive type to be constructed. - */ -private Type(final int sort){ - this.sort = sort; - this.len = 1; -} - -/** - * Constructs a reference type. - * - * @param sort the sort of the reference type to be constructed. - * @param buf a buffer containing the descriptor of the previous type. - * @param off the offset of this descriptor in the previous buffer. - * @param len the length of this descriptor. - */ -private Type(final int sort, final char[] buf, final int off, final int len){ - this.sort = sort; - this.buf = buf; - this.off = off; - this.len = len; -} - -/** - * Returns the Java type corresponding to the given type descriptor. - * - * @param typeDescriptor a type descriptor. - * @return the Java type corresponding to the given type descriptor. - */ -public static Type getType(final String typeDescriptor){ - return getType(typeDescriptor.toCharArray(), 0); -} - -/** - * Returns the Java type corresponding to the given class. - * - * @param c a class. - * @return the Java type corresponding to the given class. - */ -public static Type getType(final Class c){ - if(c.isPrimitive()) - { - if(c == Integer.TYPE) - { - return INT_TYPE; - } - else if(c == Void.TYPE) - { - return VOID_TYPE; - } - else if(c == Boolean.TYPE) - { - return BOOLEAN_TYPE; - } - else if(c == Byte.TYPE) - { - return BYTE_TYPE; - } - else if(c == Character.TYPE) - { - return CHAR_TYPE; - } - else if(c == Short.TYPE) - { - return SHORT_TYPE; - } - else if(c == Double.TYPE) - { - return DOUBLE_TYPE; - } - else if(c == Float.TYPE) - { - return FLOAT_TYPE; - } - else /* if (c == Long.TYPE) */ - { - return LONG_TYPE; - } - } - else - { - return getType(getDescriptor(c)); - } -} - -/** - * Returns the {@link Type#OBJECT} type for the given internal class name. - * This is a shortcut method for Type.getType("L"+name+";"). - * Note that opposed to {@link Type#getType(String)}, this method takes - * internal class names and not class descriptor. - * - * @param name an internal class name. - * @return the the {@link Type#OBJECT} type for the given class name. - */ -public static Type getObjectType(String name){ - int l = name.length(); - char[] buf = new char[l + 2]; - buf[0] = 'L'; - buf[l + 1] = ';'; - name.getChars(0, l, buf, 1); - return new Type(OBJECT, buf, 0, l + 2); -} - -/** - * Returns the Java types corresponding to the argument types of the given - * method descriptor. - * - * @param methodDescriptor a method descriptor. - * @return the Java types corresponding to the argument types of the given - * method descriptor. - */ -public static Type[] getArgumentTypes(final String methodDescriptor){ - char[] buf = methodDescriptor.toCharArray(); - int off = 1; - int size = 0; - while(true) - { - char car = buf[off++]; - if(car == ')') - { - break; - } - else if(car == 'L') - { - while(buf[off++] != ';') - { - } - ++size; - } - else if(car != '[') - { - ++size; - } - } - Type[] args = new Type[size]; - off = 1; - size = 0; - while(buf[off] != ')') - { - args[size] = getType(buf, off); - off += args[size].len; - size += 1; - } - return args; -} - -/** - * Returns the Java types corresponding to the argument types of the given - * method. - * - * @param method a method. - * @return the Java types corresponding to the argument types of the given - * method. - */ -public static Type[] getArgumentTypes(final Method method){ - Class[] classes = method.getParameterTypes(); - Type[] types = new Type[classes.length]; - for(int i = classes.length - 1; i >= 0; --i) - { - types[i] = getType(classes[i]); - } - return types; -} - -/** - * Returns the Java type corresponding to the return type of the given - * method descriptor. - * - * @param methodDescriptor a method descriptor. - * @return the Java type corresponding to the return type of the given - * method descriptor. - */ -public static Type getReturnType(final String methodDescriptor){ - char[] buf = methodDescriptor.toCharArray(); - return getType(buf, methodDescriptor.indexOf(')') + 1); -} - -/** - * Returns the Java type corresponding to the return type of the given - * method. - * - * @param method a method. - * @return the Java type corresponding to the return type of the given - * method. - */ -public static Type getReturnType(final Method method){ - return getType(method.getReturnType()); -} - -/** - * Returns the Java type corresponding to the given type descriptor. - * - * @param buf a buffer containing a type descriptor. - * @param off the offset of this descriptor in the previous buffer. - * @return the Java type corresponding to the given type descriptor. - */ -private static Type getType(final char[] buf, final int off){ - int len; - switch(buf[off]) - { - case'V': - return VOID_TYPE; - case'Z': - return BOOLEAN_TYPE; - case'C': - return CHAR_TYPE; - case'B': - return BYTE_TYPE; - case'S': - return SHORT_TYPE; - case'I': - return INT_TYPE; - case'F': - return FLOAT_TYPE; - case'J': - return LONG_TYPE; - case'D': - return DOUBLE_TYPE; - case'[': - len = 1; - while(buf[off + len] == '[') - { - ++len; - } - if(buf[off + len] == 'L') - { - ++len; - while(buf[off + len] != ';') - { - ++len; - } - } - return new Type(ARRAY, buf, off, len + 1); - // case 'L': - default: - len = 1; - while(buf[off + len] != ';') - { - ++len; - } - return new Type(OBJECT, buf, off, len + 1); - } -} - -// ------------------------------------------------------------------------ -// Accessors -// ------------------------------------------------------------------------ - -/** - * Returns the sort of this Java type. - * - * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, - * {@link #CHAR CHAR}, {@link #BYTE BYTE}, {@link #SHORT SHORT}, - * {@link #INT INT}, {@link #FLOAT FLOAT}, {@link #LONG LONG}, - * {@link #DOUBLE DOUBLE}, {@link #ARRAY ARRAY} or - * {@link #OBJECT OBJECT}. - */ -public int getSort(){ - return sort; -} - -/** - * Returns the number of dimensions of this array type. This method should - * only be used for an array type. - * - * @return the number of dimensions of this array type. - */ -public int getDimensions(){ - int i = 1; - while(buf[off + i] == '[') - { - ++i; - } - return i; -} - -/** - * Returns the type of the elements of this array type. This method should - * only be used for an array type. - * - * @return Returns the type of the elements of this array type. - */ -public Type getElementType(){ - return getType(buf, off + getDimensions()); -} - -/** - * Returns the name of the class corresponding to this type. - * - * @return the fully qualified name of the class corresponding to this type. - */ -public String getClassName(){ - switch(sort) - { - case VOID: - return "void"; - case BOOLEAN: - return "boolean"; - case CHAR: - return "char"; - case BYTE: - return "byte"; - case SHORT: - return "short"; - case INT: - return "int"; - case FLOAT: - return "float"; - case LONG: - return "long"; - case DOUBLE: - return "double"; - case ARRAY: - StringBuffer b = new StringBuffer(getElementType().getClassName()); - for(int i = getDimensions(); i > 0; --i) - { - b.append("[]"); - } - return b.toString(); - // case OBJECT: - default: - return new String(buf, off + 1, len - 2).replace('/', '.'); - } -} - -/** - * Returns the internal name of the class corresponding to this object type. - * The internal name of a class is its fully qualified name, where '.' are - * replaced by '/'. This method should only be used for an object type. - * - * @return the internal name of the class corresponding to this object type. - */ -public String getInternalName(){ - return new String(buf, off + 1, len - 2); -} - -// ------------------------------------------------------------------------ -// Conversion to type descriptors -// ------------------------------------------------------------------------ - -/** - * Returns the descriptor corresponding to this Java type. - * - * @return the descriptor corresponding to this Java type. - */ -public String getDescriptor(){ - StringBuffer buf = new StringBuffer(); - getDescriptor(buf); - return buf.toString(); -} - -/** - * Returns the descriptor corresponding to the given argument and return - * types. - * - * @param returnType the return type of the method. - * @param argumentTypes the argument types of the method. - * @return the descriptor corresponding to the given argument and return - * types. - */ -public static String getMethodDescriptor( - final Type returnType, - final Type[] argumentTypes){ - StringBuffer buf = new StringBuffer(); - buf.append('('); - for(int i = 0; i < argumentTypes.length; ++i) - { - argumentTypes[i].getDescriptor(buf); - } - buf.append(')'); - returnType.getDescriptor(buf); - return buf.toString(); -} - -/** - * Appends the descriptor corresponding to this Java type to the given - * string buffer. - * - * @param buf the string buffer to which the descriptor must be appended. - */ -private void getDescriptor(final StringBuffer buf){ - switch(sort) - { - case VOID: - buf.append('V'); - return; - case BOOLEAN: - buf.append('Z'); - return; - case CHAR: - buf.append('C'); - return; - case BYTE: - buf.append('B'); - return; - case SHORT: - buf.append('S'); - return; - case INT: - buf.append('I'); - return; - case FLOAT: - buf.append('F'); - return; - case LONG: - buf.append('J'); - return; - case DOUBLE: - buf.append('D'); - return; - // case ARRAY: - // case OBJECT: - default: - buf.append(this.buf, off, len); - } -} - -// ------------------------------------------------------------------------ -// Direct conversion from classes to type descriptors, -// without intermediate Type objects -// ------------------------------------------------------------------------ - -/** - * Returns the internal name of the given class. The internal name of a - * class is its fully qualified name, where '.' are replaced by '/'. - * - * @param c an object class. - * @return the internal name of the given class. - */ -public static String getInternalName(final Class c){ - return c.getName().replace('.', '/'); -} - -/** - * Returns the descriptor corresponding to the given Java type. - * - * @param c an object class, a primitive class or an array class. - * @return the descriptor corresponding to the given class. - */ -public static String getDescriptor(final Class c){ - StringBuffer buf = new StringBuffer(); - getDescriptor(buf, c); - return buf.toString(); -} - -/** - * Returns the descriptor corresponding to the given constructor. - * - * @param c a {@link Constructor Constructor} object. - * @return the descriptor of the given constructor. - */ -public static String getConstructorDescriptor(final Constructor c){ - Class[] parameters = c.getParameterTypes(); - StringBuffer buf = new StringBuffer(); - buf.append('('); - for(int i = 0; i < parameters.length; ++i) - { - getDescriptor(buf, parameters[i]); - } - return buf.append(")V").toString(); -} - -/** - * Returns the descriptor corresponding to the given method. - * - * @param m a {@link Method Method} object. - * @return the descriptor of the given method. - */ -public static String getMethodDescriptor(final Method m){ - Class[] parameters = m.getParameterTypes(); - StringBuffer buf = new StringBuffer(); - buf.append('('); - for(int i = 0; i < parameters.length; ++i) - { - getDescriptor(buf, parameters[i]); - } - buf.append(')'); - getDescriptor(buf, m.getReturnType()); - return buf.toString(); -} - -/** - * Appends the descriptor of the given class to the given string buffer. - * - * @param buf the string buffer to which the descriptor must be appended. - * @param c the class whose descriptor must be computed. - */ -private static void getDescriptor(final StringBuffer buf, final Class c){ - Class d = c; - while(true) - { - if(d.isPrimitive()) - { - char car; - if(d == Integer.TYPE) - { - car = 'I'; - } - else if(d == Void.TYPE) - { - car = 'V'; - } - else if(d == Boolean.TYPE) - { - car = 'Z'; - } - else if(d == Byte.TYPE) - { - car = 'B'; - } - else if(d == Character.TYPE) - { - car = 'C'; - } - else if(d == Short.TYPE) - { - car = 'S'; - } - else if(d == Double.TYPE) - { - car = 'D'; - } - else if(d == Float.TYPE) - { - car = 'F'; - } - else /* if (d == Long.TYPE) */ - { - car = 'J'; - } - buf.append(car); - return; - } - else if(d.isArray()) - { - buf.append('['); - d = d.getComponentType(); - } - else - { - buf.append('L'); - String name = d.getName(); - int len = name.length(); - for(int i = 0; i < len; ++i) - { - char car = name.charAt(i); - buf.append(car == '.' ? '/' : car); - } - buf.append(';'); - return; - } - } -} - -// ------------------------------------------------------------------------ -// Corresponding size and opcodes -// ------------------------------------------------------------------------ - -/** - * Returns the size of values of this type. - * - * @return the size of values of this type, i.e., 2 for long and - * double, and 1 otherwise. - */ -public int getSize(){ - return sort == LONG || sort == DOUBLE ? 2 : 1; -} - -/** - * Returns a JVM instruction opcode adapted to this Java type. - * - * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, - * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, - * ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. - * @return an opcode that is similar to the given opcode, but adapted to - * this Java type. For example, if this type is float and - * opcode is IRETURN, this method returns FRETURN. - */ -public int getOpcode(final int opcode){ - if(opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) - { - switch(sort) - { - case BOOLEAN: - case BYTE: - return opcode + 5; - case CHAR: - return opcode + 6; - case SHORT: - return opcode + 7; - case INT: - return opcode; - case FLOAT: - return opcode + 2; - case LONG: - return opcode + 1; - case DOUBLE: - return opcode + 3; - // case ARRAY: - // case OBJECT: - default: - return opcode + 4; - } - } - else - { - switch(sort) - { - case VOID: - return opcode + 5; - case BOOLEAN: - case CHAR: - case BYTE: - case SHORT: - case INT: - return opcode; - case FLOAT: - return opcode + 2; - case LONG: - return opcode + 1; - case DOUBLE: - return opcode + 3; - // case ARRAY: - // case OBJECT: - default: - return opcode + 4; - } - } -} - -// ------------------------------------------------------------------------ -// Equals, hashCode and toString -// ------------------------------------------------------------------------ - -/** - * Tests if the given object is equal to this type. - * - * @param o the object to be compared to this type. - * @return true if the given object is equal to this type. - */ -public boolean equals(final Object o){ - if(this == o) - { - return true; - } - if(!(o instanceof Type)) - { - return false; - } - Type t = (Type) o; - if(sort != t.sort) - { - return false; - } - if(sort == Type.OBJECT || sort == Type.ARRAY) - { - if(len != t.len) - { - return false; - } - for(int i = off, j = t.off, end = i + len; i < end; i++, j++) - { - if(buf[i] != t.buf[j]) - { - return false; - } - } - } - return true; -} - -/** - * Returns a hash code value for this type. - * - * @return a hash code value for this type. - */ -public int hashCode(){ - int hc = 13 * sort; - if(sort == Type.OBJECT || sort == Type.ARRAY) - { - for(int i = off, end = i + len; i < end; i++) - { - hc = 17 * (hc + buf[i]); - } - } - return hc; -} - -/** - * Returns a string representation of this type. - * - * @return the descriptor of this type. - */ -public String toString(){ - return getDescriptor(); -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +/** + * A Java type. This class can be used to make it easier to manipulate type and + * method descriptors. + * + * @author Eric Bruneton + * @author Chris Nokleberg + */ +public class Type{ + +/** + * The sort of the void type. See {@link #getSort getSort}. + */ +public final static int VOID = 0; + +/** + * The sort of the boolean type. See {@link #getSort getSort}. + */ +public final static int BOOLEAN = 1; + +/** + * The sort of the char type. See {@link #getSort getSort}. + */ +public final static int CHAR = 2; + +/** + * The sort of the byte type. See {@link #getSort getSort}. + */ +public final static int BYTE = 3; + +/** + * The sort of the short type. See {@link #getSort getSort}. + */ +public final static int SHORT = 4; + +/** + * The sort of the int type. See {@link #getSort getSort}. + */ +public final static int INT = 5; + +/** + * The sort of the float type. See {@link #getSort getSort}. + */ +public final static int FLOAT = 6; + +/** + * The sort of the long type. See {@link #getSort getSort}. + */ +public final static int LONG = 7; + +/** + * The sort of the double type. See {@link #getSort getSort}. + */ +public final static int DOUBLE = 8; + +/** + * The sort of array reference types. See {@link #getSort getSort}. + */ +public final static int ARRAY = 9; + +/** + * The sort of object reference type. See {@link #getSort getSort}. + */ +public final static int OBJECT = 10; + +/** + * The void type. + */ +public final static Type VOID_TYPE = new Type(VOID); + +/** + * The boolean type. + */ +public final static Type BOOLEAN_TYPE = new Type(BOOLEAN); + +/** + * The char type. + */ +public final static Type CHAR_TYPE = new Type(CHAR); + +/** + * The byte type. + */ +public final static Type BYTE_TYPE = new Type(BYTE); + +/** + * The short type. + */ +public final static Type SHORT_TYPE = new Type(SHORT); + +/** + * The int type. + */ +public final static Type INT_TYPE = new Type(INT); + +/** + * The float type. + */ +public final static Type FLOAT_TYPE = new Type(FLOAT); + +/** + * The long type. + */ +public final static Type LONG_TYPE = new Type(LONG); + +/** + * The double type. + */ +public final static Type DOUBLE_TYPE = new Type(DOUBLE); + +// ------------------------------------------------------------------------ +// Fields +// ------------------------------------------------------------------------ + +/** + * The sort of this Java type. + */ +private final int sort; + +/** + * A buffer containing the descriptor of this Java type. This field is only + * used for reference types. + */ +private char[] buf; + +/** + * The offset of the descriptor of this Java type in {@link #buf buf}. This + * field is only used for reference types. + */ +private int off; + +/** + * The length of the descriptor of this Java type. + */ +private int len; + +// ------------------------------------------------------------------------ +// Constructors +// ------------------------------------------------------------------------ + +/** + * Constructs a primitive type. + * + * @param sort the sort of the primitive type to be constructed. + */ +private Type(final int sort){ + this.sort = sort; + this.len = 1; +} + +/** + * Constructs a reference type. + * + * @param sort the sort of the reference type to be constructed. + * @param buf a buffer containing the descriptor of the previous type. + * @param off the offset of this descriptor in the previous buffer. + * @param len the length of this descriptor. + */ +private Type(final int sort, final char[] buf, final int off, final int len){ + this.sort = sort; + this.buf = buf; + this.off = off; + this.len = len; +} + +/** + * Returns the Java type corresponding to the given type descriptor. + * + * @param typeDescriptor a type descriptor. + * @return the Java type corresponding to the given type descriptor. + */ +public static Type getType(final String typeDescriptor){ + return getType(typeDescriptor.toCharArray(), 0); +} + +/** + * Returns the Java type corresponding to the given class. + * + * @param c a class. + * @return the Java type corresponding to the given class. + */ +public static Type getType(final Class c){ + if(c.isPrimitive()) + { + if(c == Integer.TYPE) + { + return INT_TYPE; + } + else if(c == Void.TYPE) + { + return VOID_TYPE; + } + else if(c == Boolean.TYPE) + { + return BOOLEAN_TYPE; + } + else if(c == Byte.TYPE) + { + return BYTE_TYPE; + } + else if(c == Character.TYPE) + { + return CHAR_TYPE; + } + else if(c == Short.TYPE) + { + return SHORT_TYPE; + } + else if(c == Double.TYPE) + { + return DOUBLE_TYPE; + } + else if(c == Float.TYPE) + { + return FLOAT_TYPE; + } + else /* if (c == Long.TYPE) */ + { + return LONG_TYPE; + } + } + else + { + return getType(getDescriptor(c)); + } +} + +/** + * Returns the {@link Type#OBJECT} type for the given internal class name. + * This is a shortcut method for Type.getType("L"+name+";"). + * Note that opposed to {@link Type#getType(String)}, this method takes + * internal class names and not class descriptor. + * + * @param name an internal class name. + * @return the the {@link Type#OBJECT} type for the given class name. + */ +public static Type getObjectType(String name){ + int l = name.length(); + char[] buf = new char[l + 2]; + buf[0] = 'L'; + buf[l + 1] = ';'; + name.getChars(0, l, buf, 1); + return new Type(OBJECT, buf, 0, l + 2); +} + +/** + * Returns the Java types corresponding to the argument types of the given + * method descriptor. + * + * @param methodDescriptor a method descriptor. + * @return the Java types corresponding to the argument types of the given + * method descriptor. + */ +public static Type[] getArgumentTypes(final String methodDescriptor){ + char[] buf = methodDescriptor.toCharArray(); + int off = 1; + int size = 0; + while(true) + { + char car = buf[off++]; + if(car == ')') + { + break; + } + else if(car == 'L') + { + while(buf[off++] != ';') + { + } + ++size; + } + else if(car != '[') + { + ++size; + } + } + Type[] args = new Type[size]; + off = 1; + size = 0; + while(buf[off] != ')') + { + args[size] = getType(buf, off); + off += args[size].len; + size += 1; + } + return args; +} + +/** + * Returns the Java types corresponding to the argument types of the given + * method. + * + * @param method a method. + * @return the Java types corresponding to the argument types of the given + * method. + */ +public static Type[] getArgumentTypes(final Method method){ + Class[] classes = method.getParameterTypes(); + Type[] types = new Type[classes.length]; + for(int i = classes.length - 1; i >= 0; --i) + { + types[i] = getType(classes[i]); + } + return types; +} + +/** + * Returns the Java type corresponding to the return type of the given + * method descriptor. + * + * @param methodDescriptor a method descriptor. + * @return the Java type corresponding to the return type of the given + * method descriptor. + */ +public static Type getReturnType(final String methodDescriptor){ + char[] buf = methodDescriptor.toCharArray(); + return getType(buf, methodDescriptor.indexOf(')') + 1); +} + +/** + * Returns the Java type corresponding to the return type of the given + * method. + * + * @param method a method. + * @return the Java type corresponding to the return type of the given + * method. + */ +public static Type getReturnType(final Method method){ + return getType(method.getReturnType()); +} + +/** + * Returns the Java type corresponding to the given type descriptor. + * + * @param buf a buffer containing a type descriptor. + * @param off the offset of this descriptor in the previous buffer. + * @return the Java type corresponding to the given type descriptor. + */ +private static Type getType(final char[] buf, final int off){ + int len; + switch(buf[off]) + { + case'V': + return VOID_TYPE; + case'Z': + return BOOLEAN_TYPE; + case'C': + return CHAR_TYPE; + case'B': + return BYTE_TYPE; + case'S': + return SHORT_TYPE; + case'I': + return INT_TYPE; + case'F': + return FLOAT_TYPE; + case'J': + return LONG_TYPE; + case'D': + return DOUBLE_TYPE; + case'[': + len = 1; + while(buf[off + len] == '[') + { + ++len; + } + if(buf[off + len] == 'L') + { + ++len; + while(buf[off + len] != ';') + { + ++len; + } + } + return new Type(ARRAY, buf, off, len + 1); + // case 'L': + default: + len = 1; + while(buf[off + len] != ';') + { + ++len; + } + return new Type(OBJECT, buf, off, len + 1); + } +} + +// ------------------------------------------------------------------------ +// Accessors +// ------------------------------------------------------------------------ + +/** + * Returns the sort of this Java type. + * + * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, + * {@link #CHAR CHAR}, {@link #BYTE BYTE}, {@link #SHORT SHORT}, + * {@link #INT INT}, {@link #FLOAT FLOAT}, {@link #LONG LONG}, + * {@link #DOUBLE DOUBLE}, {@link #ARRAY ARRAY} or + * {@link #OBJECT OBJECT}. + */ +public int getSort(){ + return sort; +} + +/** + * Returns the number of dimensions of this array type. This method should + * only be used for an array type. + * + * @return the number of dimensions of this array type. + */ +public int getDimensions(){ + int i = 1; + while(buf[off + i] == '[') + { + ++i; + } + return i; +} + +/** + * Returns the type of the elements of this array type. This method should + * only be used for an array type. + * + * @return Returns the type of the elements of this array type. + */ +public Type getElementType(){ + return getType(buf, off + getDimensions()); +} + +/** + * Returns the name of the class corresponding to this type. + * + * @return the fully qualified name of the class corresponding to this type. + */ +public String getClassName(){ + switch(sort) + { + case VOID: + return "void"; + case BOOLEAN: + return "boolean"; + case CHAR: + return "char"; + case BYTE: + return "byte"; + case SHORT: + return "short"; + case INT: + return "int"; + case FLOAT: + return "float"; + case LONG: + return "long"; + case DOUBLE: + return "double"; + case ARRAY: + StringBuffer b = new StringBuffer(getElementType().getClassName()); + for(int i = getDimensions(); i > 0; --i) + { + b.append("[]"); + } + return b.toString(); + // case OBJECT: + default: + return new String(buf, off + 1, len - 2).replace('/', '.'); + } +} + +/** + * Returns the internal name of the class corresponding to this object type. + * The internal name of a class is its fully qualified name, where '.' are + * replaced by '/'. This method should only be used for an object type. + * + * @return the internal name of the class corresponding to this object type. + */ +public String getInternalName(){ + return new String(buf, off + 1, len - 2); +} + +// ------------------------------------------------------------------------ +// Conversion to type descriptors +// ------------------------------------------------------------------------ + +/** + * Returns the descriptor corresponding to this Java type. + * + * @return the descriptor corresponding to this Java type. + */ +public String getDescriptor(){ + StringBuffer buf = new StringBuffer(); + getDescriptor(buf); + return buf.toString(); +} + +/** + * Returns the descriptor corresponding to the given argument and return + * types. + * + * @param returnType the return type of the method. + * @param argumentTypes the argument types of the method. + * @return the descriptor corresponding to the given argument and return + * types. + */ +public static String getMethodDescriptor( + final Type returnType, + final Type[] argumentTypes){ + StringBuffer buf = new StringBuffer(); + buf.append('('); + for(int i = 0; i < argumentTypes.length; ++i) + { + argumentTypes[i].getDescriptor(buf); + } + buf.append(')'); + returnType.getDescriptor(buf); + return buf.toString(); +} + +/** + * Appends the descriptor corresponding to this Java type to the given + * string buffer. + * + * @param buf the string buffer to which the descriptor must be appended. + */ +private void getDescriptor(final StringBuffer buf){ + switch(sort) + { + case VOID: + buf.append('V'); + return; + case BOOLEAN: + buf.append('Z'); + return; + case CHAR: + buf.append('C'); + return; + case BYTE: + buf.append('B'); + return; + case SHORT: + buf.append('S'); + return; + case INT: + buf.append('I'); + return; + case FLOAT: + buf.append('F'); + return; + case LONG: + buf.append('J'); + return; + case DOUBLE: + buf.append('D'); + return; + // case ARRAY: + // case OBJECT: + default: + buf.append(this.buf, off, len); + } +} + +// ------------------------------------------------------------------------ +// Direct conversion from classes to type descriptors, +// without intermediate Type objects +// ------------------------------------------------------------------------ + +/** + * Returns the internal name of the given class. The internal name of a + * class is its fully qualified name, where '.' are replaced by '/'. + * + * @param c an object class. + * @return the internal name of the given class. + */ +public static String getInternalName(final Class c){ + return c.getName().replace('.', '/'); +} + +/** + * Returns the descriptor corresponding to the given Java type. + * + * @param c an object class, a primitive class or an array class. + * @return the descriptor corresponding to the given class. + */ +public static String getDescriptor(final Class c){ + StringBuffer buf = new StringBuffer(); + getDescriptor(buf, c); + return buf.toString(); +} + +/** + * Returns the descriptor corresponding to the given constructor. + * + * @param c a {@link Constructor Constructor} object. + * @return the descriptor of the given constructor. + */ +public static String getConstructorDescriptor(final Constructor c){ + Class[] parameters = c.getParameterTypes(); + StringBuffer buf = new StringBuffer(); + buf.append('('); + for(int i = 0; i < parameters.length; ++i) + { + getDescriptor(buf, parameters[i]); + } + return buf.append(")V").toString(); +} + +/** + * Returns the descriptor corresponding to the given method. + * + * @param m a {@link Method Method} object. + * @return the descriptor of the given method. + */ +public static String getMethodDescriptor(final Method m){ + Class[] parameters = m.getParameterTypes(); + StringBuffer buf = new StringBuffer(); + buf.append('('); + for(int i = 0; i < parameters.length; ++i) + { + getDescriptor(buf, parameters[i]); + } + buf.append(')'); + getDescriptor(buf, m.getReturnType()); + return buf.toString(); +} + +/** + * Appends the descriptor of the given class to the given string buffer. + * + * @param buf the string buffer to which the descriptor must be appended. + * @param c the class whose descriptor must be computed. + */ +private static void getDescriptor(final StringBuffer buf, final Class c){ + Class d = c; + while(true) + { + if(d.isPrimitive()) + { + char car; + if(d == Integer.TYPE) + { + car = 'I'; + } + else if(d == Void.TYPE) + { + car = 'V'; + } + else if(d == Boolean.TYPE) + { + car = 'Z'; + } + else if(d == Byte.TYPE) + { + car = 'B'; + } + else if(d == Character.TYPE) + { + car = 'C'; + } + else if(d == Short.TYPE) + { + car = 'S'; + } + else if(d == Double.TYPE) + { + car = 'D'; + } + else if(d == Float.TYPE) + { + car = 'F'; + } + else /* if (d == Long.TYPE) */ + { + car = 'J'; + } + buf.append(car); + return; + } + else if(d.isArray()) + { + buf.append('['); + d = d.getComponentType(); + } + else + { + buf.append('L'); + String name = d.getName(); + int len = name.length(); + for(int i = 0; i < len; ++i) + { + char car = name.charAt(i); + buf.append(car == '.' ? '/' : car); + } + buf.append(';'); + return; + } + } +} + +// ------------------------------------------------------------------------ +// Corresponding size and opcodes +// ------------------------------------------------------------------------ + +/** + * Returns the size of values of this type. + * + * @return the size of values of this type, i.e., 2 for long and + * double, and 1 otherwise. + */ +public int getSize(){ + return sort == LONG || sort == DOUBLE ? 2 : 1; +} + +/** + * Returns a JVM instruction opcode adapted to this Java type. + * + * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, + * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, + * ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. + * @return an opcode that is similar to the given opcode, but adapted to + * this Java type. For example, if this type is float and + * opcode is IRETURN, this method returns FRETURN. + */ +public int getOpcode(final int opcode){ + if(opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) + { + switch(sort) + { + case BOOLEAN: + case BYTE: + return opcode + 5; + case CHAR: + return opcode + 6; + case SHORT: + return opcode + 7; + case INT: + return opcode; + case FLOAT: + return opcode + 2; + case LONG: + return opcode + 1; + case DOUBLE: + return opcode + 3; + // case ARRAY: + // case OBJECT: + default: + return opcode + 4; + } + } + else + { + switch(sort) + { + case VOID: + return opcode + 5; + case BOOLEAN: + case CHAR: + case BYTE: + case SHORT: + case INT: + return opcode; + case FLOAT: + return opcode + 2; + case LONG: + return opcode + 1; + case DOUBLE: + return opcode + 3; + // case ARRAY: + // case OBJECT: + default: + return opcode + 4; + } + } +} + +// ------------------------------------------------------------------------ +// Equals, hashCode and toString +// ------------------------------------------------------------------------ + +/** + * Tests if the given object is equal to this type. + * + * @param o the object to be compared to this type. + * @return true if the given object is equal to this type. + */ +public boolean equals(final Object o){ + if(this == o) + { + return true; + } + if(!(o instanceof Type)) + { + return false; + } + Type t = (Type) o; + if(sort != t.sort) + { + return false; + } + if(sort == Type.OBJECT || sort == Type.ARRAY) + { + if(len != t.len) + { + return false; + } + for(int i = off, j = t.off, end = i + len; i < end; i++, j++) + { + if(buf[i] != t.buf[j]) + { + return false; + } + } + } + return true; +} + +/** + * Returns a hash code value for this type. + * + * @return a hash code value for this type. + */ +public int hashCode(){ + int hc = 13 * sort; + if(sort == Type.OBJECT || sort == Type.ARRAY) + { + for(int i = off, end = i + len; i < end; i++) + { + hc = 17 * (hc + buf[i]); + } + } + return hc; +} + +/** + * Returns a string representation of this type. + * + * @return the descriptor of this type. + */ +public String toString(){ + return getDescriptor(); +} +} diff --git a/src/jvm/clojure/asm/commons/AdviceAdapter.java b/src/jvm/clojure/asm/commons/AdviceAdapter.java index a03ccb0..3d53bd3 100644 --- a/src/jvm/clojure/asm/commons/AdviceAdapter.java +++ b/src/jvm/clojure/asm/commons/AdviceAdapter.java @@ -1,681 +1,681 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm.commons; - -import java.util.ArrayList; -import java.util.HashMap; - -import clojure.asm.Label; -import clojure.asm.MethodVisitor; -import clojure.asm.Opcodes; -import clojure.asm.Type; - -/** - * A {@link clojure.asm.MethodAdapter} to insert before, after and around - * advices in methods and constructors.

The behavior for constructors is - * like this:

    - *

    - *

  1. as long as the INVOKESPECIAL for the object initialization has not been - * reached, every bytecode instruction is dispatched in the ctor code visitor
  2. - *

    - *

  3. when this one is reached, it is only added in the ctor code visitor and - * a JP invoke is added
  4. - *

    - *

  5. after that, only the other code visitor receives the instructions
  6. - *

    - *

- * - * @author Eugene Kuleshov - * @author Eric Bruneton - */ -public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes{ -private static final Object THIS = new Object(); -private static final Object OTHER = new Object(); - -protected int methodAccess; -protected String methodDesc; - -private boolean constructor; -private boolean superInitialized; -private ArrayList stackFrame; -private HashMap branches; - -/** - * Creates a new {@link AdviceAdapter}. - * - * @param mv the method visitor to which this adapter delegates calls. - * @param access the method's access flags (see {@link Opcodes}). - * @param name the method's name. - * @param desc the method's descriptor (see {@link Type Type}). - */ -public AdviceAdapter( - final MethodVisitor mv, - final int access, - final String name, - final String desc){ - super(mv, access, name, desc); - methodAccess = access; - methodDesc = desc; - - constructor = "".equals(name); -} - -public void visitCode(){ - mv.visitCode(); - if(!constructor) - { - superInitialized = true; - onMethodEnter(); - } - else - { - stackFrame = new ArrayList(); - branches = new HashMap(); - } -} - -public void visitLabel(final Label label){ - mv.visitLabel(label); - - if(constructor && branches != null) - { - ArrayList frame = (ArrayList) branches.get(label); - if(frame != null) - { - stackFrame = frame; - branches.remove(label); - } - } -} - -public void visitInsn(final int opcode){ - if(constructor) - { - switch(opcode) - { - case RETURN: // empty stack - onMethodExit(opcode); - break; - - case IRETURN: // 1 before n/a after - case FRETURN: // 1 before n/a after - case ARETURN: // 1 before n/a after - case ATHROW: // 1 before n/a after - popValue(); - popValue(); - onMethodExit(opcode); - break; - - case LRETURN: // 2 before n/a after - case DRETURN: // 2 before n/a after - popValue(); - popValue(); - onMethodExit(opcode); - break; - - case NOP: - case LALOAD: // remove 2 add 2 - case DALOAD: // remove 2 add 2 - case LNEG: - case DNEG: - case FNEG: - case INEG: - case L2D: - case D2L: - case F2I: - case I2B: - case I2C: - case I2S: - case I2F: - case Opcodes.ARRAYLENGTH: - break; - - case ACONST_NULL: - case ICONST_M1: - case ICONST_0: - case ICONST_1: - case ICONST_2: - case ICONST_3: - case ICONST_4: - case ICONST_5: - case FCONST_0: - case FCONST_1: - case FCONST_2: - case F2L: // 1 before 2 after - case F2D: - case I2L: - case I2D: - pushValue(OTHER); - break; - - case LCONST_0: - case LCONST_1: - case DCONST_0: - case DCONST_1: - pushValue(OTHER); - pushValue(OTHER); - break; - - case IALOAD: // remove 2 add 1 - case FALOAD: // remove 2 add 1 - case AALOAD: // remove 2 add 1 - case BALOAD: // remove 2 add 1 - case CALOAD: // remove 2 add 1 - case SALOAD: // remove 2 add 1 - case POP: - case IADD: - case FADD: - case ISUB: - case LSHL: // 3 before 2 after - case LSHR: // 3 before 2 after - case LUSHR: // 3 before 2 after - case L2I: // 2 before 1 after - case L2F: // 2 before 1 after - case D2I: // 2 before 1 after - case D2F: // 2 before 1 after - case FSUB: - case FMUL: - case FDIV: - case FREM: - case FCMPL: // 2 before 1 after - case FCMPG: // 2 before 1 after - case IMUL: - case IDIV: - case IREM: - case ISHL: - case ISHR: - case IUSHR: - case IAND: - case IOR: - case IXOR: - case MONITORENTER: - case MONITOREXIT: - popValue(); - break; - - case POP2: - case LSUB: - case LMUL: - case LDIV: - case LREM: - case LADD: - case LAND: - case LOR: - case LXOR: - case DADD: - case DMUL: - case DSUB: - case DDIV: - case DREM: - popValue(); - popValue(); - break; - - case IASTORE: - case FASTORE: - case AASTORE: - case BASTORE: - case CASTORE: - case SASTORE: - case LCMP: // 4 before 1 after - case DCMPL: - case DCMPG: - popValue(); - popValue(); - popValue(); - break; - - case LASTORE: - case DASTORE: - popValue(); - popValue(); - popValue(); - popValue(); - break; - - case DUP: - pushValue(peekValue()); - break; - - case DUP_X1: - // TODO optimize this - { - Object o1 = popValue(); - Object o2 = popValue(); - pushValue(o1); - pushValue(o2); - pushValue(o1); - } - break; - - case DUP_X2: - // TODO optimize this - { - Object o1 = popValue(); - Object o2 = popValue(); - Object o3 = popValue(); - pushValue(o1); - pushValue(o3); - pushValue(o2); - pushValue(o1); - } - break; - - case DUP2: - // TODO optimize this - { - Object o1 = popValue(); - Object o2 = popValue(); - pushValue(o2); - pushValue(o1); - pushValue(o2); - pushValue(o1); - } - break; - - case DUP2_X1: - // TODO optimize this - { - Object o1 = popValue(); - Object o2 = popValue(); - Object o3 = popValue(); - pushValue(o2); - pushValue(o1); - pushValue(o3); - pushValue(o2); - pushValue(o1); - } - break; - - case DUP2_X2: - // TODO optimize this - { - Object o1 = popValue(); - Object o2 = popValue(); - Object o3 = popValue(); - Object o4 = popValue(); - pushValue(o2); - pushValue(o1); - pushValue(o4); - pushValue(o3); - pushValue(o2); - pushValue(o1); - } - break; - - case SWAP: - { - Object o1 = popValue(); - Object o2 = popValue(); - pushValue(o1); - pushValue(o2); - } - break; - } - } - else - { - switch(opcode) - { - case RETURN: - case IRETURN: - case FRETURN: - case ARETURN: - case LRETURN: - case DRETURN: - case ATHROW: - onMethodExit(opcode); - break; - } - } - mv.visitInsn(opcode); -} - -public void visitVarInsn(final int opcode, final int var){ - super.visitVarInsn(opcode, var); - - if(constructor) - { - switch(opcode) - { - case ILOAD: - case FLOAD: - pushValue(OTHER); - break; - case LLOAD: - case DLOAD: - pushValue(OTHER); - pushValue(OTHER); - break; - case ALOAD: - pushValue(var == 0 ? THIS : OTHER); - break; - case ASTORE: - case ISTORE: - case FSTORE: - popValue(); - break; - case LSTORE: - case DSTORE: - popValue(); - popValue(); - break; - } - } -} - -public void visitFieldInsn( - final int opcode, - final String owner, - final String name, - final String desc){ - mv.visitFieldInsn(opcode, owner, name, desc); - - if(constructor) - { - char c = desc.charAt(0); - boolean longOrDouble = c == 'J' || c == 'D'; - switch(opcode) - { - case GETSTATIC: - pushValue(OTHER); - if(longOrDouble) - { - pushValue(OTHER); - } - break; - case PUTSTATIC: - popValue(); - if(longOrDouble) - { - popValue(); - } - break; - case PUTFIELD: - popValue(); - if(longOrDouble) - { - popValue(); - popValue(); - } - break; - // case GETFIELD: - default: - if(longOrDouble) - { - pushValue(OTHER); - } - } - } -} - -public void visitIntInsn(final int opcode, final int operand){ - mv.visitIntInsn(opcode, operand); - - if(constructor && opcode != NEWARRAY) - { - pushValue(OTHER); - } -} - -public void visitLdcInsn(final Object cst){ - mv.visitLdcInsn(cst); - - if(constructor) - { - pushValue(OTHER); - if(cst instanceof Double || cst instanceof Long) - { - pushValue(OTHER); - } - } -} - -public void visitMultiANewArrayInsn(final String desc, final int dims){ - mv.visitMultiANewArrayInsn(desc, dims); - - if(constructor) - { - for(int i = 0; i < dims; i++) - { - popValue(); - } - pushValue(OTHER); - } -} - -public void visitTypeInsn(final int opcode, final String name){ - mv.visitTypeInsn(opcode, name); - - // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack - if(constructor && opcode == NEW) - { - pushValue(OTHER); - } -} - -public void visitMethodInsn( - final int opcode, - final String owner, - final String name, - final String desc){ - mv.visitMethodInsn(opcode, owner, name, desc); - - if(constructor) - { - Type[] types = Type.getArgumentTypes(desc); - for(int i = 0; i < types.length; i++) - { - popValue(); - if(types[i].getSize() == 2) - { - popValue(); - } - } - switch(opcode) - { - // case INVOKESTATIC: - // break; - - case INVOKEINTERFACE: - case INVOKEVIRTUAL: - popValue(); // objectref - break; - - case INVOKESPECIAL: - Object type = popValue(); // objectref - if(type == THIS && !superInitialized) - { - onMethodEnter(); - superInitialized = true; - // once super has been initialized it is no longer - // necessary to keep track of stack state - constructor = false; - } - break; - } - - Type returnType = Type.getReturnType(desc); - if(returnType != Type.VOID_TYPE) - { - pushValue(OTHER); - if(returnType.getSize() == 2) - { - pushValue(OTHER); - } - } - } -} - -public void visitJumpInsn(final int opcode, final Label label){ - mv.visitJumpInsn(opcode, label); - - if(constructor) - { - switch(opcode) - { - case IFEQ: - case IFNE: - case IFLT: - case IFGE: - case IFGT: - case IFLE: - case IFNULL: - case IFNONNULL: - popValue(); - break; - - case IF_ICMPEQ: - case IF_ICMPNE: - case IF_ICMPLT: - case IF_ICMPGE: - case IF_ICMPGT: - case IF_ICMPLE: - case IF_ACMPEQ: - case IF_ACMPNE: - popValue(); - popValue(); - break; - - case JSR: - pushValue(OTHER); - break; - } - addBranch(label); - } -} - -public void visitLookupSwitchInsn( - final Label dflt, - final int[] keys, - final Label[] labels){ - mv.visitLookupSwitchInsn(dflt, keys, labels); - - if(constructor) - { - popValue(); - addBranches(dflt, labels); - } -} - -public void visitTableSwitchInsn( - final int min, - final int max, - final Label dflt, - final Label[] labels){ - mv.visitTableSwitchInsn(min, max, dflt, labels); - - if(constructor) - { - popValue(); - addBranches(dflt, labels); - } -} - -private void addBranches(final Label dflt, final Label[] labels){ - addBranch(dflt); - for(int i = 0; i < labels.length; i++) - { - addBranch(labels[i]); - } -} - -private void addBranch(final Label label){ - if(branches.containsKey(label)) - { - return; - } - ArrayList frame = new ArrayList(); - frame.addAll(stackFrame); - branches.put(label, frame); -} - -private Object popValue(){ - return stackFrame.remove(stackFrame.size() - 1); -} - -private Object peekValue(){ - return stackFrame.get(stackFrame.size() - 1); -} - -private void pushValue(final Object o){ - stackFrame.add(o); -} - -/** - * Called at the beginning of the method or after super class class call in - * the constructor.

- *

- * Custom code can use or change all the local variables, but should not - * change state of the stack. - */ -protected abstract void onMethodEnter(); - -/** - * Called before explicit exit from the method using either return or throw. - * Top element on the stack contains the return value or exception instance. - * For example: - *

- *

- *   public void onMethodExit(int opcode) {
- *     if(opcode==RETURN) {
- *         visitInsn(ACONST_NULL);
- *     } else if(opcode==ARETURN || opcode==ATHROW) {
- *         dup();
- *     } else {
- *         if(opcode==LRETURN || opcode==DRETURN) {
- *             dup2();
- *         } else {
- *             dup();
- *         }
- *         box(Type.getReturnType(this.methodDesc));
- *     }
- *     visitIntInsn(SIPUSH, opcode);
- *     visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
- *   }
- * 

- * // an actual call back method - * public static void onExit(int opcode, Object param) { - * ... - *

- *

- *

- *

- * Custom code can use or change all the local variables, but should not - * change state of the stack. - * - * @param opcode one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, - * DRETURN or ATHROW - */ -protected abstract void onMethodExit(int opcode); - -// TODO onException, onMethodCall - -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm.commons; + +import java.util.ArrayList; +import java.util.HashMap; + +import clojure.asm.Label; +import clojure.asm.MethodVisitor; +import clojure.asm.Opcodes; +import clojure.asm.Type; + +/** + * A {@link clojure.asm.MethodAdapter} to insert before, after and around + * advices in methods and constructors.

The behavior for constructors is + * like this:

    + *

    + *

  1. as long as the INVOKESPECIAL for the object initialization has not been + * reached, every bytecode instruction is dispatched in the ctor code visitor
  2. + *

    + *

  3. when this one is reached, it is only added in the ctor code visitor and + * a JP invoke is added
  4. + *

    + *

  5. after that, only the other code visitor receives the instructions
  6. + *

    + *

+ * + * @author Eugene Kuleshov + * @author Eric Bruneton + */ +public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes{ +private static final Object THIS = new Object(); +private static final Object OTHER = new Object(); + +protected int methodAccess; +protected String methodDesc; + +private boolean constructor; +private boolean superInitialized; +private ArrayList stackFrame; +private HashMap branches; + +/** + * Creates a new {@link AdviceAdapter}. + * + * @param mv the method visitor to which this adapter delegates calls. + * @param access the method's access flags (see {@link Opcodes}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + */ +public AdviceAdapter( + final MethodVisitor mv, + final int access, + final String name, + final String desc){ + super(mv, access, name, desc); + methodAccess = access; + methodDesc = desc; + + constructor = "".equals(name); +} + +public void visitCode(){ + mv.visitCode(); + if(!constructor) + { + superInitialized = true; + onMethodEnter(); + } + else + { + stackFrame = new ArrayList(); + branches = new HashMap(); + } +} + +public void visitLabel(final Label label){ + mv.visitLabel(label); + + if(constructor && branches != null) + { + ArrayList frame = (ArrayList) branches.get(label); + if(frame != null) + { + stackFrame = frame; + branches.remove(label); + } + } +} + +public void visitInsn(final int opcode){ + if(constructor) + { + switch(opcode) + { + case RETURN: // empty stack + onMethodExit(opcode); + break; + + case IRETURN: // 1 before n/a after + case FRETURN: // 1 before n/a after + case ARETURN: // 1 before n/a after + case ATHROW: // 1 before n/a after + popValue(); + popValue(); + onMethodExit(opcode); + break; + + case LRETURN: // 2 before n/a after + case DRETURN: // 2 before n/a after + popValue(); + popValue(); + onMethodExit(opcode); + break; + + case NOP: + case LALOAD: // remove 2 add 2 + case DALOAD: // remove 2 add 2 + case LNEG: + case DNEG: + case FNEG: + case INEG: + case L2D: + case D2L: + case F2I: + case I2B: + case I2C: + case I2S: + case I2F: + case Opcodes.ARRAYLENGTH: + break; + + case ACONST_NULL: + case ICONST_M1: + case ICONST_0: + case ICONST_1: + case ICONST_2: + case ICONST_3: + case ICONST_4: + case ICONST_5: + case FCONST_0: + case FCONST_1: + case FCONST_2: + case F2L: // 1 before 2 after + case F2D: + case I2L: + case I2D: + pushValue(OTHER); + break; + + case LCONST_0: + case LCONST_1: + case DCONST_0: + case DCONST_1: + pushValue(OTHER); + pushValue(OTHER); + break; + + case IALOAD: // remove 2 add 1 + case FALOAD: // remove 2 add 1 + case AALOAD: // remove 2 add 1 + case BALOAD: // remove 2 add 1 + case CALOAD: // remove 2 add 1 + case SALOAD: // remove 2 add 1 + case POP: + case IADD: + case FADD: + case ISUB: + case LSHL: // 3 before 2 after + case LSHR: // 3 before 2 after + case LUSHR: // 3 before 2 after + case L2I: // 2 before 1 after + case L2F: // 2 before 1 after + case D2I: // 2 before 1 after + case D2F: // 2 before 1 after + case FSUB: + case FMUL: + case FDIV: + case FREM: + case FCMPL: // 2 before 1 after + case FCMPG: // 2 before 1 after + case IMUL: + case IDIV: + case IREM: + case ISHL: + case ISHR: + case IUSHR: + case IAND: + case IOR: + case IXOR: + case MONITORENTER: + case MONITOREXIT: + popValue(); + break; + + case POP2: + case LSUB: + case LMUL: + case LDIV: + case LREM: + case LADD: + case LAND: + case LOR: + case LXOR: + case DADD: + case DMUL: + case DSUB: + case DDIV: + case DREM: + popValue(); + popValue(); + break; + + case IASTORE: + case FASTORE: + case AASTORE: + case BASTORE: + case CASTORE: + case SASTORE: + case LCMP: // 4 before 1 after + case DCMPL: + case DCMPG: + popValue(); + popValue(); + popValue(); + break; + + case LASTORE: + case DASTORE: + popValue(); + popValue(); + popValue(); + popValue(); + break; + + case DUP: + pushValue(peekValue()); + break; + + case DUP_X1: + // TODO optimize this + { + Object o1 = popValue(); + Object o2 = popValue(); + pushValue(o1); + pushValue(o2); + pushValue(o1); + } + break; + + case DUP_X2: + // TODO optimize this + { + Object o1 = popValue(); + Object o2 = popValue(); + Object o3 = popValue(); + pushValue(o1); + pushValue(o3); + pushValue(o2); + pushValue(o1); + } + break; + + case DUP2: + // TODO optimize this + { + Object o1 = popValue(); + Object o2 = popValue(); + pushValue(o2); + pushValue(o1); + pushValue(o2); + pushValue(o1); + } + break; + + case DUP2_X1: + // TODO optimize this + { + Object o1 = popValue(); + Object o2 = popValue(); + Object o3 = popValue(); + pushValue(o2); + pushValue(o1); + pushValue(o3); + pushValue(o2); + pushValue(o1); + } + break; + + case DUP2_X2: + // TODO optimize this + { + Object o1 = popValue(); + Object o2 = popValue(); + Object o3 = popValue(); + Object o4 = popValue(); + pushValue(o2); + pushValue(o1); + pushValue(o4); + pushValue(o3); + pushValue(o2); + pushValue(o1); + } + break; + + case SWAP: + { + Object o1 = popValue(); + Object o2 = popValue(); + pushValue(o1); + pushValue(o2); + } + break; + } + } + else + { + switch(opcode) + { + case RETURN: + case IRETURN: + case FRETURN: + case ARETURN: + case LRETURN: + case DRETURN: + case ATHROW: + onMethodExit(opcode); + break; + } + } + mv.visitInsn(opcode); +} + +public void visitVarInsn(final int opcode, final int var){ + super.visitVarInsn(opcode, var); + + if(constructor) + { + switch(opcode) + { + case ILOAD: + case FLOAD: + pushValue(OTHER); + break; + case LLOAD: + case DLOAD: + pushValue(OTHER); + pushValue(OTHER); + break; + case ALOAD: + pushValue(var == 0 ? THIS : OTHER); + break; + case ASTORE: + case ISTORE: + case FSTORE: + popValue(); + break; + case LSTORE: + case DSTORE: + popValue(); + popValue(); + break; + } + } +} + +public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc){ + mv.visitFieldInsn(opcode, owner, name, desc); + + if(constructor) + { + char c = desc.charAt(0); + boolean longOrDouble = c == 'J' || c == 'D'; + switch(opcode) + { + case GETSTATIC: + pushValue(OTHER); + if(longOrDouble) + { + pushValue(OTHER); + } + break; + case PUTSTATIC: + popValue(); + if(longOrDouble) + { + popValue(); + } + break; + case PUTFIELD: + popValue(); + if(longOrDouble) + { + popValue(); + popValue(); + } + break; + // case GETFIELD: + default: + if(longOrDouble) + { + pushValue(OTHER); + } + } + } +} + +public void visitIntInsn(final int opcode, final int operand){ + mv.visitIntInsn(opcode, operand); + + if(constructor && opcode != NEWARRAY) + { + pushValue(OTHER); + } +} + +public void visitLdcInsn(final Object cst){ + mv.visitLdcInsn(cst); + + if(constructor) + { + pushValue(OTHER); + if(cst instanceof Double || cst instanceof Long) + { + pushValue(OTHER); + } + } +} + +public void visitMultiANewArrayInsn(final String desc, final int dims){ + mv.visitMultiANewArrayInsn(desc, dims); + + if(constructor) + { + for(int i = 0; i < dims; i++) + { + popValue(); + } + pushValue(OTHER); + } +} + +public void visitTypeInsn(final int opcode, final String name){ + mv.visitTypeInsn(opcode, name); + + // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack + if(constructor && opcode == NEW) + { + pushValue(OTHER); + } +} + +public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc){ + mv.visitMethodInsn(opcode, owner, name, desc); + + if(constructor) + { + Type[] types = Type.getArgumentTypes(desc); + for(int i = 0; i < types.length; i++) + { + popValue(); + if(types[i].getSize() == 2) + { + popValue(); + } + } + switch(opcode) + { + // case INVOKESTATIC: + // break; + + case INVOKEINTERFACE: + case INVOKEVIRTUAL: + popValue(); // objectref + break; + + case INVOKESPECIAL: + Object type = popValue(); // objectref + if(type == THIS && !superInitialized) + { + onMethodEnter(); + superInitialized = true; + // once super has been initialized it is no longer + // necessary to keep track of stack state + constructor = false; + } + break; + } + + Type returnType = Type.getReturnType(desc); + if(returnType != Type.VOID_TYPE) + { + pushValue(OTHER); + if(returnType.getSize() == 2) + { + pushValue(OTHER); + } + } + } +} + +public void visitJumpInsn(final int opcode, final Label label){ + mv.visitJumpInsn(opcode, label); + + if(constructor) + { + switch(opcode) + { + case IFEQ: + case IFNE: + case IFLT: + case IFGE: + case IFGT: + case IFLE: + case IFNULL: + case IFNONNULL: + popValue(); + break; + + case IF_ICMPEQ: + case IF_ICMPNE: + case IF_ICMPLT: + case IF_ICMPGE: + case IF_ICMPGT: + case IF_ICMPLE: + case IF_ACMPEQ: + case IF_ACMPNE: + popValue(); + popValue(); + break; + + case JSR: + pushValue(OTHER); + break; + } + addBranch(label); + } +} + +public void visitLookupSwitchInsn( + final Label dflt, + final int[] keys, + final Label[] labels){ + mv.visitLookupSwitchInsn(dflt, keys, labels); + + if(constructor) + { + popValue(); + addBranches(dflt, labels); + } +} + +public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label[] labels){ + mv.visitTableSwitchInsn(min, max, dflt, labels); + + if(constructor) + { + popValue(); + addBranches(dflt, labels); + } +} + +private void addBranches(final Label dflt, final Label[] labels){ + addBranch(dflt); + for(int i = 0; i < labels.length; i++) + { + addBranch(labels[i]); + } +} + +private void addBranch(final Label label){ + if(branches.containsKey(label)) + { + return; + } + ArrayList frame = new ArrayList(); + frame.addAll(stackFrame); + branches.put(label, frame); +} + +private Object popValue(){ + return stackFrame.remove(stackFrame.size() - 1); +} + +private Object peekValue(){ + return stackFrame.get(stackFrame.size() - 1); +} + +private void pushValue(final Object o){ + stackFrame.add(o); +} + +/** + * Called at the beginning of the method or after super class class call in + * the constructor.

+ *

+ * Custom code can use or change all the local variables, but should not + * change state of the stack. + */ +protected abstract void onMethodEnter(); + +/** + * Called before explicit exit from the method using either return or throw. + * Top element on the stack contains the return value or exception instance. + * For example: + *

+ *

+ *   public void onMethodExit(int opcode) {
+ *     if(opcode==RETURN) {
+ *         visitInsn(ACONST_NULL);
+ *     } else if(opcode==ARETURN || opcode==ATHROW) {
+ *         dup();
+ *     } else {
+ *         if(opcode==LRETURN || opcode==DRETURN) {
+ *             dup2();
+ *         } else {
+ *             dup();
+ *         }
+ *         box(Type.getReturnType(this.methodDesc));
+ *     }
+ *     visitIntInsn(SIPUSH, opcode);
+ *     visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
+ *   }
+ * 

+ * // an actual call back method + * public static void onExit(int opcode, Object param) { + * ... + *

+ *

+ *

+ *

+ * Custom code can use or change all the local variables, but should not + * change state of the stack. + * + * @param opcode one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, + * DRETURN or ATHROW + */ +protected abstract void onMethodExit(int opcode); + +// TODO onException, onMethodCall + +} diff --git a/src/jvm/clojure/asm/commons/AnalyzerAdapter.java b/src/jvm/clojure/asm/commons/AnalyzerAdapter.java index 3b62d75..02c30bb 100644 --- a/src/jvm/clojure/asm/commons/AnalyzerAdapter.java +++ b/src/jvm/clojure/asm/commons/AnalyzerAdapter.java @@ -1,938 +1,938 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm.commons; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import clojure.asm.Label; -import clojure.asm.MethodAdapter; -import clojure.asm.MethodVisitor; -import clojure.asm.Opcodes; -import clojure.asm.Type; - -/** - * A {@link MethodAdapter} that keeps track of stack map frame changes between - * {@link #visitFrame(int,int,Object[],int,Object[]) visitFrame} calls. This - * adapter must be used with the - * {@link clojure.asm.ClassReader#EXPAND_FRAMES} option. Each visitXXX - * instruction delegates to the next visitor in the chain, if any, and then - * simulates the effect of this instruction on the stack map frame, represented - * by {@link #locals} and {@link #stack}. The next visitor in the chain can get - * the state of the stack map frame before each instruction by reading - * the value of these fields in its visitXXX methods (this requires a - * reference to the AnalyzerAdapter that is before it in the chain). - * - * @author Eric Bruneton - */ -public class AnalyzerAdapter extends MethodAdapter{ - -/** - * List of the local variable slots for current execution - * frame. Primitive types are represented by {@link Opcodes#TOP}, - * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, - * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or - * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by a - * two elements, the second one being TOP). Reference types are represented - * by String objects (representing internal names, or type descriptors for - * array types), and uninitialized types by Label objects (this label - * designates the NEW instruction that created this uninitialized value). - * This field is null for unreacheable instructions. - */ -public List locals; - -/** - * List of the operand stack slots for current execution - * frame. Primitive types are represented by {@link Opcodes#TOP}, - * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, - * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or - * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by a - * two elements, the second one being TOP). Reference types are represented - * by String objects (representing internal names, or type descriptors for - * array types), and uninitialized types by Label objects (this label - * designates the NEW instruction that created this uninitialized value). - * This field is null for unreacheable instructions. - */ -public List stack; - -/** - * The labels that designate the next instruction to be visited. May be - * null. - */ -private List labels; - -/** - * Information about uninitialized types in the current execution frame. - * This map associates internal names to Label objects. Each label - * designates a NEW instruction that created the currently uninitialized - * types, and the associated internal name represents the NEW operand, i.e. - * the final, initialized type value. - */ -private Map uninitializedTypes; - -/** - * The maximum stack size of this method. - */ -private int maxStack; - -/** - * The maximum number of local variables of this method. - */ -private int maxLocals; - -/** - * Creates a new {@link AnalyzerAdapter}. - * - * @param owner the owner's class name. - * @param access the method's access flags (see {@link Opcodes}). - * @param name the method's name. - * @param desc the method's descriptor (see {@link Type Type}). - * @param mv the method visitor to which this adapter delegates calls. May - * be null. - */ -public AnalyzerAdapter( - final String owner, - final int access, - final String name, - final String desc, - final MethodVisitor mv){ - super(mv); - locals = new ArrayList(); - stack = new ArrayList(); - uninitializedTypes = new HashMap(); - - if((access & Opcodes.ACC_STATIC) == 0) - { - if(name.equals("")) - { - locals.add(Opcodes.UNINITIALIZED_THIS); - } - else - { - locals.add(owner); - } - } - Type[] types = Type.getArgumentTypes(desc); - for(int i = 0; i < types.length; ++i) - { - Type type = types[i]; - switch(type.getSort()) - { - case Type.BOOLEAN: - case Type.CHAR: - case Type.BYTE: - case Type.SHORT: - case Type.INT: - locals.add(Opcodes.INTEGER); - break; - case Type.FLOAT: - locals.add(Opcodes.FLOAT); - break; - case Type.LONG: - locals.add(Opcodes.LONG); - locals.add(Opcodes.TOP); - break; - case Type.DOUBLE: - locals.add(Opcodes.DOUBLE); - locals.add(Opcodes.TOP); - break; - case Type.ARRAY: - locals.add(types[i].getDescriptor()); - break; - // case Type.OBJECT: - default: - locals.add(types[i].getInternalName()); - } - } -} - -public void visitFrame( - final int type, - final int nLocal, - final Object[] local, - final int nStack, - final Object[] stack){ - if(type != Opcodes.F_NEW) - { // uncompressed frame - throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag"); - } - - if(mv != null) - { - mv.visitFrame(type, nLocal, local, nStack, stack); - } - - if(this.locals != null) - { - this.locals.clear(); - this.stack.clear(); - } - else - { - this.locals = new ArrayList(); - this.stack = new ArrayList(); - } - visitFrameTypes(nLocal, local, this.locals); - visitFrameTypes(nStack, stack, this.stack); - maxStack = Math.max(maxStack, this.stack.size()); -} - -private void visitFrameTypes( - final int n, - final Object[] types, - final List result){ - for(int i = 0; i < n; ++i) - { - Object type = types[i]; - result.add(type); - if(type == Opcodes.LONG || type == Opcodes.DOUBLE) - { - result.add(Opcodes.TOP); - } - } -} - -public void visitInsn(final int opcode){ - if(mv != null) - { - mv.visitInsn(opcode); - } - execute(opcode, 0, null); - if((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) - || opcode == Opcodes.ATHROW) - { - this.locals = null; - this.stack = null; - } -} - -public void visitIntInsn(final int opcode, final int operand){ - if(mv != null) - { - mv.visitIntInsn(opcode, operand); - } - execute(opcode, operand, null); -} - -public void visitVarInsn(final int opcode, final int var){ - if(mv != null) - { - mv.visitVarInsn(opcode, var); - } - execute(opcode, var, null); -} - -public void visitTypeInsn(final int opcode, final String desc){ - if(opcode == Opcodes.NEW) - { - if(labels == null) - { - Label l = new Label(); - labels = new ArrayList(3); - labels.add(l); - if(mv != null) - { - mv.visitLabel(l); - } - } - for(int i = 0; i < labels.size(); ++i) - { - uninitializedTypes.put(labels.get(i), desc); - } - } - if(mv != null) - { - mv.visitTypeInsn(opcode, desc); - } - execute(opcode, 0, desc); -} - -public void visitFieldInsn( - final int opcode, - final String owner, - final String name, - final String desc){ - if(mv != null) - { - mv.visitFieldInsn(opcode, owner, name, desc); - } - execute(opcode, 0, desc); -} - -public void visitMethodInsn( - final int opcode, - final String owner, - final String name, - final String desc){ - if(mv != null) - { - mv.visitMethodInsn(opcode, owner, name, desc); - } - pop(desc); - if(opcode != Opcodes.INVOKESTATIC) - { - Object t = pop(); - if(opcode == Opcodes.INVOKESPECIAL && name.charAt(0) == '<') - { - Object u; - if(t == Opcodes.UNINITIALIZED_THIS) - { - u = owner; - } - else - { - u = uninitializedTypes.get(t); - } - for(int i = 0; i < locals.size(); ++i) - { - if(locals.get(i) == t) - { - locals.set(i, u); - } - } - for(int i = 0; i < stack.size(); ++i) - { - if(stack.get(i) == t) - { - stack.set(i, u); - } - } - } - } - pushDesc(desc); - labels = null; -} - -public void visitJumpInsn(final int opcode, final Label label){ - if(mv != null) - { - mv.visitJumpInsn(opcode, label); - } - execute(opcode, 0, null); - if(opcode == Opcodes.GOTO) - { - this.locals = null; - this.stack = null; - } -} - -public void visitLabel(final Label label){ - if(mv != null) - { - mv.visitLabel(label); - } - if(labels == null) - { - labels = new ArrayList(3); - } - labels.add(label); -} - -public void visitLdcInsn(final Object cst){ - if(mv != null) - { - mv.visitLdcInsn(cst); - } - if(cst instanceof Integer) - { - push(Opcodes.INTEGER); - } - else if(cst instanceof Long) - { - push(Opcodes.LONG); - push(Opcodes.TOP); - } - else if(cst instanceof Float) - { - push(Opcodes.FLOAT); - } - else if(cst instanceof Double) - { - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - } - else if(cst instanceof String) - { - push("java/lang/String"); - } - else if(cst instanceof Type) - { - push("java/lang/Class"); - } - else - { - throw new IllegalArgumentException(); - } - labels = null; -} - -public void visitIincInsn(final int var, final int increment){ - if(mv != null) - { - mv.visitIincInsn(var, increment); - } - execute(Opcodes.IINC, var, null); -} - -public void visitTableSwitchInsn( - final int min, - final int max, - final Label dflt, - final Label labels[]){ - if(mv != null) - { - mv.visitTableSwitchInsn(min, max, dflt, labels); - } - execute(Opcodes.TABLESWITCH, 0, null); - this.locals = null; - this.stack = null; -} - -public void visitLookupSwitchInsn( - final Label dflt, - final int keys[], - final Label labels[]){ - if(mv != null) - { - mv.visitLookupSwitchInsn(dflt, keys, labels); - } - execute(Opcodes.LOOKUPSWITCH, 0, null); - this.locals = null; - this.stack = null; -} - -public void visitMultiANewArrayInsn(final String desc, final int dims){ - if(mv != null) - { - mv.visitMultiANewArrayInsn(desc, dims); - } - execute(Opcodes.MULTIANEWARRAY, dims, desc); -} - -public void visitMaxs(final int maxStack, final int maxLocals){ - if(mv != null) - { - this.maxStack = Math.max(this.maxStack, maxStack); - this.maxLocals = Math.max(this.maxLocals, maxLocals); - mv.visitMaxs(this.maxStack, this.maxLocals); - } -} - -// ------------------------------------------------------------------------ - -private Object get(final int local){ - maxLocals = Math.max(maxLocals, local); - return local < locals.size() ? locals.get(local) : Opcodes.TOP; -} - -private void set(final int local, final Object type){ - maxLocals = Math.max(maxLocals, local); - while(local >= locals.size()) - { - locals.add(Opcodes.TOP); - } - locals.set(local, type); -} - -private void push(final Object type){ - stack.add(type); - maxStack = Math.max(maxStack, stack.size()); -} - -private void pushDesc(final String desc){ - int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; - switch(desc.charAt(index)) - { - case'V': - return; - case'Z': - case'C': - case'B': - case'S': - case'I': - push(Opcodes.INTEGER); - return; - case'F': - push(Opcodes.FLOAT); - return; - case'J': - push(Opcodes.LONG); - push(Opcodes.TOP); - return; - case'D': - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - return; - case'[': - if(index == 0) - { - push(desc); - } - else - { - push(desc.substring(index, desc.length())); - } - break; - // case 'L': - default: - if(index == 0) - { - push(desc.substring(1, desc.length() - 1)); - } - else - { - push(desc.substring(index + 1, desc.length() - 1)); - } - return; - } -} - -private Object pop(){ - return stack.remove(stack.size() - 1); -} - -private void pop(final int n){ - int size = stack.size(); - int end = size - n; - for(int i = size - 1; i >= end; --i) - { - stack.remove(i); - } -} - -private void pop(final String desc){ - char c = desc.charAt(0); - if(c == '(') - { - int n = 0; - Type[] types = Type.getArgumentTypes(desc); - for(int i = 0; i < types.length; ++i) - { - n += types[i].getSize(); - } - pop(n); - } - else if(c == 'J' || c == 'D') - { - pop(2); - } - else - { - pop(1); - } -} - -private void execute(final int opcode, final int iarg, final String sarg){ - if(this.locals == null) - { - return; - } - Object t1, t2, t3, t4; - switch(opcode) - { - case Opcodes.NOP: - case Opcodes.INEG: - case Opcodes.LNEG: - case Opcodes.FNEG: - case Opcodes.DNEG: - case Opcodes.I2B: - case Opcodes.I2C: - case Opcodes.I2S: - case Opcodes.GOTO: - case Opcodes.RETURN: - break; - case Opcodes.ACONST_NULL: - push(Opcodes.NULL); - break; - case Opcodes.ICONST_M1: - case Opcodes.ICONST_0: - case Opcodes.ICONST_1: - case Opcodes.ICONST_2: - case Opcodes.ICONST_3: - case Opcodes.ICONST_4: - case Opcodes.ICONST_5: - case Opcodes.BIPUSH: - case Opcodes.SIPUSH: - push(Opcodes.INTEGER); - break; - case Opcodes.LCONST_0: - case Opcodes.LCONST_1: - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Opcodes.FCONST_0: - case Opcodes.FCONST_1: - case Opcodes.FCONST_2: - push(Opcodes.FLOAT); - break; - case Opcodes.DCONST_0: - case Opcodes.DCONST_1: - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - break; - case Opcodes.ILOAD: - case Opcodes.FLOAD: - case Opcodes.ALOAD: - push(get(iarg)); - break; - case Opcodes.LLOAD: - case Opcodes.DLOAD: - push(get(iarg)); - push(Opcodes.TOP); - break; - case Opcodes.IALOAD: - case Opcodes.BALOAD: - case Opcodes.CALOAD: - case Opcodes.SALOAD: - pop(2); - push(Opcodes.INTEGER); - break; - case Opcodes.LALOAD: - case Opcodes.D2L: - pop(2); - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Opcodes.FALOAD: - pop(2); - push(Opcodes.FLOAT); - break; - case Opcodes.DALOAD: - case Opcodes.L2D: - pop(2); - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - break; - case Opcodes.AALOAD: - pop(1); - t1 = pop(); - pushDesc(((String) t1).substring(1)); - break; - case Opcodes.ISTORE: - case Opcodes.FSTORE: - case Opcodes.ASTORE: - t1 = pop(); - set(iarg, t1); - if(iarg > 0) - { - t2 = get(iarg - 1); - if(t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) - { - set(iarg - 1, Opcodes.TOP); - } - } - break; - case Opcodes.LSTORE: - case Opcodes.DSTORE: - pop(1); - t1 = pop(); - set(iarg, t1); - set(iarg + 1, Opcodes.TOP); - if(iarg > 0) - { - t2 = get(iarg - 1); - if(t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) - { - set(iarg - 1, Opcodes.TOP); - } - } - break; - case Opcodes.IASTORE: - case Opcodes.BASTORE: - case Opcodes.CASTORE: - case Opcodes.SASTORE: - case Opcodes.FASTORE: - case Opcodes.AASTORE: - pop(3); - break; - case Opcodes.LASTORE: - case Opcodes.DASTORE: - pop(4); - break; - case Opcodes.POP: - case Opcodes.IFEQ: - case Opcodes.IFNE: - case Opcodes.IFLT: - case Opcodes.IFGE: - case Opcodes.IFGT: - case Opcodes.IFLE: - case Opcodes.IRETURN: - case Opcodes.FRETURN: - case Opcodes.ARETURN: - case Opcodes.TABLESWITCH: - case Opcodes.LOOKUPSWITCH: - case Opcodes.ATHROW: - case Opcodes.MONITORENTER: - case Opcodes.MONITOREXIT: - case Opcodes.IFNULL: - case Opcodes.IFNONNULL: - pop(1); - break; - case Opcodes.POP2: - case Opcodes.IF_ICMPEQ: - case Opcodes.IF_ICMPNE: - case Opcodes.IF_ICMPLT: - case Opcodes.IF_ICMPGE: - case Opcodes.IF_ICMPGT: - case Opcodes.IF_ICMPLE: - case Opcodes.IF_ACMPEQ: - case Opcodes.IF_ACMPNE: - case Opcodes.LRETURN: - case Opcodes.DRETURN: - pop(2); - break; - case Opcodes.DUP: - t1 = pop(); - push(t1); - push(t1); - break; - case Opcodes.DUP_X1: - t1 = pop(); - t2 = pop(); - push(t1); - push(t2); - push(t1); - break; - case Opcodes.DUP_X2: - t1 = pop(); - t2 = pop(); - t3 = pop(); - push(t1); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.DUP2: - t1 = pop(); - t2 = pop(); - push(t2); - push(t1); - push(t2); - push(t1); - break; - case Opcodes.DUP2_X1: - t1 = pop(); - t2 = pop(); - t3 = pop(); - push(t2); - push(t1); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.DUP2_X2: - t1 = pop(); - t2 = pop(); - t3 = pop(); - t4 = pop(); - push(t2); - push(t1); - push(t4); - push(t3); - push(t2); - push(t1); - break; - case Opcodes.SWAP: - t1 = pop(); - t2 = pop(); - push(t1); - push(t2); - break; - case Opcodes.IADD: - case Opcodes.ISUB: - case Opcodes.IMUL: - case Opcodes.IDIV: - case Opcodes.IREM: - case Opcodes.IAND: - case Opcodes.IOR: - case Opcodes.IXOR: - case Opcodes.ISHL: - case Opcodes.ISHR: - case Opcodes.IUSHR: - case Opcodes.L2I: - case Opcodes.D2I: - case Opcodes.FCMPL: - case Opcodes.FCMPG: - pop(2); - push(Opcodes.INTEGER); - break; - case Opcodes.LADD: - case Opcodes.LSUB: - case Opcodes.LMUL: - case Opcodes.LDIV: - case Opcodes.LREM: - case Opcodes.LAND: - case Opcodes.LOR: - case Opcodes.LXOR: - pop(4); - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Opcodes.FADD: - case Opcodes.FSUB: - case Opcodes.FMUL: - case Opcodes.FDIV: - case Opcodes.FREM: - case Opcodes.L2F: - case Opcodes.D2F: - pop(2); - push(Opcodes.FLOAT); - break; - case Opcodes.DADD: - case Opcodes.DSUB: - case Opcodes.DMUL: - case Opcodes.DDIV: - case Opcodes.DREM: - pop(4); - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - break; - case Opcodes.LSHL: - case Opcodes.LSHR: - case Opcodes.LUSHR: - pop(3); - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Opcodes.IINC: - set(iarg, Opcodes.INTEGER); - break; - case Opcodes.I2L: - case Opcodes.F2L: - pop(1); - push(Opcodes.LONG); - push(Opcodes.TOP); - break; - case Opcodes.I2F: - pop(1); - push(Opcodes.FLOAT); - break; - case Opcodes.I2D: - case Opcodes.F2D: - pop(1); - push(Opcodes.DOUBLE); - push(Opcodes.TOP); - break; - case Opcodes.F2I: - case Opcodes.ARRAYLENGTH: - case Opcodes.INSTANCEOF: - pop(1); - push(Opcodes.INTEGER); - break; - case Opcodes.LCMP: - case Opcodes.DCMPL: - case Opcodes.DCMPG: - pop(4); - push(Opcodes.INTEGER); - break; - case Opcodes.JSR: - case Opcodes.RET: - throw new RuntimeException("JSR/RET are not supported"); - case Opcodes.GETSTATIC: - pushDesc(sarg); - break; - case Opcodes.PUTSTATIC: - pop(sarg); - break; - case Opcodes.GETFIELD: - pop(1); - pushDesc(sarg); - break; - case Opcodes.PUTFIELD: - pop(sarg); - pop(); - break; - case Opcodes.NEW: - push(labels.get(0)); - break; - case Opcodes.NEWARRAY: - pop(); - switch(iarg) - { - case Opcodes.T_BOOLEAN: - pushDesc("[Z"); - break; - case Opcodes.T_CHAR: - pushDesc("[C"); - break; - case Opcodes.T_BYTE: - pushDesc("[B"); - break; - case Opcodes.T_SHORT: - pushDesc("[S"); - break; - case Opcodes.T_INT: - pushDesc("[I"); - break; - case Opcodes.T_FLOAT: - pushDesc("[F"); - break; - case Opcodes.T_DOUBLE: - pushDesc("[D"); - break; - // case Opcodes.T_LONG: - default: - pushDesc("[J"); - break; - } - break; - case Opcodes.ANEWARRAY: - pop(); - if(sarg.charAt(0) == '[') - { - pushDesc("[" + sarg); - } - else - { - pushDesc("[L" + sarg + ";"); - } - break; - case Opcodes.CHECKCAST: - pop(); - if(sarg.charAt(0) == '[') - { - pushDesc(sarg); - } - else - { - push(sarg); - } - break; - // case Opcodes.MULTIANEWARRAY: - default: - pop(iarg); - pushDesc(sarg); - break; - } - labels = null; -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm.commons; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import clojure.asm.Label; +import clojure.asm.MethodAdapter; +import clojure.asm.MethodVisitor; +import clojure.asm.Opcodes; +import clojure.asm.Type; + +/** + * A {@link MethodAdapter} that keeps track of stack map frame changes between + * {@link #visitFrame(int,int,Object[],int,Object[]) visitFrame} calls. This + * adapter must be used with the + * {@link clojure.asm.ClassReader#EXPAND_FRAMES} option. Each visitXXX + * instruction delegates to the next visitor in the chain, if any, and then + * simulates the effect of this instruction on the stack map frame, represented + * by {@link #locals} and {@link #stack}. The next visitor in the chain can get + * the state of the stack map frame before each instruction by reading + * the value of these fields in its visitXXX methods (this requires a + * reference to the AnalyzerAdapter that is before it in the chain). + * + * @author Eric Bruneton + */ +public class AnalyzerAdapter extends MethodAdapter{ + +/** + * List of the local variable slots for current execution + * frame. Primitive types are represented by {@link Opcodes#TOP}, + * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or + * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by a + * two elements, the second one being TOP). Reference types are represented + * by String objects (representing internal names, or type descriptors for + * array types), and uninitialized types by Label objects (this label + * designates the NEW instruction that created this uninitialized value). + * This field is null for unreacheable instructions. + */ +public List locals; + +/** + * List of the operand stack slots for current execution + * frame. Primitive types are represented by {@link Opcodes#TOP}, + * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, + * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or + * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by a + * two elements, the second one being TOP). Reference types are represented + * by String objects (representing internal names, or type descriptors for + * array types), and uninitialized types by Label objects (this label + * designates the NEW instruction that created this uninitialized value). + * This field is null for unreacheable instructions. + */ +public List stack; + +/** + * The labels that designate the next instruction to be visited. May be + * null. + */ +private List labels; + +/** + * Information about uninitialized types in the current execution frame. + * This map associates internal names to Label objects. Each label + * designates a NEW instruction that created the currently uninitialized + * types, and the associated internal name represents the NEW operand, i.e. + * the final, initialized type value. + */ +private Map uninitializedTypes; + +/** + * The maximum stack size of this method. + */ +private int maxStack; + +/** + * The maximum number of local variables of this method. + */ +private int maxLocals; + +/** + * Creates a new {@link AnalyzerAdapter}. + * + * @param owner the owner's class name. + * @param access the method's access flags (see {@link Opcodes}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + * @param mv the method visitor to which this adapter delegates calls. May + * be null. + */ +public AnalyzerAdapter( + final String owner, + final int access, + final String name, + final String desc, + final MethodVisitor mv){ + super(mv); + locals = new ArrayList(); + stack = new ArrayList(); + uninitializedTypes = new HashMap(); + + if((access & Opcodes.ACC_STATIC) == 0) + { + if(name.equals("")) + { + locals.add(Opcodes.UNINITIALIZED_THIS); + } + else + { + locals.add(owner); + } + } + Type[] types = Type.getArgumentTypes(desc); + for(int i = 0; i < types.length; ++i) + { + Type type = types[i]; + switch(type.getSort()) + { + case Type.BOOLEAN: + case Type.CHAR: + case Type.BYTE: + case Type.SHORT: + case Type.INT: + locals.add(Opcodes.INTEGER); + break; + case Type.FLOAT: + locals.add(Opcodes.FLOAT); + break; + case Type.LONG: + locals.add(Opcodes.LONG); + locals.add(Opcodes.TOP); + break; + case Type.DOUBLE: + locals.add(Opcodes.DOUBLE); + locals.add(Opcodes.TOP); + break; + case Type.ARRAY: + locals.add(types[i].getDescriptor()); + break; + // case Type.OBJECT: + default: + locals.add(types[i].getInternalName()); + } + } +} + +public void visitFrame( + final int type, + final int nLocal, + final Object[] local, + final int nStack, + final Object[] stack){ + if(type != Opcodes.F_NEW) + { // uncompressed frame + throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag"); + } + + if(mv != null) + { + mv.visitFrame(type, nLocal, local, nStack, stack); + } + + if(this.locals != null) + { + this.locals.clear(); + this.stack.clear(); + } + else + { + this.locals = new ArrayList(); + this.stack = new ArrayList(); + } + visitFrameTypes(nLocal, local, this.locals); + visitFrameTypes(nStack, stack, this.stack); + maxStack = Math.max(maxStack, this.stack.size()); +} + +private void visitFrameTypes( + final int n, + final Object[] types, + final List result){ + for(int i = 0; i < n; ++i) + { + Object type = types[i]; + result.add(type); + if(type == Opcodes.LONG || type == Opcodes.DOUBLE) + { + result.add(Opcodes.TOP); + } + } +} + +public void visitInsn(final int opcode){ + if(mv != null) + { + mv.visitInsn(opcode); + } + execute(opcode, 0, null); + if((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) + || opcode == Opcodes.ATHROW) + { + this.locals = null; + this.stack = null; + } +} + +public void visitIntInsn(final int opcode, final int operand){ + if(mv != null) + { + mv.visitIntInsn(opcode, operand); + } + execute(opcode, operand, null); +} + +public void visitVarInsn(final int opcode, final int var){ + if(mv != null) + { + mv.visitVarInsn(opcode, var); + } + execute(opcode, var, null); +} + +public void visitTypeInsn(final int opcode, final String desc){ + if(opcode == Opcodes.NEW) + { + if(labels == null) + { + Label l = new Label(); + labels = new ArrayList(3); + labels.add(l); + if(mv != null) + { + mv.visitLabel(l); + } + } + for(int i = 0; i < labels.size(); ++i) + { + uninitializedTypes.put(labels.get(i), desc); + } + } + if(mv != null) + { + mv.visitTypeInsn(opcode, desc); + } + execute(opcode, 0, desc); +} + +public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc){ + if(mv != null) + { + mv.visitFieldInsn(opcode, owner, name, desc); + } + execute(opcode, 0, desc); +} + +public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc){ + if(mv != null) + { + mv.visitMethodInsn(opcode, owner, name, desc); + } + pop(desc); + if(opcode != Opcodes.INVOKESTATIC) + { + Object t = pop(); + if(opcode == Opcodes.INVOKESPECIAL && name.charAt(0) == '<') + { + Object u; + if(t == Opcodes.UNINITIALIZED_THIS) + { + u = owner; + } + else + { + u = uninitializedTypes.get(t); + } + for(int i = 0; i < locals.size(); ++i) + { + if(locals.get(i) == t) + { + locals.set(i, u); + } + } + for(int i = 0; i < stack.size(); ++i) + { + if(stack.get(i) == t) + { + stack.set(i, u); + } + } + } + } + pushDesc(desc); + labels = null; +} + +public void visitJumpInsn(final int opcode, final Label label){ + if(mv != null) + { + mv.visitJumpInsn(opcode, label); + } + execute(opcode, 0, null); + if(opcode == Opcodes.GOTO) + { + this.locals = null; + this.stack = null; + } +} + +public void visitLabel(final Label label){ + if(mv != null) + { + mv.visitLabel(label); + } + if(labels == null) + { + labels = new ArrayList(3); + } + labels.add(label); +} + +public void visitLdcInsn(final Object cst){ + if(mv != null) + { + mv.visitLdcInsn(cst); + } + if(cst instanceof Integer) + { + push(Opcodes.INTEGER); + } + else if(cst instanceof Long) + { + push(Opcodes.LONG); + push(Opcodes.TOP); + } + else if(cst instanceof Float) + { + push(Opcodes.FLOAT); + } + else if(cst instanceof Double) + { + push(Opcodes.DOUBLE); + push(Opcodes.TOP); + } + else if(cst instanceof String) + { + push("java/lang/String"); + } + else if(cst instanceof Type) + { + push("java/lang/Class"); + } + else + { + throw new IllegalArgumentException(); + } + labels = null; +} + +public void visitIincInsn(final int var, final int increment){ + if(mv != null) + { + mv.visitIincInsn(var, increment); + } + execute(Opcodes.IINC, var, null); +} + +public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label labels[]){ + if(mv != null) + { + mv.visitTableSwitchInsn(min, max, dflt, labels); + } + execute(Opcodes.TABLESWITCH, 0, null); + this.locals = null; + this.stack = null; +} + +public void visitLookupSwitchInsn( + final Label dflt, + final int keys[], + final Label labels[]){ + if(mv != null) + { + mv.visitLookupSwitchInsn(dflt, keys, labels); + } + execute(Opcodes.LOOKUPSWITCH, 0, null); + this.locals = null; + this.stack = null; +} + +public void visitMultiANewArrayInsn(final String desc, final int dims){ + if(mv != null) + { + mv.visitMultiANewArrayInsn(desc, dims); + } + execute(Opcodes.MULTIANEWARRAY, dims, desc); +} + +public void visitMaxs(final int maxStack, final int maxLocals){ + if(mv != null) + { + this.maxStack = Math.max(this.maxStack, maxStack); + this.maxLocals = Math.max(this.maxLocals, maxLocals); + mv.visitMaxs(this.maxStack, this.maxLocals); + } +} + +// ------------------------------------------------------------------------ + +private Object get(final int local){ + maxLocals = Math.max(maxLocals, local); + return local < locals.size() ? locals.get(local) : Opcodes.TOP; +} + +private void set(final int local, final Object type){ + maxLocals = Math.max(maxLocals, local); + while(local >= locals.size()) + { + locals.add(Opcodes.TOP); + } + locals.set(local, type); +} + +private void push(final Object type){ + stack.add(type); + maxStack = Math.max(maxStack, stack.size()); +} + +private void pushDesc(final String desc){ + int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; + switch(desc.charAt(index)) + { + case'V': + return; + case'Z': + case'C': + case'B': + case'S': + case'I': + push(Opcodes.INTEGER); + return; + case'F': + push(Opcodes.FLOAT); + return; + case'J': + push(Opcodes.LONG); + push(Opcodes.TOP); + return; + case'D': + push(Opcodes.DOUBLE); + push(Opcodes.TOP); + return; + case'[': + if(index == 0) + { + push(desc); + } + else + { + push(desc.substring(index, desc.length())); + } + break; + // case 'L': + default: + if(index == 0) + { + push(desc.substring(1, desc.length() - 1)); + } + else + { + push(desc.substring(index + 1, desc.length() - 1)); + } + return; + } +} + +private Object pop(){ + return stack.remove(stack.size() - 1); +} + +private void pop(final int n){ + int size = stack.size(); + int end = size - n; + for(int i = size - 1; i >= end; --i) + { + stack.remove(i); + } +} + +private void pop(final String desc){ + char c = desc.charAt(0); + if(c == '(') + { + int n = 0; + Type[] types = Type.getArgumentTypes(desc); + for(int i = 0; i < types.length; ++i) + { + n += types[i].getSize(); + } + pop(n); + } + else if(c == 'J' || c == 'D') + { + pop(2); + } + else + { + pop(1); + } +} + +private void execute(final int opcode, final int iarg, final String sarg){ + if(this.locals == null) + { + return; + } + Object t1, t2, t3, t4; + switch(opcode) + { + case Opcodes.NOP: + case Opcodes.INEG: + case Opcodes.LNEG: + case Opcodes.FNEG: + case Opcodes.DNEG: + case Opcodes.I2B: + case Opcodes.I2C: + case Opcodes.I2S: + case Opcodes.GOTO: + case Opcodes.RETURN: + break; + case Opcodes.ACONST_NULL: + push(Opcodes.NULL); + break; + case Opcodes.ICONST_M1: + case Opcodes.ICONST_0: + case Opcodes.ICONST_1: + case Opcodes.ICONST_2: + case Opcodes.ICONST_3: + case Opcodes.ICONST_4: + case Opcodes.ICONST_5: + case Opcodes.BIPUSH: + case Opcodes.SIPUSH: + push(Opcodes.INTEGER); + break; + case Opcodes.LCONST_0: + case Opcodes.LCONST_1: + push(Opcodes.LONG); + push(Opcodes.TOP); + break; + case Opcodes.FCONST_0: + case Opcodes.FCONST_1: + case Opcodes.FCONST_2: + push(Opcodes.FLOAT); + break; + case Opcodes.DCONST_0: + case Opcodes.DCONST_1: + push(Opcodes.DOUBLE); + push(Opcodes.TOP); + break; + case Opcodes.ILOAD: + case Opcodes.FLOAD: + case Opcodes.ALOAD: + push(get(iarg)); + break; + case Opcodes.LLOAD: + case Opcodes.DLOAD: + push(get(iarg)); + push(Opcodes.TOP); + break; + case Opcodes.IALOAD: + case Opcodes.BALOAD: + case Opcodes.CALOAD: + case Opcodes.SALOAD: + pop(2); + push(Opcodes.INTEGER); + break; + case Opcodes.LALOAD: + case Opcodes.D2L: + pop(2); + push(Opcodes.LONG); + push(Opcodes.TOP); + break; + case Opcodes.FALOAD: + pop(2); + push(Opcodes.FLOAT); + break; + case Opcodes.DALOAD: + case Opcodes.L2D: + pop(2); + push(Opcodes.DOUBLE); + push(Opcodes.TOP); + break; + case Opcodes.AALOAD: + pop(1); + t1 = pop(); + pushDesc(((String) t1).substring(1)); + break; + case Opcodes.ISTORE: + case Opcodes.FSTORE: + case Opcodes.ASTORE: + t1 = pop(); + set(iarg, t1); + if(iarg > 0) + { + t2 = get(iarg - 1); + if(t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) + { + set(iarg - 1, Opcodes.TOP); + } + } + break; + case Opcodes.LSTORE: + case Opcodes.DSTORE: + pop(1); + t1 = pop(); + set(iarg, t1); + set(iarg + 1, Opcodes.TOP); + if(iarg > 0) + { + t2 = get(iarg - 1); + if(t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) + { + set(iarg - 1, Opcodes.TOP); + } + } + break; + case Opcodes.IASTORE: + case Opcodes.BASTORE: + case Opcodes.CASTORE: + case Opcodes.SASTORE: + case Opcodes.FASTORE: + case Opcodes.AASTORE: + pop(3); + break; + case Opcodes.LASTORE: + case Opcodes.DASTORE: + pop(4); + break; + case Opcodes.POP: + case Opcodes.IFEQ: + case Opcodes.IFNE: + case Opcodes.IFLT: + case Opcodes.IFGE: + case Opcodes.IFGT: + case Opcodes.IFLE: + case Opcodes.IRETURN: + case Opcodes.FRETURN: + case Opcodes.ARETURN: + case Opcodes.TABLESWITCH: + case Opcodes.LOOKUPSWITCH: + case Opcodes.ATHROW: + case Opcodes.MONITORENTER: + case Opcodes.MONITOREXIT: + case Opcodes.IFNULL: + case Opcodes.IFNONNULL: + pop(1); + break; + case Opcodes.POP2: + case Opcodes.IF_ICMPEQ: + case Opcodes.IF_ICMPNE: + case Opcodes.IF_ICMPLT: + case Opcodes.IF_ICMPGE: + case Opcodes.IF_ICMPGT: + case Opcodes.IF_ICMPLE: + case Opcodes.IF_ACMPEQ: + case Opcodes.IF_ACMPNE: + case Opcodes.LRETURN: + case Opcodes.DRETURN: + pop(2); + break; + case Opcodes.DUP: + t1 = pop(); + push(t1); + push(t1); + break; + case Opcodes.DUP_X1: + t1 = pop(); + t2 = pop(); + push(t1); + push(t2); + push(t1); + break; + case Opcodes.DUP_X2: + t1 = pop(); + t2 = pop(); + t3 = pop(); + push(t1); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.DUP2: + t1 = pop(); + t2 = pop(); + push(t2); + push(t1); + push(t2); + push(t1); + break; + case Opcodes.DUP2_X1: + t1 = pop(); + t2 = pop(); + t3 = pop(); + push(t2); + push(t1); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.DUP2_X2: + t1 = pop(); + t2 = pop(); + t3 = pop(); + t4 = pop(); + push(t2); + push(t1); + push(t4); + push(t3); + push(t2); + push(t1); + break; + case Opcodes.SWAP: + t1 = pop(); + t2 = pop(); + push(t1); + push(t2); + break; + case Opcodes.IADD: + case Opcodes.ISUB: + case Opcodes.IMUL: + case Opcodes.IDIV: + case Opcodes.IREM: + case Opcodes.IAND: + case Opcodes.IOR: + case Opcodes.IXOR: + case Opcodes.ISHL: + case Opcodes.ISHR: + case Opcodes.IUSHR: + case Opcodes.L2I: + case Opcodes.D2I: + case Opcodes.FCMPL: + case Opcodes.FCMPG: + pop(2); + push(Opcodes.INTEGER); + break; + case Opcodes.LADD: + case Opcodes.LSUB: + case Opcodes.LMUL: + case Opcodes.LDIV: + case Opcodes.LREM: + case Opcodes.LAND: + case Opcodes.LOR: + case Opcodes.LXOR: + pop(4); + push(Opcodes.LONG); + push(Opcodes.TOP); + break; + case Opcodes.FADD: + case Opcodes.FSUB: + case Opcodes.FMUL: + case Opcodes.FDIV: + case Opcodes.FREM: + case Opcodes.L2F: + case Opcodes.D2F: + pop(2); + push(Opcodes.FLOAT); + break; + case Opcodes.DADD: + case Opcodes.DSUB: + case Opcodes.DMUL: + case Opcodes.DDIV: + case Opcodes.DREM: + pop(4); + push(Opcodes.DOUBLE); + push(Opcodes.TOP); + break; + case Opcodes.LSHL: + case Opcodes.LSHR: + case Opcodes.LUSHR: + pop(3); + push(Opcodes.LONG); + push(Opcodes.TOP); + break; + case Opcodes.IINC: + set(iarg, Opcodes.INTEGER); + break; + case Opcodes.I2L: + case Opcodes.F2L: + pop(1); + push(Opcodes.LONG); + push(Opcodes.TOP); + break; + case Opcodes.I2F: + pop(1); + push(Opcodes.FLOAT); + break; + case Opcodes.I2D: + case Opcodes.F2D: + pop(1); + push(Opcodes.DOUBLE); + push(Opcodes.TOP); + break; + case Opcodes.F2I: + case Opcodes.ARRAYLENGTH: + case Opcodes.INSTANCEOF: + pop(1); + push(Opcodes.INTEGER); + break; + case Opcodes.LCMP: + case Opcodes.DCMPL: + case Opcodes.DCMPG: + pop(4); + push(Opcodes.INTEGER); + break; + case Opcodes.JSR: + case Opcodes.RET: + throw new RuntimeException("JSR/RET are not supported"); + case Opcodes.GETSTATIC: + pushDesc(sarg); + break; + case Opcodes.PUTSTATIC: + pop(sarg); + break; + case Opcodes.GETFIELD: + pop(1); + pushDesc(sarg); + break; + case Opcodes.PUTFIELD: + pop(sarg); + pop(); + break; + case Opcodes.NEW: + push(labels.get(0)); + break; + case Opcodes.NEWARRAY: + pop(); + switch(iarg) + { + case Opcodes.T_BOOLEAN: + pushDesc("[Z"); + break; + case Opcodes.T_CHAR: + pushDesc("[C"); + break; + case Opcodes.T_BYTE: + pushDesc("[B"); + break; + case Opcodes.T_SHORT: + pushDesc("[S"); + break; + case Opcodes.T_INT: + pushDesc("[I"); + break; + case Opcodes.T_FLOAT: + pushDesc("[F"); + break; + case Opcodes.T_DOUBLE: + pushDesc("[D"); + break; + // case Opcodes.T_LONG: + default: + pushDesc("[J"); + break; + } + break; + case Opcodes.ANEWARRAY: + pop(); + if(sarg.charAt(0) == '[') + { + pushDesc("[" + sarg); + } + else + { + pushDesc("[L" + sarg + ";"); + } + break; + case Opcodes.CHECKCAST: + pop(); + if(sarg.charAt(0) == '[') + { + pushDesc(sarg); + } + else + { + push(sarg); + } + break; + // case Opcodes.MULTIANEWARRAY: + default: + pop(iarg); + pushDesc(sarg); + break; + } + labels = null; +} +} diff --git a/src/jvm/clojure/asm/commons/CodeSizeEvaluator.java b/src/jvm/clojure/asm/commons/CodeSizeEvaluator.java index 234db41..b30fff6 100644 --- a/src/jvm/clojure/asm/commons/CodeSizeEvaluator.java +++ b/src/jvm/clojure/asm/commons/CodeSizeEvaluator.java @@ -1,234 +1,234 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm.commons; - -import clojure.asm.Label; -import clojure.asm.MethodAdapter; -import clojure.asm.MethodVisitor; -import clojure.asm.Opcodes; - -/** - * A {@link MethodAdapter} that can be used to approximate method size. - * - * @author Eugene Kuleshov - */ -public class CodeSizeEvaluator extends MethodAdapter implements Opcodes{ - -private int minSize; - -private int maxSize; - -public CodeSizeEvaluator(final MethodVisitor mv){ - super(mv); -} - -public int getMinSize(){ - return this.minSize; -} - -public int getMaxSize(){ - return this.maxSize; -} - -public void visitInsn(final int opcode){ - minSize += 1; - maxSize += 1; - if(mv != null) - { - mv.visitInsn(opcode); - } -} - -public void visitIntInsn(final int opcode, final int operand){ - if(opcode == SIPUSH) - { - minSize += 3; - maxSize += 3; - } - else - { - minSize += 2; - maxSize += 2; - } - if(mv != null) - { - mv.visitIntInsn(opcode, operand); - } -} - -public void visitVarInsn(final int opcode, final int var){ - if(var < 4 && opcode != Opcodes.RET) - { - minSize += 1; - maxSize += 1; - } - else if(var >= 256) - { - minSize += 4; - maxSize += 4; - } - else - { - minSize += 2; - maxSize += 2; - } - if(mv != null) - { - mv.visitVarInsn(opcode, var); - } -} - -public void visitTypeInsn(final int opcode, final String desc){ - minSize += 3; - maxSize += 3; - if(mv != null) - { - mv.visitTypeInsn(opcode, desc); - } -} - -public void visitFieldInsn( - final int opcode, - final String owner, - final String name, - final String desc){ - minSize += 3; - maxSize += 3; - if(mv != null) - { - mv.visitFieldInsn(opcode, owner, name, desc); - } -} - -public void visitMethodInsn( - final int opcode, - final String owner, - final String name, - final String desc){ - if(opcode == INVOKEINTERFACE) - { - minSize += 5; - maxSize += 5; - } - else - { - minSize += 3; - maxSize += 3; - } - if(mv != null) - { - mv.visitMethodInsn(opcode, owner, name, desc); - } -} - -public void visitJumpInsn(final int opcode, final Label label){ - minSize += 3; - if(opcode == GOTO || opcode == JSR) - { - maxSize += 5; - } - else - { - maxSize += 8; - } - if(mv != null) - { - mv.visitJumpInsn(opcode, label); - } -} - -public void visitLdcInsn(final Object cst){ - if(cst instanceof Long || cst instanceof Double) - { - minSize += 3; - maxSize += 3; - } - else - { - minSize += 2; - maxSize += 3; - } - if(mv != null) - { - mv.visitLdcInsn(cst); - } -} - -public void visitIincInsn(final int var, final int increment){ - if(var > 255 || increment > 127 || increment < -128) - { - minSize += 6; - maxSize += 6; - } - else - { - minSize += 3; - maxSize += 3; - } - if(mv != null) - { - mv.visitIincInsn(var, increment); - } -} - -public void visitTableSwitchInsn( - final int min, - final int max, - final Label dflt, - final Label[] labels){ - minSize += 13 + labels.length * 4; - maxSize += 16 + labels.length * 4; - if(mv != null) - { - mv.visitTableSwitchInsn(min, max, dflt, labels); - } -} - -public void visitLookupSwitchInsn( - final Label dflt, - final int[] keys, - final Label[] labels){ - minSize += 9 + keys.length * 8; - maxSize += 12 + keys.length * 8; - if(mv != null) - { - mv.visitLookupSwitchInsn(dflt, keys, labels); - } -} - -public void visitMultiANewArrayInsn(final String desc, final int dims){ - minSize += 4; - maxSize += 4; - if(mv != null) - { - mv.visitMultiANewArrayInsn(desc, dims); - } -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm.commons; + +import clojure.asm.Label; +import clojure.asm.MethodAdapter; +import clojure.asm.MethodVisitor; +import clojure.asm.Opcodes; + +/** + * A {@link MethodAdapter} that can be used to approximate method size. + * + * @author Eugene Kuleshov + */ +public class CodeSizeEvaluator extends MethodAdapter implements Opcodes{ + +private int minSize; + +private int maxSize; + +public CodeSizeEvaluator(final MethodVisitor mv){ + super(mv); +} + +public int getMinSize(){ + return this.minSize; +} + +public int getMaxSize(){ + return this.maxSize; +} + +public void visitInsn(final int opcode){ + minSize += 1; + maxSize += 1; + if(mv != null) + { + mv.visitInsn(opcode); + } +} + +public void visitIntInsn(final int opcode, final int operand){ + if(opcode == SIPUSH) + { + minSize += 3; + maxSize += 3; + } + else + { + minSize += 2; + maxSize += 2; + } + if(mv != null) + { + mv.visitIntInsn(opcode, operand); + } +} + +public void visitVarInsn(final int opcode, final int var){ + if(var < 4 && opcode != Opcodes.RET) + { + minSize += 1; + maxSize += 1; + } + else if(var >= 256) + { + minSize += 4; + maxSize += 4; + } + else + { + minSize += 2; + maxSize += 2; + } + if(mv != null) + { + mv.visitVarInsn(opcode, var); + } +} + +public void visitTypeInsn(final int opcode, final String desc){ + minSize += 3; + maxSize += 3; + if(mv != null) + { + mv.visitTypeInsn(opcode, desc); + } +} + +public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc){ + minSize += 3; + maxSize += 3; + if(mv != null) + { + mv.visitFieldInsn(opcode, owner, name, desc); + } +} + +public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc){ + if(opcode == INVOKEINTERFACE) + { + minSize += 5; + maxSize += 5; + } + else + { + minSize += 3; + maxSize += 3; + } + if(mv != null) + { + mv.visitMethodInsn(opcode, owner, name, desc); + } +} + +public void visitJumpInsn(final int opcode, final Label label){ + minSize += 3; + if(opcode == GOTO || opcode == JSR) + { + maxSize += 5; + } + else + { + maxSize += 8; + } + if(mv != null) + { + mv.visitJumpInsn(opcode, label); + } +} + +public void visitLdcInsn(final Object cst){ + if(cst instanceof Long || cst instanceof Double) + { + minSize += 3; + maxSize += 3; + } + else + { + minSize += 2; + maxSize += 3; + } + if(mv != null) + { + mv.visitLdcInsn(cst); + } +} + +public void visitIincInsn(final int var, final int increment){ + if(var > 255 || increment > 127 || increment < -128) + { + minSize += 6; + maxSize += 6; + } + else + { + minSize += 3; + maxSize += 3; + } + if(mv != null) + { + mv.visitIincInsn(var, increment); + } +} + +public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label[] labels){ + minSize += 13 + labels.length * 4; + maxSize += 16 + labels.length * 4; + if(mv != null) + { + mv.visitTableSwitchInsn(min, max, dflt, labels); + } +} + +public void visitLookupSwitchInsn( + final Label dflt, + final int[] keys, + final Label[] labels){ + minSize += 9 + keys.length * 8; + maxSize += 12 + keys.length * 8; + if(mv != null) + { + mv.visitLookupSwitchInsn(dflt, keys, labels); + } +} + +public void visitMultiANewArrayInsn(final String desc, final int dims){ + minSize += 4; + maxSize += 4; + if(mv != null) + { + mv.visitMultiANewArrayInsn(desc, dims); + } +} +} diff --git a/src/jvm/clojure/asm/commons/EmptyVisitor.java b/src/jvm/clojure/asm/commons/EmptyVisitor.java index b721b08..b83e131 100644 --- a/src/jvm/clojure/asm/commons/EmptyVisitor.java +++ b/src/jvm/clojure/asm/commons/EmptyVisitor.java @@ -1,221 +1,221 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm.commons; - -import clojure.asm.AnnotationVisitor; -import clojure.asm.Attribute; -import clojure.asm.ClassVisitor; -import clojure.asm.FieldVisitor; -import clojure.asm.Label; -import clojure.asm.MethodVisitor; - -/** - * An empty implementation of the ASM visitor interfaces. - * - * @author Eric Bruneton - */ -public class EmptyVisitor implements - ClassVisitor, - FieldVisitor, - MethodVisitor, - AnnotationVisitor{ - -public void visit( - final int version, - final int access, - final String name, - final String signature, - final String superName, - final String[] interfaces){ -} - -public void visitSource(final String source, final String debug){ -} - -public void visitOuterClass( - final String owner, - final String name, - final String desc){ -} - -public AnnotationVisitor visitAnnotation( - final String desc, - final boolean visible){ - return this; -} - -public void visitAttribute(final Attribute attr){ -} - -public void visitInnerClass( - final String name, - final String outerName, - final String innerName, - final int access){ -} - -public FieldVisitor visitField( - final int access, - final String name, - final String desc, - final String signature, - final Object value){ - return this; -} - -public MethodVisitor visitMethod( - final int access, - final String name, - final String desc, - final String signature, - final String[] exceptions){ - return this; -} - -public void visitEnd(){ -} - -public AnnotationVisitor visitAnnotationDefault(){ - return this; -} - -public AnnotationVisitor visitParameterAnnotation( - final int parameter, - final String desc, - final boolean visible){ - return this; -} - -public void visitCode(){ -} - -public void visitFrame( - final int type, - final int nLocal, - final Object[] local, - final int nStack, - final Object[] stack){ -} - -public void visitInsn(final int opcode){ -} - -public void visitIntInsn(final int opcode, final int operand){ -} - -public void visitVarInsn(final int opcode, final int var){ -} - -public void visitTypeInsn(final int opcode, final String desc){ -} - -public void visitFieldInsn( - final int opcode, - final String owner, - final String name, - final String desc){ -} - -public void visitMethodInsn( - final int opcode, - final String owner, - final String name, - final String desc){ -} - -public void visitJumpInsn(final int opcode, final Label label){ -} - -public void visitLabel(final Label label){ -} - -public void visitLdcInsn(final Object cst){ -} - -public void visitIincInsn(final int var, final int increment){ -} - -public void visitTableSwitchInsn( - final int min, - final int max, - final Label dflt, - final Label labels[]){ -} - -public void visitLookupSwitchInsn( - final Label dflt, - final int keys[], - final Label labels[]){ -} - -public void visitMultiANewArrayInsn(final String desc, final int dims){ -} - -public void visitTryCatchBlock( - final Label start, - final Label end, - final Label handler, - final String type){ -} - -public void visitLocalVariable( - final String name, - final String desc, - final String signature, - final Label start, - final Label end, - final int index){ -} - -public void visitLineNumber(final int line, final Label start){ -} - -public void visitMaxs(final int maxStack, final int maxLocals){ -} - -public void visit(final String name, final Object value){ -} - -public void visitEnum( - final String name, - final String desc, - final String value){ -} - -public AnnotationVisitor visitAnnotation( - final String name, - final String desc){ - return this; -} - -public AnnotationVisitor visitArray(final String name){ - return this; -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm.commons; + +import clojure.asm.AnnotationVisitor; +import clojure.asm.Attribute; +import clojure.asm.ClassVisitor; +import clojure.asm.FieldVisitor; +import clojure.asm.Label; +import clojure.asm.MethodVisitor; + +/** + * An empty implementation of the ASM visitor interfaces. + * + * @author Eric Bruneton + */ +public class EmptyVisitor implements + ClassVisitor, + FieldVisitor, + MethodVisitor, + AnnotationVisitor{ + +public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces){ +} + +public void visitSource(final String source, final String debug){ +} + +public void visitOuterClass( + final String owner, + final String name, + final String desc){ +} + +public AnnotationVisitor visitAnnotation( + final String desc, + final boolean visible){ + return this; +} + +public void visitAttribute(final Attribute attr){ +} + +public void visitInnerClass( + final String name, + final String outerName, + final String innerName, + final int access){ +} + +public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value){ + return this; +} + +public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions){ + return this; +} + +public void visitEnd(){ +} + +public AnnotationVisitor visitAnnotationDefault(){ + return this; +} + +public AnnotationVisitor visitParameterAnnotation( + final int parameter, + final String desc, + final boolean visible){ + return this; +} + +public void visitCode(){ +} + +public void visitFrame( + final int type, + final int nLocal, + final Object[] local, + final int nStack, + final Object[] stack){ +} + +public void visitInsn(final int opcode){ +} + +public void visitIntInsn(final int opcode, final int operand){ +} + +public void visitVarInsn(final int opcode, final int var){ +} + +public void visitTypeInsn(final int opcode, final String desc){ +} + +public void visitFieldInsn( + final int opcode, + final String owner, + final String name, + final String desc){ +} + +public void visitMethodInsn( + final int opcode, + final String owner, + final String name, + final String desc){ +} + +public void visitJumpInsn(final int opcode, final Label label){ +} + +public void visitLabel(final Label label){ +} + +public void visitLdcInsn(final Object cst){ +} + +public void visitIincInsn(final int var, final int increment){ +} + +public void visitTableSwitchInsn( + final int min, + final int max, + final Label dflt, + final Label labels[]){ +} + +public void visitLookupSwitchInsn( + final Label dflt, + final int keys[], + final Label labels[]){ +} + +public void visitMultiANewArrayInsn(final String desc, final int dims){ +} + +public void visitTryCatchBlock( + final Label start, + final Label end, + final Label handler, + final String type){ +} + +public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index){ +} + +public void visitLineNumber(final int line, final Label start){ +} + +public void visitMaxs(final int maxStack, final int maxLocals){ +} + +public void visit(final String name, final Object value){ +} + +public void visitEnum( + final String name, + final String desc, + final String value){ +} + +public AnnotationVisitor visitAnnotation( + final String name, + final String desc){ + return this; +} + +public AnnotationVisitor visitArray(final String name){ + return this; +} +} diff --git a/src/jvm/clojure/asm/commons/GeneratorAdapter.java b/src/jvm/clojure/asm/commons/GeneratorAdapter.java index 1859641..b8b786d 100644 --- a/src/jvm/clojure/asm/commons/GeneratorAdapter.java +++ b/src/jvm/clojure/asm/commons/GeneratorAdapter.java @@ -1,1533 +1,1533 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm.commons; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import clojure.asm.ClassVisitor; -import clojure.asm.Label; -import clojure.asm.MethodVisitor; -import clojure.asm.Opcodes; -import clojure.asm.Type; - -/** - * A {@link clojure.asm.MethodAdapter} with convenient methods to generate - * code. For example, using this adapter, the class below - *

- *

- * public class Example {
- *     public static void main(String[] args) {
- *         System.out.println("Hello world!");
- *     }
- * }
- * 
- *

- * can be generated as follows: - *

- *

- * ClassWriter cw = new ClassWriter(true);
- * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
- * 

- * Method m = Method.getMethod("void <init> ()"); - * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); - * mg.loadThis(); - * mg.invokeConstructor(Type.getType(Object.class), m); - * mg.returnValue(); - * mg.endMethod(); - *

- * m = Method.getMethod("void main (String[])"); - * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw); - * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); - * mg.push("Hello world!"); - * mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)")); - * mg.returnValue(); - * mg.endMethod(); - *

- * cw.visitEnd(); - *

- * - * @author Juozas Baliuka - * @author Chris Nokleberg - * @author Eric Bruneton - */ -public class GeneratorAdapter extends LocalVariablesSorter{ - -private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte"); - -private final static Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean"); - -private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short"); - -private final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character"); - -private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer"); - -private final static Type FLOAT_TYPE = Type.getObjectType("java/lang/Float"); - -private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long"); - -private final static Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double"); - -private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number"); - -private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); - -private final static Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()"); - -private final static Method CHAR_VALUE = Method.getMethod("char charValue()"); - -private final static Method INT_VALUE = Method.getMethod("int intValue()"); - -private final static Method FLOAT_VALUE = Method.getMethod("float floatValue()"); - -private final static Method LONG_VALUE = Method.getMethod("long longValue()"); - -private final static Method DOUBLE_VALUE = Method.getMethod("double doubleValue()"); - -/** - * Constant for the {@link #math math} method. - */ -public final static int ADD = Opcodes.IADD; - -/** - * Constant for the {@link #math math} method. - */ -public final static int SUB = Opcodes.ISUB; - -/** - * Constant for the {@link #math math} method. - */ -public final static int MUL = Opcodes.IMUL; - -/** - * Constant for the {@link #math math} method. - */ -public final static int DIV = Opcodes.IDIV; - -/** - * Constant for the {@link #math math} method. - */ -public final static int REM = Opcodes.IREM; - -/** - * Constant for the {@link #math math} method. - */ -public final static int NEG = Opcodes.INEG; - -/** - * Constant for the {@link #math math} method. - */ -public final static int SHL = Opcodes.ISHL; - -/** - * Constant for the {@link #math math} method. - */ -public final static int SHR = Opcodes.ISHR; - -/** - * Constant for the {@link #math math} method. - */ -public final static int USHR = Opcodes.IUSHR; - -/** - * Constant for the {@link #math math} method. - */ -public final static int AND = Opcodes.IAND; - -/** - * Constant for the {@link #math math} method. - */ -public final static int OR = Opcodes.IOR; - -/** - * Constant for the {@link #math math} method. - */ -public final static int XOR = Opcodes.IXOR; - -/** - * Constant for the {@link #ifCmp ifCmp} method. - */ -public final static int EQ = Opcodes.IFEQ; - -/** - * Constant for the {@link #ifCmp ifCmp} method. - */ -public final static int NE = Opcodes.IFNE; - -/** - * Constant for the {@link #ifCmp ifCmp} method. - */ -public final static int LT = Opcodes.IFLT; - -/** - * Constant for the {@link #ifCmp ifCmp} method. - */ -public final static int GE = Opcodes.IFGE; - -/** - * Constant for the {@link #ifCmp ifCmp} method. - */ -public final static int GT = Opcodes.IFGT; - -/** - * Constant for the {@link #ifCmp ifCmp} method. - */ -public final static int LE = Opcodes.IFLE; - -/** - * Access flags of the method visited by this adapter. - */ -private final int access; - -/** - * Return type of the method visited by this adapter. - */ -private final Type returnType; - -/** - * Argument types of the method visited by this adapter. - */ -private final Type[] argumentTypes; - -/** - * Types of the local variables of the method visited by this adapter. - */ -private final List localTypes = new ArrayList(); - -/** - * Creates a new {@link GeneratorAdapter}. - * - * @param mv the method visitor to which this adapter delegates calls. - * @param access the method's access flags (see {@link Opcodes}). - * @param name the method's name. - * @param desc the method's descriptor (see {@link Type Type}). - */ -public GeneratorAdapter( - final MethodVisitor mv, - final int access, - final String name, - final String desc){ - super(access, desc, mv); - this.access = access; - this.returnType = Type.getReturnType(desc); - this.argumentTypes = Type.getArgumentTypes(desc); -} - -/** - * Creates a new {@link GeneratorAdapter}. - * - * @param access access flags of the adapted method. - * @param method the adapted method. - * @param mv the method visitor to which this adapter delegates calls. - */ -public GeneratorAdapter( - final int access, - final Method method, - final MethodVisitor mv){ - super(access, method.getDescriptor(), mv); - this.access = access; - this.returnType = method.getReturnType(); - this.argumentTypes = method.getArgumentTypes(); -} - -/** - * Creates a new {@link GeneratorAdapter}. - * - * @param access access flags of the adapted method. - * @param method the adapted method. - * @param signature the signature of the adapted method (may be - * null). - * @param exceptions the exceptions thrown by the adapted method (may be - * null). - * @param cv the class visitor to which this adapter delegates calls. - */ -public GeneratorAdapter( - final int access, - final Method method, - final String signature, - final Type[] exceptions, - final ClassVisitor cv){ - this(access, method, cv.visitMethod(access, - method.getName(), - method.getDescriptor(), - signature, - getInternalNames(exceptions))); -} - -/** - * Returns the internal names of the given types. - * - * @param types a set of types. - * @return the internal names of the given types. - */ -private static String[] getInternalNames(final Type[] types){ - if(types == null) - { - return null; - } - String[] names = new String[types.length]; - for(int i = 0; i < names.length; ++i) - { - names[i] = types[i].getInternalName(); - } - return names; -} - -// ------------------------------------------------------------------------ -// Instructions to push constants on the stack -// ------------------------------------------------------------------------ - -/** - * Generates the instruction to push the given value on the stack. - * - * @param value the value to be pushed on the stack. - */ -public void push(final boolean value){ - push(value ? 1 : 0); -} - -/** - * Generates the instruction to push the given value on the stack. - * - * @param value the value to be pushed on the stack. - */ -public void push(final int value){ - if(value >= -1 && value <= 5) - { - mv.visitInsn(Opcodes.ICONST_0 + value); - } - else if(value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) - { - mv.visitIntInsn(Opcodes.BIPUSH, value); - } - else if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) - { - mv.visitIntInsn(Opcodes.SIPUSH, value); - } - else - { - mv.visitLdcInsn(new Integer(value)); - } -} - -/** - * Generates the instruction to push the given value on the stack. - * - * @param value the value to be pushed on the stack. - */ -public void push(final long value){ - if(value == 0L || value == 1L) - { - mv.visitInsn(Opcodes.LCONST_0 + (int) value); - } - else - { - mv.visitLdcInsn(new Long(value)); - } -} - -/** - * Generates the instruction to push the given value on the stack. - * - * @param value the value to be pushed on the stack. - */ -public void push(final float value){ - int bits = Float.floatToIntBits(value); - if(bits == 0L || bits == 0x3f800000 || bits == 0x40000000) - { // 0..2 - mv.visitInsn(Opcodes.FCONST_0 + (int) value); - } - else - { - mv.visitLdcInsn(new Float(value)); - } -} - -/** - * Generates the instruction to push the given value on the stack. - * - * @param value the value to be pushed on the stack. - */ -public void push(final double value){ - long bits = Double.doubleToLongBits(value); - if(bits == 0L || bits == 0x3ff0000000000000L) - { // +0.0d and 1.0d - mv.visitInsn(Opcodes.DCONST_0 + (int) value); - } - else - { - mv.visitLdcInsn(new Double(value)); - } -} - -/** - * Generates the instruction to push the given value on the stack. - * - * @param value the value to be pushed on the stack. May be null. - */ -public void push(final String value){ - if(value == null) - { - mv.visitInsn(Opcodes.ACONST_NULL); - } - else - { - mv.visitLdcInsn(value); - } -} - -/** - * Generates the instruction to push the given value on the stack. - * - * @param value the value to be pushed on the stack. - */ -public void push(final Type value){ - if(value == null) - { - mv.visitInsn(Opcodes.ACONST_NULL); - } - else - { - mv.visitLdcInsn(value); - } -} - -// ------------------------------------------------------------------------ -// Instructions to load and store method arguments -// ------------------------------------------------------------------------ - -/** - * Returns the index of the given method argument in the frame's local - * variables array. - * - * @param arg the index of a method argument. - * @return the index of the given method argument in the frame's local - * variables array. - */ -private int getArgIndex(final int arg){ - int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0; - for(int i = 0; i < arg; i++) - { - index += argumentTypes[i].getSize(); - } - return index; -} - -/** - * Generates the instruction to push a local variable on the stack. - * - * @param type the type of the local variable to be loaded. - * @param index an index in the frame's local variables array. - */ -private void loadInsn(final Type type, final int index){ - mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index); -} - -/** - * Generates the instruction to store the top stack value in a local - * variable. - * - * @param type the type of the local variable to be stored. - * @param index an index in the frame's local variables array. - */ -private void storeInsn(final Type type, final int index){ - mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index); -} - -/** - * Generates the instruction to load 'this' on the stack. - */ -public void loadThis(){ - if((access & Opcodes.ACC_STATIC) != 0) - { - throw new IllegalStateException("no 'this' pointer within static method"); - } - mv.visitVarInsn(Opcodes.ALOAD, 0); -} - -/** - * Generates the instruction to load the given method argument on the stack. - * - * @param arg the index of a method argument. - */ -public void loadArg(final int arg){ - loadInsn(argumentTypes[arg], getArgIndex(arg)); -} - -/** - * Generates the instructions to load the given method arguments on the - * stack. - * - * @param arg the index of the first method argument to be loaded. - * @param count the number of method arguments to be loaded. - */ -public void loadArgs(final int arg, final int count){ - int index = getArgIndex(arg); - for(int i = 0; i < count; ++i) - { - Type t = argumentTypes[arg + i]; - loadInsn(t, index); - index += t.getSize(); - } -} - -/** - * Generates the instructions to load all the method arguments on the stack. - */ -public void loadArgs(){ - loadArgs(0, argumentTypes.length); -} - -/** - * Generates the instructions to load all the method arguments on the stack, - * as a single object array. - */ -public void loadArgArray(){ - push(argumentTypes.length); - newArray(OBJECT_TYPE); - for(int i = 0; i < argumentTypes.length; i++) - { - dup(); - push(i); - loadArg(i); - box(argumentTypes[i]); - arrayStore(OBJECT_TYPE); - } -} - -/** - * Generates the instruction to store the top stack value in the given - * method argument. - * - * @param arg the index of a method argument. - */ -public void storeArg(final int arg){ - storeInsn(argumentTypes[arg], getArgIndex(arg)); -} - -// ------------------------------------------------------------------------ -// Instructions to load and store local variables -// ------------------------------------------------------------------------ - -/** - * Returns the type of the given local variable. - * - * @param local a local variable identifier, as returned by - * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. - * @return the type of the given local variable. - */ -public Type getLocalType(final int local){ - return (Type) localTypes.get(local - firstLocal); -} - -protected void setLocalType(final int local, final Type type){ - int index = local - firstLocal; - while(localTypes.size() < index + 1) - { - localTypes.add(null); - } - localTypes.set(index, type); -} - -/** - * Generates the instruction to load the given local variable on the stack. - * - * @param local a local variable identifier, as returned by - * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. - */ -public void loadLocal(final int local){ - loadInsn(getLocalType(local), local); -} - -/** - * Generates the instruction to load the given local variable on the stack. - * - * @param local a local variable identifier, as returned by - * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. - * @param type the type of this local variable. - */ -public void loadLocal(final int local, final Type type){ - setLocalType(local, type); - loadInsn(type, local); -} - -/** - * Generates the instruction to store the top stack value in the given local - * variable. - * - * @param local a local variable identifier, as returned by - * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. - */ -public void storeLocal(final int local){ - storeInsn(getLocalType(local), local); -} - -/** - * Generates the instruction to store the top stack value in the given local - * variable. - * - * @param local a local variable identifier, as returned by - * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. - * @param type the type of this local variable. - */ -public void storeLocal(final int local, final Type type){ - setLocalType(local, type); - storeInsn(type, local); -} - -/** - * Generates the instruction to load an element from an array. - * - * @param type the type of the array element to be loaded. - */ -public void arrayLoad(final Type type){ - mv.visitInsn(type.getOpcode(Opcodes.IALOAD)); -} - -/** - * Generates the instruction to store an element in an array. - * - * @param type the type of the array element to be stored. - */ -public void arrayStore(final Type type){ - mv.visitInsn(type.getOpcode(Opcodes.IASTORE)); -} - -// ------------------------------------------------------------------------ -// Instructions to manage the stack -// ------------------------------------------------------------------------ - -/** - * Generates a POP instruction. - */ -public void pop(){ - mv.visitInsn(Opcodes.POP); -} - -/** - * Generates a POP2 instruction. - */ -public void pop2(){ - mv.visitInsn(Opcodes.POP2); -} - -/** - * Generates a DUP instruction. - */ -public void dup(){ - mv.visitInsn(Opcodes.DUP); -} - -/** - * Generates a DUP2 instruction. - */ -public void dup2(){ - mv.visitInsn(Opcodes.DUP2); -} - -/** - * Generates a DUP_X1 instruction. - */ -public void dupX1(){ - mv.visitInsn(Opcodes.DUP_X1); -} - -/** - * Generates a DUP_X2 instruction. - */ -public void dupX2(){ - mv.visitInsn(Opcodes.DUP_X2); -} - -/** - * Generates a DUP2_X1 instruction. - */ -public void dup2X1(){ - mv.visitInsn(Opcodes.DUP2_X1); -} - -/** - * Generates a DUP2_X2 instruction. - */ -public void dup2X2(){ - mv.visitInsn(Opcodes.DUP2_X2); -} - -/** - * Generates a SWAP instruction. - */ -public void swap(){ - mv.visitInsn(Opcodes.SWAP); -} - -/** - * Generates the instructions to swap the top two stack values. - * - * @param prev type of the top - 1 stack value. - * @param type type of the top stack value. - */ -public void swap(final Type prev, final Type type){ - if(type.getSize() == 1) - { - if(prev.getSize() == 1) - { - swap(); // same as dupX1(), pop(); - } - else - { - dupX2(); - pop(); - } - } - else - { - if(prev.getSize() == 1) - { - dup2X1(); - pop2(); - } - else - { - dup2X2(); - pop2(); - } - } -} - -// ------------------------------------------------------------------------ -// Instructions to do mathematical and logical operations -// ------------------------------------------------------------------------ - -/** - * Generates the instruction to do the specified mathematical or logical - * operation. - * - * @param op a mathematical or logical operation. Must be one of ADD, SUB, - * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR. - * @param type the type of the operand(s) for this operation. - */ -public void math(final int op, final Type type){ - mv.visitInsn(type.getOpcode(op)); -} - -/** - * Generates the instructions to compute the bitwise negation of the top - * stack value. - */ -public void not(){ - mv.visitInsn(Opcodes.ICONST_1); - mv.visitInsn(Opcodes.IXOR); -} - -/** - * Generates the instruction to increment the given local variable. - * - * @param local the local variable to be incremented. - * @param amount the amount by which the local variable must be incremented. - */ -public void iinc(final int local, final int amount){ - mv.visitIincInsn(local, amount); -} - -/** - * Generates the instructions to cast a numerical value from one type to - * another. - * - * @param from the type of the top stack value - * @param to the type into which this value must be cast. - */ -public void cast(final Type from, final Type to){ - if(from != to) - { - if(from == Type.DOUBLE_TYPE) - { - if(to == Type.FLOAT_TYPE) - { - mv.visitInsn(Opcodes.D2F); - } - else if(to == Type.LONG_TYPE) - { - mv.visitInsn(Opcodes.D2L); - } - else - { - mv.visitInsn(Opcodes.D2I); - cast(Type.INT_TYPE, to); - } - } - else if(from == Type.FLOAT_TYPE) - { - if(to == Type.DOUBLE_TYPE) - { - mv.visitInsn(Opcodes.F2D); - } - else if(to == Type.LONG_TYPE) - { - mv.visitInsn(Opcodes.F2L); - } - else - { - mv.visitInsn(Opcodes.F2I); - cast(Type.INT_TYPE, to); - } - } - else if(from == Type.LONG_TYPE) - { - if(to == Type.DOUBLE_TYPE) - { - mv.visitInsn(Opcodes.L2D); - } - else if(to == Type.FLOAT_TYPE) - { - mv.visitInsn(Opcodes.L2F); - } - else - { - mv.visitInsn(Opcodes.L2I); - cast(Type.INT_TYPE, to); - } - } - else - { - if(to == Type.BYTE_TYPE) - { - mv.visitInsn(Opcodes.I2B); - } - else if(to == Type.CHAR_TYPE) - { - mv.visitInsn(Opcodes.I2C); - } - else if(to == Type.DOUBLE_TYPE) - { - mv.visitInsn(Opcodes.I2D); - } - else if(to == Type.FLOAT_TYPE) - { - mv.visitInsn(Opcodes.I2F); - } - else if(to == Type.LONG_TYPE) - { - mv.visitInsn(Opcodes.I2L); - } - else if(to == Type.SHORT_TYPE) - { - mv.visitInsn(Opcodes.I2S); - } - } - } -} - -// ------------------------------------------------------------------------ -// Instructions to do boxing and unboxing operations -// ------------------------------------------------------------------------ - -/** - * Generates the instructions to box the top stack value. This value is - * replaced by its boxed equivalent on top of the stack. - * - * @param type the type of the top stack value. - */ -public void box(final Type type){ - if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) - { - return; - } - if(type == Type.VOID_TYPE) - { - push((String) null); - } - else - { - Type boxed = type; - switch(type.getSort()) - { - case Type.BYTE: - boxed = BYTE_TYPE; - break; - case Type.BOOLEAN: - boxed = BOOLEAN_TYPE; - break; - case Type.SHORT: - boxed = SHORT_TYPE; - break; - case Type.CHAR: - boxed = CHARACTER_TYPE; - break; - case Type.INT: - boxed = INTEGER_TYPE; - break; - case Type.FLOAT: - boxed = FLOAT_TYPE; - break; - case Type.LONG: - boxed = LONG_TYPE; - break; - case Type.DOUBLE: - boxed = DOUBLE_TYPE; - break; - } - newInstance(boxed); - if(type.getSize() == 2) - { - // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o - dupX2(); - dupX2(); - pop(); - } - else - { - // p -> po -> opo -> oop -> o - dupX1(); - swap(); - } - invokeConstructor(boxed, new Method("", - Type.VOID_TYPE, - new Type[]{type})); - } -} - -/** - * Generates the instructions to unbox the top stack value. This value is - * replaced by its unboxed equivalent on top of the stack. - * - * @param type the type of the top stack value. - */ -public void unbox(final Type type){ - Type t = NUMBER_TYPE; - Method sig = null; - switch(type.getSort()) - { - case Type.VOID: - return; - case Type.CHAR: - t = CHARACTER_TYPE; - sig = CHAR_VALUE; - break; - case Type.BOOLEAN: - t = BOOLEAN_TYPE; - sig = BOOLEAN_VALUE; - break; - case Type.DOUBLE: - sig = DOUBLE_VALUE; - break; - case Type.FLOAT: - sig = FLOAT_VALUE; - break; - case Type.LONG: - sig = LONG_VALUE; - break; - case Type.INT: - case Type.SHORT: - case Type.BYTE: - sig = INT_VALUE; - } - if(sig == null) - { - checkCast(type); - } - else - { - checkCast(t); - invokeVirtual(t, sig); - } -} - -// ------------------------------------------------------------------------ -// Instructions to jump to other instructions -// ------------------------------------------------------------------------ - -/** - * Creates a new {@link Label}. - * - * @return a new {@link Label}. - */ -public Label newLabel(){ - return new Label(); -} - -/** - * Marks the current code position with the given label. - * - * @param label a label. - */ -public void mark(final Label label){ - mv.visitLabel(label); -} - -/** - * Marks the current code position with a new label. - * - * @return the label that was created to mark the current code position. - */ -public Label mark(){ - Label label = new Label(); - mv.visitLabel(label); - return label; -} - -/** - * Generates the instructions to jump to a label based on the comparison of - * the top two stack values. - * - * @param type the type of the top two stack values. - * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, - * LE. - * @param label where to jump if the comparison result is true. - */ -public void ifCmp(final Type type, final int mode, final Label label){ - int intOp = -1; - switch(type.getSort()) - { - case Type.LONG: - mv.visitInsn(Opcodes.LCMP); - break; - case Type.DOUBLE: - mv.visitInsn(Opcodes.DCMPG); - break; - case Type.FLOAT: - mv.visitInsn(Opcodes.FCMPG); - break; - case Type.ARRAY: - case Type.OBJECT: - switch(mode) - { - case EQ: - mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label); - return; - case NE: - mv.visitJumpInsn(Opcodes.IF_ACMPNE, label); - return; - } - throw new IllegalArgumentException("Bad comparison for type " - + type); - default: - switch(mode) - { - case EQ: - intOp = Opcodes.IF_ICMPEQ; - break; - case NE: - intOp = Opcodes.IF_ICMPNE; - break; - case GE: - intOp = Opcodes.IF_ICMPGE; - break; - case LT: - intOp = Opcodes.IF_ICMPLT; - break; - case LE: - intOp = Opcodes.IF_ICMPLE; - break; - case GT: - intOp = Opcodes.IF_ICMPGT; - break; - } - mv.visitJumpInsn(intOp, label); - return; - } - int jumpMode = mode; - switch(mode) - { - case GE: - jumpMode = LT; - break; - case LE: - jumpMode = GT; - break; - } - mv.visitJumpInsn(jumpMode, label); -} - -/** - * Generates the instructions to jump to a label based on the comparison of - * the top two integer stack values. - * - * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, - * LE. - * @param label where to jump if the comparison result is true. - */ -public void ifICmp(final int mode, final Label label){ - ifCmp(Type.INT_TYPE, mode, label); -} - -/** - * Generates the instructions to jump to a label based on the comparison of - * the top integer stack value with zero. - * - * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, - * LE. - * @param label where to jump if the comparison result is true. - */ -public void ifZCmp(final int mode, final Label label){ - mv.visitJumpInsn(mode, label); -} - -/** - * Generates the instruction to jump to the given label if the top stack - * value is null. - * - * @param label where to jump if the condition is true. - */ -public void ifNull(final Label label){ - mv.visitJumpInsn(Opcodes.IFNULL, label); -} - -/** - * Generates the instruction to jump to the given label if the top stack - * value is not null. - * - * @param label where to jump if the condition is true. - */ -public void ifNonNull(final Label label){ - mv.visitJumpInsn(Opcodes.IFNONNULL, label); -} - -/** - * Generates the instruction to jump to the given label. - * - * @param label where to jump if the condition is true. - */ -public void goTo(final Label label){ - mv.visitJumpInsn(Opcodes.GOTO, label); -} - -/** - * Generates a RET instruction. - * - * @param local a local variable identifier, as returned by - * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. - */ -public void ret(final int local){ - mv.visitVarInsn(Opcodes.RET, local); -} - -/** - * Generates the instructions for a switch statement. - * - * @param keys the switch case keys. - * @param generator a generator to generate the code for the switch cases. - */ -public void tableSwitch( - final int[] keys, - final TableSwitchGenerator generator){ - float density; - if(keys.length == 0) - { - density = 0; - } - else - { - density = (float) keys.length - / (keys[keys.length - 1] - keys[0] + 1); - } - tableSwitch(keys, generator, density >= 0.5f); -} - -/** - * Generates the instructions for a switch statement. - * - * @param keys the switch case keys. - * @param generator a generator to generate the code for the switch cases. - * @param useTable true to use a TABLESWITCH instruction, or - * false to use a LOOKUPSWITCH instruction. - */ -public void tableSwitch( - final int[] keys, - final TableSwitchGenerator generator, - final boolean useTable){ - for(int i = 1; i < keys.length; ++i) - { - if(keys[i] < keys[i - 1]) - { - throw new IllegalArgumentException("keys must be sorted ascending"); - } - } - Label def = newLabel(); - Label end = newLabel(); - if(keys.length > 0) - { - int len = keys.length; - int min = keys[0]; - int max = keys[len - 1]; - int range = max - min + 1; - if(useTable) - { - Label[] labels = new Label[range]; - Arrays.fill(labels, def); - for(int i = 0; i < len; ++i) - { - labels[keys[i] - min] = newLabel(); - } - mv.visitTableSwitchInsn(min, max, def, labels); - for(int i = 0; i < range; ++i) - { - Label label = labels[i]; - if(label != def) - { - mark(label); - generator.generateCase(i + min, end); - } - } - } - else - { - Label[] labels = new Label[len]; - for(int i = 0; i < len; ++i) - { - labels[i] = newLabel(); - } - mv.visitLookupSwitchInsn(def, keys, labels); - for(int i = 0; i < len; ++i) - { - mark(labels[i]); - generator.generateCase(keys[i], end); - } - } - } - mark(def); - generator.generateDefault(); - mark(end); -} - -/** - * Generates the instruction to return the top stack value to the caller. - */ -public void returnValue(){ - mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); -} - -// ------------------------------------------------------------------------ -// Instructions to load and store fields -// ------------------------------------------------------------------------ - -/** - * Generates a get field or set field instruction. - * - * @param opcode the instruction's opcode. - * @param ownerType the class in which the field is defined. - * @param name the name of the field. - * @param fieldType the type of the field. - */ -private void fieldInsn( - final int opcode, - final Type ownerType, - final String name, - final Type fieldType){ - mv.visitFieldInsn(opcode, - ownerType.getInternalName(), - name, - fieldType.getDescriptor()); -} - -/** - * Generates the instruction to push the value of a static field on the - * stack. - * - * @param owner the class in which the field is defined. - * @param name the name of the field. - * @param type the type of the field. - */ -public void getStatic(final Type owner, final String name, final Type type){ - fieldInsn(Opcodes.GETSTATIC, owner, name, type); -} - -/** - * Generates the instruction to store the top stack value in a static field. - * - * @param owner the class in which the field is defined. - * @param name the name of the field. - * @param type the type of the field. - */ -public void putStatic(final Type owner, final String name, final Type type){ - fieldInsn(Opcodes.PUTSTATIC, owner, name, type); -} - -/** - * Generates the instruction to push the value of a non static field on the - * stack. - * - * @param owner the class in which the field is defined. - * @param name the name of the field. - * @param type the type of the field. - */ -public void getField(final Type owner, final String name, final Type type){ - fieldInsn(Opcodes.GETFIELD, owner, name, type); -} - -/** - * Generates the instruction to store the top stack value in a non static - * field. - * - * @param owner the class in which the field is defined. - * @param name the name of the field. - * @param type the type of the field. - */ -public void putField(final Type owner, final String name, final Type type){ - fieldInsn(Opcodes.PUTFIELD, owner, name, type); -} - -// ------------------------------------------------------------------------ -// Instructions to invoke methods -// ------------------------------------------------------------------------ - -/** - * Generates an invoke method instruction. - * - * @param opcode the instruction's opcode. - * @param type the class in which the method is defined. - * @param method the method to be invoked. - */ -private void invokeInsn( - final int opcode, - final Type type, - final Method method){ - String owner = type.getSort() == Type.ARRAY - ? type.getDescriptor() - : type.getInternalName(); - mv.visitMethodInsn(opcode, - owner, - method.getName(), - method.getDescriptor()); -} - -/** - * Generates the instruction to invoke a normal method. - * - * @param owner the class in which the method is defined. - * @param method the method to be invoked. - */ -public void invokeVirtual(final Type owner, final Method method){ - invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method); -} - -/** - * Generates the instruction to invoke a constructor. - * - * @param type the class in which the constructor is defined. - * @param method the constructor to be invoked. - */ -public void invokeConstructor(final Type type, final Method method){ - invokeInsn(Opcodes.INVOKESPECIAL, type, method); -} - -/** - * Generates the instruction to invoke a static method. - * - * @param owner the class in which the method is defined. - * @param method the method to be invoked. - */ -public void invokeStatic(final Type owner, final Method method){ - invokeInsn(Opcodes.INVOKESTATIC, owner, method); -} - -/** - * Generates the instruction to invoke an interface method. - * - * @param owner the class in which the method is defined. - * @param method the method to be invoked. - */ -public void invokeInterface(final Type owner, final Method method){ - invokeInsn(Opcodes.INVOKEINTERFACE, owner, method); -} - -// ------------------------------------------------------------------------ -// Instructions to create objects and arrays -// ------------------------------------------------------------------------ - -/** - * Generates a type dependent instruction. - * - * @param opcode the instruction's opcode. - * @param type the instruction's operand. - */ -private void typeInsn(final int opcode, final Type type){ - String desc; - if(type.getSort() == Type.ARRAY) - { - desc = type.getDescriptor(); - } - else - { - desc = type.getInternalName(); - } - mv.visitTypeInsn(opcode, desc); -} - -/** - * Generates the instruction to create a new object. - * - * @param type the class of the object to be created. - */ -public void newInstance(final Type type){ - typeInsn(Opcodes.NEW, type); -} - -/** - * Generates the instruction to create a new array. - * - * @param type the type of the array elements. - */ -public void newArray(final Type type){ - int typ; - switch(type.getSort()) - { - case Type.BOOLEAN: - typ = Opcodes.T_BOOLEAN; - break; - case Type.CHAR: - typ = Opcodes.T_CHAR; - break; - case Type.BYTE: - typ = Opcodes.T_BYTE; - break; - case Type.SHORT: - typ = Opcodes.T_SHORT; - break; - case Type.INT: - typ = Opcodes.T_INT; - break; - case Type.FLOAT: - typ = Opcodes.T_FLOAT; - break; - case Type.LONG: - typ = Opcodes.T_LONG; - break; - case Type.DOUBLE: - typ = Opcodes.T_DOUBLE; - break; - default: - typeInsn(Opcodes.ANEWARRAY, type); - return; - } - mv.visitIntInsn(Opcodes.NEWARRAY, typ); -} - -// ------------------------------------------------------------------------ -// Miscelaneous instructions -// ------------------------------------------------------------------------ - -/** - * Generates the instruction to compute the length of an array. - */ -public void arrayLength(){ - mv.visitInsn(Opcodes.ARRAYLENGTH); -} - -/** - * Generates the instruction to throw an exception. - */ -public void throwException(){ - mv.visitInsn(Opcodes.ATHROW); -} - -/** - * Generates the instructions to create and throw an exception. The - * exception class must have a constructor with a single String argument. - * - * @param type the class of the exception to be thrown. - * @param msg the detailed message of the exception. - */ -public void throwException(final Type type, final String msg){ - newInstance(type); - dup(); - push(msg); - invokeConstructor(type, Method.getMethod("void (String)")); - throwException(); -} - -/** - * Generates the instruction to check that the top stack value is of the - * given type. - * - * @param type a class or interface type. - */ -public void checkCast(final Type type){ - if(!type.equals(OBJECT_TYPE)) - { - typeInsn(Opcodes.CHECKCAST, type); - } -} - -/** - * Generates the instruction to test if the top stack value is of the given - * type. - * - * @param type a class or interface type. - */ -public void instanceOf(final Type type){ - typeInsn(Opcodes.INSTANCEOF, type); -} - -/** - * Generates the instruction to get the monitor of the top stack value. - */ -public void monitorEnter(){ - mv.visitInsn(Opcodes.MONITORENTER); -} - -/** - * Generates the instruction to release the monitor of the top stack value. - */ -public void monitorExit(){ - mv.visitInsn(Opcodes.MONITOREXIT); -} - -// ------------------------------------------------------------------------ -// Non instructions -// ------------------------------------------------------------------------ - -/** - * Marks the end of the visited method. - */ -public void endMethod(){ - if((access & Opcodes.ACC_ABSTRACT) == 0) - { - mv.visitMaxs(0, 0); - } - mv.visitEnd(); -} - -/** - * Marks the start of an exception handler. - * - * @param start beginning of the exception handler's scope (inclusive). - * @param end end of the exception handler's scope (exclusive). - * @param exception internal name of the type of exceptions handled by the - * handler. - */ -public void catchException( - final Label start, - final Label end, - final Type exception){ - mv.visitTryCatchBlock(start, end, mark(), exception.getInternalName()); -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm.commons; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import clojure.asm.ClassVisitor; +import clojure.asm.Label; +import clojure.asm.MethodVisitor; +import clojure.asm.Opcodes; +import clojure.asm.Type; + +/** + * A {@link clojure.asm.MethodAdapter} with convenient methods to generate + * code. For example, using this adapter, the class below + *

+ *

+ * public class Example {
+ *     public static void main(String[] args) {
+ *         System.out.println("Hello world!");
+ *     }
+ * }
+ * 
+ *

+ * can be generated as follows: + *

+ *

+ * ClassWriter cw = new ClassWriter(true);
+ * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
+ * 

+ * Method m = Method.getMethod("void <init> ()"); + * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); + * mg.loadThis(); + * mg.invokeConstructor(Type.getType(Object.class), m); + * mg.returnValue(); + * mg.endMethod(); + *

+ * m = Method.getMethod("void main (String[])"); + * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw); + * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); + * mg.push("Hello world!"); + * mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)")); + * mg.returnValue(); + * mg.endMethod(); + *

+ * cw.visitEnd(); + *

+ * + * @author Juozas Baliuka + * @author Chris Nokleberg + * @author Eric Bruneton + */ +public class GeneratorAdapter extends LocalVariablesSorter{ + +private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte"); + +private final static Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean"); + +private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short"); + +private final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character"); + +private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer"); + +private final static Type FLOAT_TYPE = Type.getObjectType("java/lang/Float"); + +private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long"); + +private final static Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double"); + +private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number"); + +private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); + +private final static Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()"); + +private final static Method CHAR_VALUE = Method.getMethod("char charValue()"); + +private final static Method INT_VALUE = Method.getMethod("int intValue()"); + +private final static Method FLOAT_VALUE = Method.getMethod("float floatValue()"); + +private final static Method LONG_VALUE = Method.getMethod("long longValue()"); + +private final static Method DOUBLE_VALUE = Method.getMethod("double doubleValue()"); + +/** + * Constant for the {@link #math math} method. + */ +public final static int ADD = Opcodes.IADD; + +/** + * Constant for the {@link #math math} method. + */ +public final static int SUB = Opcodes.ISUB; + +/** + * Constant for the {@link #math math} method. + */ +public final static int MUL = Opcodes.IMUL; + +/** + * Constant for the {@link #math math} method. + */ +public final static int DIV = Opcodes.IDIV; + +/** + * Constant for the {@link #math math} method. + */ +public final static int REM = Opcodes.IREM; + +/** + * Constant for the {@link #math math} method. + */ +public final static int NEG = Opcodes.INEG; + +/** + * Constant for the {@link #math math} method. + */ +public final static int SHL = Opcodes.ISHL; + +/** + * Constant for the {@link #math math} method. + */ +public final static int SHR = Opcodes.ISHR; + +/** + * Constant for the {@link #math math} method. + */ +public final static int USHR = Opcodes.IUSHR; + +/** + * Constant for the {@link #math math} method. + */ +public final static int AND = Opcodes.IAND; + +/** + * Constant for the {@link #math math} method. + */ +public final static int OR = Opcodes.IOR; + +/** + * Constant for the {@link #math math} method. + */ +public final static int XOR = Opcodes.IXOR; + +/** + * Constant for the {@link #ifCmp ifCmp} method. + */ +public final static int EQ = Opcodes.IFEQ; + +/** + * Constant for the {@link #ifCmp ifCmp} method. + */ +public final static int NE = Opcodes.IFNE; + +/** + * Constant for the {@link #ifCmp ifCmp} method. + */ +public final static int LT = Opcodes.IFLT; + +/** + * Constant for the {@link #ifCmp ifCmp} method. + */ +public final static int GE = Opcodes.IFGE; + +/** + * Constant for the {@link #ifCmp ifCmp} method. + */ +public final static int GT = Opcodes.IFGT; + +/** + * Constant for the {@link #ifCmp ifCmp} method. + */ +public final static int LE = Opcodes.IFLE; + +/** + * Access flags of the method visited by this adapter. + */ +private final int access; + +/** + * Return type of the method visited by this adapter. + */ +private final Type returnType; + +/** + * Argument types of the method visited by this adapter. + */ +private final Type[] argumentTypes; + +/** + * Types of the local variables of the method visited by this adapter. + */ +private final List localTypes = new ArrayList(); + +/** + * Creates a new {@link GeneratorAdapter}. + * + * @param mv the method visitor to which this adapter delegates calls. + * @param access the method's access flags (see {@link Opcodes}). + * @param name the method's name. + * @param desc the method's descriptor (see {@link Type Type}). + */ +public GeneratorAdapter( + final MethodVisitor mv, + final int access, + final String name, + final String desc){ + super(access, desc, mv); + this.access = access; + this.returnType = Type.getReturnType(desc); + this.argumentTypes = Type.getArgumentTypes(desc); +} + +/** + * Creates a new {@link GeneratorAdapter}. + * + * @param access access flags of the adapted method. + * @param method the adapted method. + * @param mv the method visitor to which this adapter delegates calls. + */ +public GeneratorAdapter( + final int access, + final Method method, + final MethodVisitor mv){ + super(access, method.getDescriptor(), mv); + this.access = access; + this.returnType = method.getReturnType(); + this.argumentTypes = method.getArgumentTypes(); +} + +/** + * Creates a new {@link GeneratorAdapter}. + * + * @param access access flags of the adapted method. + * @param method the adapted method. + * @param signature the signature of the adapted method (may be + * null). + * @param exceptions the exceptions thrown by the adapted method (may be + * null). + * @param cv the class visitor to which this adapter delegates calls. + */ +public GeneratorAdapter( + final int access, + final Method method, + final String signature, + final Type[] exceptions, + final ClassVisitor cv){ + this(access, method, cv.visitMethod(access, + method.getName(), + method.getDescriptor(), + signature, + getInternalNames(exceptions))); +} + +/** + * Returns the internal names of the given types. + * + * @param types a set of types. + * @return the internal names of the given types. + */ +private static String[] getInternalNames(final Type[] types){ + if(types == null) + { + return null; + } + String[] names = new String[types.length]; + for(int i = 0; i < names.length; ++i) + { + names[i] = types[i].getInternalName(); + } + return names; +} + +// ------------------------------------------------------------------------ +// Instructions to push constants on the stack +// ------------------------------------------------------------------------ + +/** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ +public void push(final boolean value){ + push(value ? 1 : 0); +} + +/** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ +public void push(final int value){ + if(value >= -1 && value <= 5) + { + mv.visitInsn(Opcodes.ICONST_0 + value); + } + else if(value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) + { + mv.visitIntInsn(Opcodes.BIPUSH, value); + } + else if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) + { + mv.visitIntInsn(Opcodes.SIPUSH, value); + } + else + { + mv.visitLdcInsn(new Integer(value)); + } +} + +/** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ +public void push(final long value){ + if(value == 0L || value == 1L) + { + mv.visitInsn(Opcodes.LCONST_0 + (int) value); + } + else + { + mv.visitLdcInsn(new Long(value)); + } +} + +/** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ +public void push(final float value){ + int bits = Float.floatToIntBits(value); + if(bits == 0L || bits == 0x3f800000 || bits == 0x40000000) + { // 0..2 + mv.visitInsn(Opcodes.FCONST_0 + (int) value); + } + else + { + mv.visitLdcInsn(new Float(value)); + } +} + +/** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ +public void push(final double value){ + long bits = Double.doubleToLongBits(value); + if(bits == 0L || bits == 0x3ff0000000000000L) + { // +0.0d and 1.0d + mv.visitInsn(Opcodes.DCONST_0 + (int) value); + } + else + { + mv.visitLdcInsn(new Double(value)); + } +} + +/** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. May be null. + */ +public void push(final String value){ + if(value == null) + { + mv.visitInsn(Opcodes.ACONST_NULL); + } + else + { + mv.visitLdcInsn(value); + } +} + +/** + * Generates the instruction to push the given value on the stack. + * + * @param value the value to be pushed on the stack. + */ +public void push(final Type value){ + if(value == null) + { + mv.visitInsn(Opcodes.ACONST_NULL); + } + else + { + mv.visitLdcInsn(value); + } +} + +// ------------------------------------------------------------------------ +// Instructions to load and store method arguments +// ------------------------------------------------------------------------ + +/** + * Returns the index of the given method argument in the frame's local + * variables array. + * + * @param arg the index of a method argument. + * @return the index of the given method argument in the frame's local + * variables array. + */ +private int getArgIndex(final int arg){ + int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0; + for(int i = 0; i < arg; i++) + { + index += argumentTypes[i].getSize(); + } + return index; +} + +/** + * Generates the instruction to push a local variable on the stack. + * + * @param type the type of the local variable to be loaded. + * @param index an index in the frame's local variables array. + */ +private void loadInsn(final Type type, final int index){ + mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index); +} + +/** + * Generates the instruction to store the top stack value in a local + * variable. + * + * @param type the type of the local variable to be stored. + * @param index an index in the frame's local variables array. + */ +private void storeInsn(final Type type, final int index){ + mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index); +} + +/** + * Generates the instruction to load 'this' on the stack. + */ +public void loadThis(){ + if((access & Opcodes.ACC_STATIC) != 0) + { + throw new IllegalStateException("no 'this' pointer within static method"); + } + mv.visitVarInsn(Opcodes.ALOAD, 0); +} + +/** + * Generates the instruction to load the given method argument on the stack. + * + * @param arg the index of a method argument. + */ +public void loadArg(final int arg){ + loadInsn(argumentTypes[arg], getArgIndex(arg)); +} + +/** + * Generates the instructions to load the given method arguments on the + * stack. + * + * @param arg the index of the first method argument to be loaded. + * @param count the number of method arguments to be loaded. + */ +public void loadArgs(final int arg, final int count){ + int index = getArgIndex(arg); + for(int i = 0; i < count; ++i) + { + Type t = argumentTypes[arg + i]; + loadInsn(t, index); + index += t.getSize(); + } +} + +/** + * Generates the instructions to load all the method arguments on the stack. + */ +public void loadArgs(){ + loadArgs(0, argumentTypes.length); +} + +/** + * Generates the instructions to load all the method arguments on the stack, + * as a single object array. + */ +public void loadArgArray(){ + push(argumentTypes.length); + newArray(OBJECT_TYPE); + for(int i = 0; i < argumentTypes.length; i++) + { + dup(); + push(i); + loadArg(i); + box(argumentTypes[i]); + arrayStore(OBJECT_TYPE); + } +} + +/** + * Generates the instruction to store the top stack value in the given + * method argument. + * + * @param arg the index of a method argument. + */ +public void storeArg(final int arg){ + storeInsn(argumentTypes[arg], getArgIndex(arg)); +} + +// ------------------------------------------------------------------------ +// Instructions to load and store local variables +// ------------------------------------------------------------------------ + +/** + * Returns the type of the given local variable. + * + * @param local a local variable identifier, as returned by + * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. + * @return the type of the given local variable. + */ +public Type getLocalType(final int local){ + return (Type) localTypes.get(local - firstLocal); +} + +protected void setLocalType(final int local, final Type type){ + int index = local - firstLocal; + while(localTypes.size() < index + 1) + { + localTypes.add(null); + } + localTypes.set(index, type); +} + +/** + * Generates the instruction to load the given local variable on the stack. + * + * @param local a local variable identifier, as returned by + * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. + */ +public void loadLocal(final int local){ + loadInsn(getLocalType(local), local); +} + +/** + * Generates the instruction to load the given local variable on the stack. + * + * @param local a local variable identifier, as returned by + * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. + * @param type the type of this local variable. + */ +public void loadLocal(final int local, final Type type){ + setLocalType(local, type); + loadInsn(type, local); +} + +/** + * Generates the instruction to store the top stack value in the given local + * variable. + * + * @param local a local variable identifier, as returned by + * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. + */ +public void storeLocal(final int local){ + storeInsn(getLocalType(local), local); +} + +/** + * Generates the instruction to store the top stack value in the given local + * variable. + * + * @param local a local variable identifier, as returned by + * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. + * @param type the type of this local variable. + */ +public void storeLocal(final int local, final Type type){ + setLocalType(local, type); + storeInsn(type, local); +} + +/** + * Generates the instruction to load an element from an array. + * + * @param type the type of the array element to be loaded. + */ +public void arrayLoad(final Type type){ + mv.visitInsn(type.getOpcode(Opcodes.IALOAD)); +} + +/** + * Generates the instruction to store an element in an array. + * + * @param type the type of the array element to be stored. + */ +public void arrayStore(final Type type){ + mv.visitInsn(type.getOpcode(Opcodes.IASTORE)); +} + +// ------------------------------------------------------------------------ +// Instructions to manage the stack +// ------------------------------------------------------------------------ + +/** + * Generates a POP instruction. + */ +public void pop(){ + mv.visitInsn(Opcodes.POP); +} + +/** + * Generates a POP2 instruction. + */ +public void pop2(){ + mv.visitInsn(Opcodes.POP2); +} + +/** + * Generates a DUP instruction. + */ +public void dup(){ + mv.visitInsn(Opcodes.DUP); +} + +/** + * Generates a DUP2 instruction. + */ +public void dup2(){ + mv.visitInsn(Opcodes.DUP2); +} + +/** + * Generates a DUP_X1 instruction. + */ +public void dupX1(){ + mv.visitInsn(Opcodes.DUP_X1); +} + +/** + * Generates a DUP_X2 instruction. + */ +public void dupX2(){ + mv.visitInsn(Opcodes.DUP_X2); +} + +/** + * Generates a DUP2_X1 instruction. + */ +public void dup2X1(){ + mv.visitInsn(Opcodes.DUP2_X1); +} + +/** + * Generates a DUP2_X2 instruction. + */ +public void dup2X2(){ + mv.visitInsn(Opcodes.DUP2_X2); +} + +/** + * Generates a SWAP instruction. + */ +public void swap(){ + mv.visitInsn(Opcodes.SWAP); +} + +/** + * Generates the instructions to swap the top two stack values. + * + * @param prev type of the top - 1 stack value. + * @param type type of the top stack value. + */ +public void swap(final Type prev, final Type type){ + if(type.getSize() == 1) + { + if(prev.getSize() == 1) + { + swap(); // same as dupX1(), pop(); + } + else + { + dupX2(); + pop(); + } + } + else + { + if(prev.getSize() == 1) + { + dup2X1(); + pop2(); + } + else + { + dup2X2(); + pop2(); + } + } +} + +// ------------------------------------------------------------------------ +// Instructions to do mathematical and logical operations +// ------------------------------------------------------------------------ + +/** + * Generates the instruction to do the specified mathematical or logical + * operation. + * + * @param op a mathematical or logical operation. Must be one of ADD, SUB, + * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR. + * @param type the type of the operand(s) for this operation. + */ +public void math(final int op, final Type type){ + mv.visitInsn(type.getOpcode(op)); +} + +/** + * Generates the instructions to compute the bitwise negation of the top + * stack value. + */ +public void not(){ + mv.visitInsn(Opcodes.ICONST_1); + mv.visitInsn(Opcodes.IXOR); +} + +/** + * Generates the instruction to increment the given local variable. + * + * @param local the local variable to be incremented. + * @param amount the amount by which the local variable must be incremented. + */ +public void iinc(final int local, final int amount){ + mv.visitIincInsn(local, amount); +} + +/** + * Generates the instructions to cast a numerical value from one type to + * another. + * + * @param from the type of the top stack value + * @param to the type into which this value must be cast. + */ +public void cast(final Type from, final Type to){ + if(from != to) + { + if(from == Type.DOUBLE_TYPE) + { + if(to == Type.FLOAT_TYPE) + { + mv.visitInsn(Opcodes.D2F); + } + else if(to == Type.LONG_TYPE) + { + mv.visitInsn(Opcodes.D2L); + } + else + { + mv.visitInsn(Opcodes.D2I); + cast(Type.INT_TYPE, to); + } + } + else if(from == Type.FLOAT_TYPE) + { + if(to == Type.DOUBLE_TYPE) + { + mv.visitInsn(Opcodes.F2D); + } + else if(to == Type.LONG_TYPE) + { + mv.visitInsn(Opcodes.F2L); + } + else + { + mv.visitInsn(Opcodes.F2I); + cast(Type.INT_TYPE, to); + } + } + else if(from == Type.LONG_TYPE) + { + if(to == Type.DOUBLE_TYPE) + { + mv.visitInsn(Opcodes.L2D); + } + else if(to == Type.FLOAT_TYPE) + { + mv.visitInsn(Opcodes.L2F); + } + else + { + mv.visitInsn(Opcodes.L2I); + cast(Type.INT_TYPE, to); + } + } + else + { + if(to == Type.BYTE_TYPE) + { + mv.visitInsn(Opcodes.I2B); + } + else if(to == Type.CHAR_TYPE) + { + mv.visitInsn(Opcodes.I2C); + } + else if(to == Type.DOUBLE_TYPE) + { + mv.visitInsn(Opcodes.I2D); + } + else if(to == Type.FLOAT_TYPE) + { + mv.visitInsn(Opcodes.I2F); + } + else if(to == Type.LONG_TYPE) + { + mv.visitInsn(Opcodes.I2L); + } + else if(to == Type.SHORT_TYPE) + { + mv.visitInsn(Opcodes.I2S); + } + } + } +} + +// ------------------------------------------------------------------------ +// Instructions to do boxing and unboxing operations +// ------------------------------------------------------------------------ + +/** + * Generates the instructions to box the top stack value. This value is + * replaced by its boxed equivalent on top of the stack. + * + * @param type the type of the top stack value. + */ +public void box(final Type type){ + if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) + { + return; + } + if(type == Type.VOID_TYPE) + { + push((String) null); + } + else + { + Type boxed = type; + switch(type.getSort()) + { + case Type.BYTE: + boxed = BYTE_TYPE; + break; + case Type.BOOLEAN: + boxed = BOOLEAN_TYPE; + break; + case Type.SHORT: + boxed = SHORT_TYPE; + break; + case Type.CHAR: + boxed = CHARACTER_TYPE; + break; + case Type.INT: + boxed = INTEGER_TYPE; + break; + case Type.FLOAT: + boxed = FLOAT_TYPE; + break; + case Type.LONG: + boxed = LONG_TYPE; + break; + case Type.DOUBLE: + boxed = DOUBLE_TYPE; + break; + } + newInstance(boxed); + if(type.getSize() == 2) + { + // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o + dupX2(); + dupX2(); + pop(); + } + else + { + // p -> po -> opo -> oop -> o + dupX1(); + swap(); + } + invokeConstructor(boxed, new Method("", + Type.VOID_TYPE, + new Type[]{type})); + } +} + +/** + * Generates the instructions to unbox the top stack value. This value is + * replaced by its unboxed equivalent on top of the stack. + * + * @param type the type of the top stack value. + */ +public void unbox(final Type type){ + Type t = NUMBER_TYPE; + Method sig = null; + switch(type.getSort()) + { + case Type.VOID: + return; + case Type.CHAR: + t = CHARACTER_TYPE; + sig = CHAR_VALUE; + break; + case Type.BOOLEAN: + t = BOOLEAN_TYPE; + sig = BOOLEAN_VALUE; + break; + case Type.DOUBLE: + sig = DOUBLE_VALUE; + break; + case Type.FLOAT: + sig = FLOAT_VALUE; + break; + case Type.LONG: + sig = LONG_VALUE; + break; + case Type.INT: + case Type.SHORT: + case Type.BYTE: + sig = INT_VALUE; + } + if(sig == null) + { + checkCast(type); + } + else + { + checkCast(t); + invokeVirtual(t, sig); + } +} + +// ------------------------------------------------------------------------ +// Instructions to jump to other instructions +// ------------------------------------------------------------------------ + +/** + * Creates a new {@link Label}. + * + * @return a new {@link Label}. + */ +public Label newLabel(){ + return new Label(); +} + +/** + * Marks the current code position with the given label. + * + * @param label a label. + */ +public void mark(final Label label){ + mv.visitLabel(label); +} + +/** + * Marks the current code position with a new label. + * + * @return the label that was created to mark the current code position. + */ +public Label mark(){ + Label label = new Label(); + mv.visitLabel(label); + return label; +} + +/** + * Generates the instructions to jump to a label based on the comparison of + * the top two stack values. + * + * @param type the type of the top two stack values. + * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, + * LE. + * @param label where to jump if the comparison result is true. + */ +public void ifCmp(final Type type, final int mode, final Label label){ + int intOp = -1; + switch(type.getSort()) + { + case Type.LONG: + mv.visitInsn(Opcodes.LCMP); + break; + case Type.DOUBLE: + mv.visitInsn(Opcodes.DCMPG); + break; + case Type.FLOAT: + mv.visitInsn(Opcodes.FCMPG); + break; + case Type.ARRAY: + case Type.OBJECT: + switch(mode) + { + case EQ: + mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label); + return; + case NE: + mv.visitJumpInsn(Opcodes.IF_ACMPNE, label); + return; + } + throw new IllegalArgumentException("Bad comparison for type " + + type); + default: + switch(mode) + { + case EQ: + intOp = Opcodes.IF_ICMPEQ; + break; + case NE: + intOp = Opcodes.IF_ICMPNE; + break; + case GE: + intOp = Opcodes.IF_ICMPGE; + break; + case LT: + intOp = Opcodes.IF_ICMPLT; + break; + case LE: + intOp = Opcodes.IF_ICMPLE; + break; + case GT: + intOp = Opcodes.IF_ICMPGT; + break; + } + mv.visitJumpInsn(intOp, label); + return; + } + int jumpMode = mode; + switch(mode) + { + case GE: + jumpMode = LT; + break; + case LE: + jumpMode = GT; + break; + } + mv.visitJumpInsn(jumpMode, label); +} + +/** + * Generates the instructions to jump to a label based on the comparison of + * the top two integer stack values. + * + * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, + * LE. + * @param label where to jump if the comparison result is true. + */ +public void ifICmp(final int mode, final Label label){ + ifCmp(Type.INT_TYPE, mode, label); +} + +/** + * Generates the instructions to jump to a label based on the comparison of + * the top integer stack value with zero. + * + * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, + * LE. + * @param label where to jump if the comparison result is true. + */ +public void ifZCmp(final int mode, final Label label){ + mv.visitJumpInsn(mode, label); +} + +/** + * Generates the instruction to jump to the given label if the top stack + * value is null. + * + * @param label where to jump if the condition is true. + */ +public void ifNull(final Label label){ + mv.visitJumpInsn(Opcodes.IFNULL, label); +} + +/** + * Generates the instruction to jump to the given label if the top stack + * value is not null. + * + * @param label where to jump if the condition is true. + */ +public void ifNonNull(final Label label){ + mv.visitJumpInsn(Opcodes.IFNONNULL, label); +} + +/** + * Generates the instruction to jump to the given label. + * + * @param label where to jump if the condition is true. + */ +public void goTo(final Label label){ + mv.visitJumpInsn(Opcodes.GOTO, label); +} + +/** + * Generates a RET instruction. + * + * @param local a local variable identifier, as returned by + * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. + */ +public void ret(final int local){ + mv.visitVarInsn(Opcodes.RET, local); +} + +/** + * Generates the instructions for a switch statement. + * + * @param keys the switch case keys. + * @param generator a generator to generate the code for the switch cases. + */ +public void tableSwitch( + final int[] keys, + final TableSwitchGenerator generator){ + float density; + if(keys.length == 0) + { + density = 0; + } + else + { + density = (float) keys.length + / (keys[keys.length - 1] - keys[0] + 1); + } + tableSwitch(keys, generator, density >= 0.5f); +} + +/** + * Generates the instructions for a switch statement. + * + * @param keys the switch case keys. + * @param generator a generator to generate the code for the switch cases. + * @param useTable true to use a TABLESWITCH instruction, or + * false to use a LOOKUPSWITCH instruction. + */ +public void tableSwitch( + final int[] keys, + final TableSwitchGenerator generator, + final boolean useTable){ + for(int i = 1; i < keys.length; ++i) + { + if(keys[i] < keys[i - 1]) + { + throw new IllegalArgumentException("keys must be sorted ascending"); + } + } + Label def = newLabel(); + Label end = newLabel(); + if(keys.length > 0) + { + int len = keys.length; + int min = keys[0]; + int max = keys[len - 1]; + int range = max - min + 1; + if(useTable) + { + Label[] labels = new Label[range]; + Arrays.fill(labels, def); + for(int i = 0; i < len; ++i) + { + labels[keys[i] - min] = newLabel(); + } + mv.visitTableSwitchInsn(min, max, def, labels); + for(int i = 0; i < range; ++i) + { + Label label = labels[i]; + if(label != def) + { + mark(label); + generator.generateCase(i + min, end); + } + } + } + else + { + Label[] labels = new Label[len]; + for(int i = 0; i < len; ++i) + { + labels[i] = newLabel(); + } + mv.visitLookupSwitchInsn(def, keys, labels); + for(int i = 0; i < len; ++i) + { + mark(labels[i]); + generator.generateCase(keys[i], end); + } + } + } + mark(def); + generator.generateDefault(); + mark(end); +} + +/** + * Generates the instruction to return the top stack value to the caller. + */ +public void returnValue(){ + mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); +} + +// ------------------------------------------------------------------------ +// Instructions to load and store fields +// ------------------------------------------------------------------------ + +/** + * Generates a get field or set field instruction. + * + * @param opcode the instruction's opcode. + * @param ownerType the class in which the field is defined. + * @param name the name of the field. + * @param fieldType the type of the field. + */ +private void fieldInsn( + final int opcode, + final Type ownerType, + final String name, + final Type fieldType){ + mv.visitFieldInsn(opcode, + ownerType.getInternalName(), + name, + fieldType.getDescriptor()); +} + +/** + * Generates the instruction to push the value of a static field on the + * stack. + * + * @param owner the class in which the field is defined. + * @param name the name of the field. + * @param type the type of the field. + */ +public void getStatic(final Type owner, final String name, final Type type){ + fieldInsn(Opcodes.GETSTATIC, owner, name, type); +} + +/** + * Generates the instruction to store the top stack value in a static field. + * + * @param owner the class in which the field is defined. + * @param name the name of the field. + * @param type the type of the field. + */ +public void putStatic(final Type owner, final String name, final Type type){ + fieldInsn(Opcodes.PUTSTATIC, owner, name, type); +} + +/** + * Generates the instruction to push the value of a non static field on the + * stack. + * + * @param owner the class in which the field is defined. + * @param name the name of the field. + * @param type the type of the field. + */ +public void getField(final Type owner, final String name, final Type type){ + fieldInsn(Opcodes.GETFIELD, owner, name, type); +} + +/** + * Generates the instruction to store the top stack value in a non static + * field. + * + * @param owner the class in which the field is defined. + * @param name the name of the field. + * @param type the type of the field. + */ +public void putField(final Type owner, final String name, final Type type){ + fieldInsn(Opcodes.PUTFIELD, owner, name, type); +} + +// ------------------------------------------------------------------------ +// Instructions to invoke methods +// ------------------------------------------------------------------------ + +/** + * Generates an invoke method instruction. + * + * @param opcode the instruction's opcode. + * @param type the class in which the method is defined. + * @param method the method to be invoked. + */ +private void invokeInsn( + final int opcode, + final Type type, + final Method method){ + String owner = type.getSort() == Type.ARRAY + ? type.getDescriptor() + : type.getInternalName(); + mv.visitMethodInsn(opcode, + owner, + method.getName(), + method.getDescriptor()); +} + +/** + * Generates the instruction to invoke a normal method. + * + * @param owner the class in which the method is defined. + * @param method the method to be invoked. + */ +public void invokeVirtual(final Type owner, final Method method){ + invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method); +} + +/** + * Generates the instruction to invoke a constructor. + * + * @param type the class in which the constructor is defined. + * @param method the constructor to be invoked. + */ +public void invokeConstructor(final Type type, final Method method){ + invokeInsn(Opcodes.INVOKESPECIAL, type, method); +} + +/** + * Generates the instruction to invoke a static method. + * + * @param owner the class in which the method is defined. + * @param method the method to be invoked. + */ +public void invokeStatic(final Type owner, final Method method){ + invokeInsn(Opcodes.INVOKESTATIC, owner, method); +} + +/** + * Generates the instruction to invoke an interface method. + * + * @param owner the class in which the method is defined. + * @param method the method to be invoked. + */ +public void invokeInterface(final Type owner, final Method method){ + invokeInsn(Opcodes.INVOKEINTERFACE, owner, method); +} + +// ------------------------------------------------------------------------ +// Instructions to create objects and arrays +// ------------------------------------------------------------------------ + +/** + * Generates a type dependent instruction. + * + * @param opcode the instruction's opcode. + * @param type the instruction's operand. + */ +private void typeInsn(final int opcode, final Type type){ + String desc; + if(type.getSort() == Type.ARRAY) + { + desc = type.getDescriptor(); + } + else + { + desc = type.getInternalName(); + } + mv.visitTypeInsn(opcode, desc); +} + +/** + * Generates the instruction to create a new object. + * + * @param type the class of the object to be created. + */ +public void newInstance(final Type type){ + typeInsn(Opcodes.NEW, type); +} + +/** + * Generates the instruction to create a new array. + * + * @param type the type of the array elements. + */ +public void newArray(final Type type){ + int typ; + switch(type.getSort()) + { + case Type.BOOLEAN: + typ = Opcodes.T_BOOLEAN; + break; + case Type.CHAR: + typ = Opcodes.T_CHAR; + break; + case Type.BYTE: + typ = Opcodes.T_BYTE; + break; + case Type.SHORT: + typ = Opcodes.T_SHORT; + break; + case Type.INT: + typ = Opcodes.T_INT; + break; + case Type.FLOAT: + typ = Opcodes.T_FLOAT; + break; + case Type.LONG: + typ = Opcodes.T_LONG; + break; + case Type.DOUBLE: + typ = Opcodes.T_DOUBLE; + break; + default: + typeInsn(Opcodes.ANEWARRAY, type); + return; + } + mv.visitIntInsn(Opcodes.NEWARRAY, typ); +} + +// ------------------------------------------------------------------------ +// Miscelaneous instructions +// ------------------------------------------------------------------------ + +/** + * Generates the instruction to compute the length of an array. + */ +public void arrayLength(){ + mv.visitInsn(Opcodes.ARRAYLENGTH); +} + +/** + * Generates the instruction to throw an exception. + */ +public void throwException(){ + mv.visitInsn(Opcodes.ATHROW); +} + +/** + * Generates the instructions to create and throw an exception. The + * exception class must have a constructor with a single String argument. + * + * @param type the class of the exception to be thrown. + * @param msg the detailed message of the exception. + */ +public void throwException(final Type type, final String msg){ + newInstance(type); + dup(); + push(msg); + invokeConstructor(type, Method.getMethod("void (String)")); + throwException(); +} + +/** + * Generates the instruction to check that the top stack value is of the + * given type. + * + * @param type a class or interface type. + */ +public void checkCast(final Type type){ + if(!type.equals(OBJECT_TYPE)) + { + typeInsn(Opcodes.CHECKCAST, type); + } +} + +/** + * Generates the instruction to test if the top stack value is of the given + * type. + * + * @param type a class or interface type. + */ +public void instanceOf(final Type type){ + typeInsn(Opcodes.INSTANCEOF, type); +} + +/** + * Generates the instruction to get the monitor of the top stack value. + */ +public void monitorEnter(){ + mv.visitInsn(Opcodes.MONITORENTER); +} + +/** + * Generates the instruction to release the monitor of the top stack value. + */ +public void monitorExit(){ + mv.visitInsn(Opcodes.MONITOREXIT); +} + +// ------------------------------------------------------------------------ +// Non instructions +// ------------------------------------------------------------------------ + +/** + * Marks the end of the visited method. + */ +public void endMethod(){ + if((access & Opcodes.ACC_ABSTRACT) == 0) + { + mv.visitMaxs(0, 0); + } + mv.visitEnd(); +} + +/** + * Marks the start of an exception handler. + * + * @param start beginning of the exception handler's scope (inclusive). + * @param end end of the exception handler's scope (exclusive). + * @param exception internal name of the type of exceptions handled by the + * handler. + */ +public void catchException( + final Label start, + final Label end, + final Type exception){ + mv.visitTryCatchBlock(start, end, mark(), exception.getInternalName()); +} +} diff --git a/src/jvm/clojure/asm/commons/LocalVariablesSorter.java b/src/jvm/clojure/asm/commons/LocalVariablesSorter.java index 13d4c74..15777aa 100644 --- a/src/jvm/clojure/asm/commons/LocalVariablesSorter.java +++ b/src/jvm/clojure/asm/commons/LocalVariablesSorter.java @@ -1,330 +1,330 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm.commons; - -import clojure.asm.Label; -import clojure.asm.MethodAdapter; -import clojure.asm.MethodVisitor; -import clojure.asm.Opcodes; -import clojure.asm.Type; - -/** - * A {@link MethodAdapter} that renumbers local variables in their order of - * appearance. This adapter allows one to easily add new local variables to a - * method. It may be used by inheriting from this class, but the preferred way - * of using it is via delegation: the next visitor in the chain can indeed add - * new locals when needed by calling {@link #newLocal} on this adapter (this - * requires a reference back to this {@link LocalVariablesSorter}). - * - * @author Chris Nokleberg - * @author Eugene Kuleshov - * @author Eric Bruneton - */ -public class LocalVariablesSorter extends MethodAdapter{ - -private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); - -/** - * Mapping from old to new local variable indexes. A local variable at index - * i of size 1 is remapped to 'mapping[2*i]', while a local variable at - * index i of size 2 is remapped to 'mapping[2*i+1]'. - */ -private int[] mapping = new int[40]; - -/** - * Array used to store stack map local variable types after remapping. - */ -private Object[] newLocals = new Object[20]; - -/** - * Index of the first local variable, after formal parameters. - */ -protected final int firstLocal; - -/** - * Index of the next local variable to be created by {@link #newLocal}. - */ -protected int nextLocal; - -/** - * Indicates if at least one local variable has moved due to remapping. - */ -private boolean changed; - -/** - * Creates a new {@link LocalVariablesSorter}. - * - * @param access access flags of the adapted method. - * @param desc the method's descriptor (see {@link Type Type}). - * @param mv the method visitor to which this adapter delegates calls. - */ -public LocalVariablesSorter( - final int access, - final String desc, - final MethodVisitor mv){ - super(mv); - Type[] args = Type.getArgumentTypes(desc); - nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1; - for(int i = 0; i < args.length; i++) - { - nextLocal += args[i].getSize(); - } - firstLocal = nextLocal; -} - -public void visitVarInsn(final int opcode, final int var){ - Type type; - switch(opcode) - { - case Opcodes.LLOAD: - case Opcodes.LSTORE: - type = Type.LONG_TYPE; - break; - - case Opcodes.DLOAD: - case Opcodes.DSTORE: - type = Type.DOUBLE_TYPE; - break; - - case Opcodes.FLOAD: - case Opcodes.FSTORE: - type = Type.FLOAT_TYPE; - break; - - case Opcodes.ILOAD: - case Opcodes.ISTORE: - type = Type.INT_TYPE; - break; - - case Opcodes.ALOAD: - case Opcodes.ASTORE: - type = OBJECT_TYPE; - break; - - // case RET: - default: - type = Type.VOID_TYPE; - } - mv.visitVarInsn(opcode, remap(var, type)); -} - -public void visitIincInsn(final int var, final int increment){ - mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); -} - -public void visitMaxs(final int maxStack, final int maxLocals){ - mv.visitMaxs(maxStack, nextLocal); -} - -public void visitLocalVariable( - final String name, - final String desc, - final String signature, - final Label start, - final Label end, - final int index){ - int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1; - int newIndex = remap(index, size); - mv.visitLocalVariable(name, desc, signature, start, end, newIndex); -} - -public void visitFrame( - final int type, - final int nLocal, - final Object[] local, - final int nStack, - final Object[] stack){ - if(type != Opcodes.F_NEW) - { // uncompressed frame - throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag"); - } - - if(!changed) - { // optimization for the case where mapping = identity - mv.visitFrame(type, nLocal, local, nStack, stack); - return; - } - - // creates a copy of newLocals - Object[] oldLocals = new Object[newLocals.length]; - System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); - - // copies types from 'local' to 'newLocals' - // 'newLocals' already contains the variables added with 'newLocal' - - int index = 0; // old local variable index - int number = 0; // old local variable number - for(; number < nLocal; ++number) - { - Object t = local[number]; - int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; - if(t != Opcodes.TOP) - { - setFrameLocal(remap(index, size), t); - } - index += size; - } - - // removes TOP after long and double types as well as trailing TOPs - - index = 0; - number = 0; - for(int i = 0; index < newLocals.length; ++i) - { - Object t = newLocals[index++]; - if(t != null && t != Opcodes.TOP) - { - newLocals[i] = t; - number = i + 1; - if(t == Opcodes.LONG || t == Opcodes.DOUBLE) - { - index += 1; - } - } - else - { - newLocals[i] = Opcodes.TOP; - } - } - - // visits remapped frame - mv.visitFrame(type, number, newLocals, nStack, stack); - - // restores original value of 'newLocals' - newLocals = oldLocals; -} - -// ------------- - -/** - * Creates a new local variable of the given type. - * - * @param type the type of the local variable to be created. - * @return the identifier of the newly created local variable. - */ -public int newLocal(final Type type){ - Object t; - switch(type.getSort()) - { - case Type.BOOLEAN: - case Type.CHAR: - case Type.BYTE: - case Type.SHORT: - case Type.INT: - t = Opcodes.INTEGER; - break; - case Type.FLOAT: - t = Opcodes.FLOAT; - break; - case Type.LONG: - t = Opcodes.LONG; - break; - case Type.DOUBLE: - t = Opcodes.DOUBLE; - break; - case Type.ARRAY: - t = type.getDescriptor(); - break; - // case Type.OBJECT: - default: - t = type.getInternalName(); - break; - } - int local = nextLocal; - setLocalType(local, type); - setFrameLocal(local, t); - nextLocal += type.getSize(); - return local; -} - -/** - * Sets the current type of the given local variable. The default - * implementation of this method does nothing. - * - * @param local a local variable identifier, as returned by {@link #newLocal - * newLocal()}. - * @param type the type of the value being stored in the local variable - */ -protected void setLocalType(final int local, final Type type){ -} - -private void setFrameLocal(final int local, final Object type){ - int l = newLocals.length; - if(local >= l) - { - Object[] a = new Object[Math.max(2 * l, local + 1)]; - System.arraycopy(newLocals, 0, a, 0, l); - newLocals = a; - } - newLocals[local] = type; -} - -private int remap(final int var, final Type type){ - if(var < firstLocal) - { - return var; - } - int key = 2 * var + type.getSize() - 1; - int size = mapping.length; - if(key >= size) - { - int[] newMapping = new int[Math.max(2 * size, key + 1)]; - System.arraycopy(mapping, 0, newMapping, 0, size); - mapping = newMapping; - } - int value = mapping[key]; - if(value == 0) - { - value = nextLocal + 1; - mapping[key] = value; - setLocalType(nextLocal, type); - nextLocal += type.getSize(); - } - if(value - 1 != var) - { - changed = true; - } - return value - 1; -} - -private int remap(final int var, final int size){ - if(var < firstLocal || !changed) - { - return var; - } - int key = 2 * var + size - 1; - int value = key < mapping.length ? mapping[key] : 0; - if(value == 0) - { - throw new IllegalStateException("Unknown local variable " + var); - } - return value - 1; -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm.commons; + +import clojure.asm.Label; +import clojure.asm.MethodAdapter; +import clojure.asm.MethodVisitor; +import clojure.asm.Opcodes; +import clojure.asm.Type; + +/** + * A {@link MethodAdapter} that renumbers local variables in their order of + * appearance. This adapter allows one to easily add new local variables to a + * method. It may be used by inheriting from this class, but the preferred way + * of using it is via delegation: the next visitor in the chain can indeed add + * new locals when needed by calling {@link #newLocal} on this adapter (this + * requires a reference back to this {@link LocalVariablesSorter}). + * + * @author Chris Nokleberg + * @author Eugene Kuleshov + * @author Eric Bruneton + */ +public class LocalVariablesSorter extends MethodAdapter{ + +private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); + +/** + * Mapping from old to new local variable indexes. A local variable at index + * i of size 1 is remapped to 'mapping[2*i]', while a local variable at + * index i of size 2 is remapped to 'mapping[2*i+1]'. + */ +private int[] mapping = new int[40]; + +/** + * Array used to store stack map local variable types after remapping. + */ +private Object[] newLocals = new Object[20]; + +/** + * Index of the first local variable, after formal parameters. + */ +protected final int firstLocal; + +/** + * Index of the next local variable to be created by {@link #newLocal}. + */ +protected int nextLocal; + +/** + * Indicates if at least one local variable has moved due to remapping. + */ +private boolean changed; + +/** + * Creates a new {@link LocalVariablesSorter}. + * + * @param access access flags of the adapted method. + * @param desc the method's descriptor (see {@link Type Type}). + * @param mv the method visitor to which this adapter delegates calls. + */ +public LocalVariablesSorter( + final int access, + final String desc, + final MethodVisitor mv){ + super(mv); + Type[] args = Type.getArgumentTypes(desc); + nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1; + for(int i = 0; i < args.length; i++) + { + nextLocal += args[i].getSize(); + } + firstLocal = nextLocal; +} + +public void visitVarInsn(final int opcode, final int var){ + Type type; + switch(opcode) + { + case Opcodes.LLOAD: + case Opcodes.LSTORE: + type = Type.LONG_TYPE; + break; + + case Opcodes.DLOAD: + case Opcodes.DSTORE: + type = Type.DOUBLE_TYPE; + break; + + case Opcodes.FLOAD: + case Opcodes.FSTORE: + type = Type.FLOAT_TYPE; + break; + + case Opcodes.ILOAD: + case Opcodes.ISTORE: + type = Type.INT_TYPE; + break; + + case Opcodes.ALOAD: + case Opcodes.ASTORE: + type = OBJECT_TYPE; + break; + + // case RET: + default: + type = Type.VOID_TYPE; + } + mv.visitVarInsn(opcode, remap(var, type)); +} + +public void visitIincInsn(final int var, final int increment){ + mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); +} + +public void visitMaxs(final int maxStack, final int maxLocals){ + mv.visitMaxs(maxStack, nextLocal); +} + +public void visitLocalVariable( + final String name, + final String desc, + final String signature, + final Label start, + final Label end, + final int index){ + int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1; + int newIndex = remap(index, size); + mv.visitLocalVariable(name, desc, signature, start, end, newIndex); +} + +public void visitFrame( + final int type, + final int nLocal, + final Object[] local, + final int nStack, + final Object[] stack){ + if(type != Opcodes.F_NEW) + { // uncompressed frame + throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag"); + } + + if(!changed) + { // optimization for the case where mapping = identity + mv.visitFrame(type, nLocal, local, nStack, stack); + return; + } + + // creates a copy of newLocals + Object[] oldLocals = new Object[newLocals.length]; + System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); + + // copies types from 'local' to 'newLocals' + // 'newLocals' already contains the variables added with 'newLocal' + + int index = 0; // old local variable index + int number = 0; // old local variable number + for(; number < nLocal; ++number) + { + Object t = local[number]; + int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; + if(t != Opcodes.TOP) + { + setFrameLocal(remap(index, size), t); + } + index += size; + } + + // removes TOP after long and double types as well as trailing TOPs + + index = 0; + number = 0; + for(int i = 0; index < newLocals.length; ++i) + { + Object t = newLocals[index++]; + if(t != null && t != Opcodes.TOP) + { + newLocals[i] = t; + number = i + 1; + if(t == Opcodes.LONG || t == Opcodes.DOUBLE) + { + index += 1; + } + } + else + { + newLocals[i] = Opcodes.TOP; + } + } + + // visits remapped frame + mv.visitFrame(type, number, newLocals, nStack, stack); + + // restores original value of 'newLocals' + newLocals = oldLocals; +} + +// ------------- + +/** + * Creates a new local variable of the given type. + * + * @param type the type of the local variable to be created. + * @return the identifier of the newly created local variable. + */ +public int newLocal(final Type type){ + Object t; + switch(type.getSort()) + { + case Type.BOOLEAN: + case Type.CHAR: + case Type.BYTE: + case Type.SHORT: + case Type.INT: + t = Opcodes.INTEGER; + break; + case Type.FLOAT: + t = Opcodes.FLOAT; + break; + case Type.LONG: + t = Opcodes.LONG; + break; + case Type.DOUBLE: + t = Opcodes.DOUBLE; + break; + case Type.ARRAY: + t = type.getDescriptor(); + break; + // case Type.OBJECT: + default: + t = type.getInternalName(); + break; + } + int local = nextLocal; + setLocalType(local, type); + setFrameLocal(local, t); + nextLocal += type.getSize(); + return local; +} + +/** + * Sets the current type of the given local variable. The default + * implementation of this method does nothing. + * + * @param local a local variable identifier, as returned by {@link #newLocal + * newLocal()}. + * @param type the type of the value being stored in the local variable + */ +protected void setLocalType(final int local, final Type type){ +} + +private void setFrameLocal(final int local, final Object type){ + int l = newLocals.length; + if(local >= l) + { + Object[] a = new Object[Math.max(2 * l, local + 1)]; + System.arraycopy(newLocals, 0, a, 0, l); + newLocals = a; + } + newLocals[local] = type; +} + +private int remap(final int var, final Type type){ + if(var < firstLocal) + { + return var; + } + int key = 2 * var + type.getSize() - 1; + int size = mapping.length; + if(key >= size) + { + int[] newMapping = new int[Math.max(2 * size, key + 1)]; + System.arraycopy(mapping, 0, newMapping, 0, size); + mapping = newMapping; + } + int value = mapping[key]; + if(value == 0) + { + value = nextLocal + 1; + mapping[key] = value; + setLocalType(nextLocal, type); + nextLocal += type.getSize(); + } + if(value - 1 != var) + { + changed = true; + } + return value - 1; +} + +private int remap(final int var, final int size){ + if(var < firstLocal || !changed) + { + return var; + } + int key = 2 * var + size - 1; + int value = key < mapping.length ? mapping[key] : 0; + if(value == 0) + { + throw new IllegalStateException("Unknown local variable " + var); + } + return value - 1; +} +} diff --git a/src/jvm/clojure/asm/commons/Method.java b/src/jvm/clojure/asm/commons/Method.java index fb5b8e9..2a067d7 100644 --- a/src/jvm/clojure/asm/commons/Method.java +++ b/src/jvm/clojure/asm/commons/Method.java @@ -1,267 +1,267 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm.commons; - -import java.util.HashMap; -import java.util.Map; - -import clojure.asm.Type; - -/** - * A named method descriptor. - * - * @author Juozas Baliuka - * @author Chris Nokleberg - * @author Eric Bruneton - */ -public class Method{ - -/** - * The method name. - */ -private final String name; - -/** - * The method descriptor. - */ -private final String desc; - -/** - * Maps primitive Java type names to their descriptors. - */ -private final static Map DESCRIPTORS; - -static - { - DESCRIPTORS = new HashMap(); - DESCRIPTORS.put("void", "V"); - DESCRIPTORS.put("byte", "B"); - DESCRIPTORS.put("char", "C"); - DESCRIPTORS.put("double", "D"); - DESCRIPTORS.put("float", "F"); - DESCRIPTORS.put("int", "I"); - DESCRIPTORS.put("long", "J"); - DESCRIPTORS.put("short", "S"); - DESCRIPTORS.put("boolean", "Z"); - } - -/** - * Creates a new {@link Method}. - * - * @param name the method's name. - * @param desc the method's descriptor. - */ -public Method(final String name, final String desc){ - this.name = name; - this.desc = desc; -} - -/** - * Creates a new {@link Method}. - * - * @param name the method's name. - * @param returnType the method's return type. - * @param argumentTypes the method's argument types. - */ -public Method( - final String name, - final Type returnType, - final Type[] argumentTypes){ - this(name, Type.getMethodDescriptor(returnType, argumentTypes)); -} - -/** - * Returns a {@link Method} corresponding to the given Java method - * declaration. - * - * @param method a Java method declaration, without argument names, of the - * form "returnType name (argumentType1, ... argumentTypeN)", where - * the types are in plain Java (e.g. "int", "float", - * "java.util.List", ...). Classes of the java.lang package can be - * specified by their unqualified name; all other classes names must - * be fully qualified. - * @return a {@link Method} corresponding to the given Java method - * declaration. - * @throws IllegalArgumentException if method could not get - * parsed. - */ -public static Method getMethod(final String method) - throws IllegalArgumentException{ - return getMethod(method, false); -} - -/** - * Returns a {@link Method} corresponding to the given Java method - * declaration. - * - * @param method a Java method declaration, without argument names, of the - * form "returnType name (argumentType1, ... argumentTypeN)", where - * the types are in plain Java (e.g. "int", "float", - * "java.util.List", ...). Classes of the java.lang package may be - * specified by their unqualified name, depending on the - * defaultPackage argument; all other classes names must be fully - * qualified. - * @param defaultPackage true if unqualified class names belong to the - * default package, or false if they correspond to java.lang classes. - * For instance "Object" means "Object" if this option is true, or - * "java.lang.Object" otherwise. - * @return a {@link Method} corresponding to the given Java method - * declaration. - * @throws IllegalArgumentException if method could not get - * parsed. - */ -public static Method getMethod( - final String method, - final boolean defaultPackage) throws IllegalArgumentException{ - int space = method.indexOf(' '); - int start = method.indexOf('(', space) + 1; - int end = method.indexOf(')', start); - if(space == -1 || start == -1 || end == -1) - { - throw new IllegalArgumentException(); - } - // TODO: Check validity of returnType, methodName and arguments. - String returnType = method.substring(0, space); - String methodName = method.substring(space + 1, start - 1).trim(); - StringBuffer sb = new StringBuffer(); - sb.append('('); - int p; - do - { - String s; - p = method.indexOf(',', start); - if(p == -1) - { - s = map(method.substring(start, end).trim(), defaultPackage); - } - else - { - s = map(method.substring(start, p).trim(), defaultPackage); - start = p + 1; - } - sb.append(s); - } while(p != -1); - sb.append(')'); - sb.append(map(returnType, defaultPackage)); - return new Method(methodName, sb.toString()); -} - -private static String map(final String type, final boolean defaultPackage){ - if(type.equals("")) - { - return type; - } - - StringBuffer sb = new StringBuffer(); - int index = 0; - while((index = type.indexOf("[]", index) + 1) > 0) - { - sb.append('['); - } - - String t = type.substring(0, type.length() - sb.length() * 2); - String desc = (String) DESCRIPTORS.get(t); - if(desc != null) - { - sb.append(desc); - } - else - { - sb.append('L'); - if(t.indexOf('.') < 0) - { - if(!defaultPackage) - { - sb.append("java/lang/"); - } - sb.append(t); - } - else - { - sb.append(t.replace('.', '/')); - } - sb.append(';'); - } - return sb.toString(); -} - -/** - * Returns the name of the method described by this object. - * - * @return the name of the method described by this object. - */ -public String getName(){ - return name; -} - -/** - * Returns the descriptor of the method described by this object. - * - * @return the descriptor of the method described by this object. - */ -public String getDescriptor(){ - return desc; -} - -/** - * Returns the return type of the method described by this object. - * - * @return the return type of the method described by this object. - */ -public Type getReturnType(){ - return Type.getReturnType(desc); -} - -/** - * Returns the argument types of the method described by this object. - * - * @return the argument types of the method described by this object. - */ -public Type[] getArgumentTypes(){ - return Type.getArgumentTypes(desc); -} - -public String toString(){ - return name + desc; -} - -public boolean equals(final Object o){ - if(!(o instanceof Method)) - { - return false; - } - Method other = (Method) o; - return name.equals(other.name) && desc.equals(other.desc); -} - -public int hashCode(){ - return name.hashCode() ^ desc.hashCode(); -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm.commons; + +import java.util.HashMap; +import java.util.Map; + +import clojure.asm.Type; + +/** + * A named method descriptor. + * + * @author Juozas Baliuka + * @author Chris Nokleberg + * @author Eric Bruneton + */ +public class Method{ + +/** + * The method name. + */ +private final String name; + +/** + * The method descriptor. + */ +private final String desc; + +/** + * Maps primitive Java type names to their descriptors. + */ +private final static Map DESCRIPTORS; + +static + { + DESCRIPTORS = new HashMap(); + DESCRIPTORS.put("void", "V"); + DESCRIPTORS.put("byte", "B"); + DESCRIPTORS.put("char", "C"); + DESCRIPTORS.put("double", "D"); + DESCRIPTORS.put("float", "F"); + DESCRIPTORS.put("int", "I"); + DESCRIPTORS.put("long", "J"); + DESCRIPTORS.put("short", "S"); + DESCRIPTORS.put("boolean", "Z"); + } + +/** + * Creates a new {@link Method}. + * + * @param name the method's name. + * @param desc the method's descriptor. + */ +public Method(final String name, final String desc){ + this.name = name; + this.desc = desc; +} + +/** + * Creates a new {@link Method}. + * + * @param name the method's name. + * @param returnType the method's return type. + * @param argumentTypes the method's argument types. + */ +public Method( + final String name, + final Type returnType, + final Type[] argumentTypes){ + this(name, Type.getMethodDescriptor(returnType, argumentTypes)); +} + +/** + * Returns a {@link Method} corresponding to the given Java method + * declaration. + * + * @param method a Java method declaration, without argument names, of the + * form "returnType name (argumentType1, ... argumentTypeN)", where + * the types are in plain Java (e.g. "int", "float", + * "java.util.List", ...). Classes of the java.lang package can be + * specified by their unqualified name; all other classes names must + * be fully qualified. + * @return a {@link Method} corresponding to the given Java method + * declaration. + * @throws IllegalArgumentException if method could not get + * parsed. + */ +public static Method getMethod(final String method) + throws IllegalArgumentException{ + return getMethod(method, false); +} + +/** + * Returns a {@link Method} corresponding to the given Java method + * declaration. + * + * @param method a Java method declaration, without argument names, of the + * form "returnType name (argumentType1, ... argumentTypeN)", where + * the types are in plain Java (e.g. "int", "float", + * "java.util.List", ...). Classes of the java.lang package may be + * specified by their unqualified name, depending on the + * defaultPackage argument; all other classes names must be fully + * qualified. + * @param defaultPackage true if unqualified class names belong to the + * default package, or false if they correspond to java.lang classes. + * For instance "Object" means "Object" if this option is true, or + * "java.lang.Object" otherwise. + * @return a {@link Method} corresponding to the given Java method + * declaration. + * @throws IllegalArgumentException if method could not get + * parsed. + */ +public static Method getMethod( + final String method, + final boolean defaultPackage) throws IllegalArgumentException{ + int space = method.indexOf(' '); + int start = method.indexOf('(', space) + 1; + int end = method.indexOf(')', start); + if(space == -1 || start == -1 || end == -1) + { + throw new IllegalArgumentException(); + } + // TODO: Check validity of returnType, methodName and arguments. + String returnType = method.substring(0, space); + String methodName = method.substring(space + 1, start - 1).trim(); + StringBuffer sb = new StringBuffer(); + sb.append('('); + int p; + do + { + String s; + p = method.indexOf(',', start); + if(p == -1) + { + s = map(method.substring(start, end).trim(), defaultPackage); + } + else + { + s = map(method.substring(start, p).trim(), defaultPackage); + start = p + 1; + } + sb.append(s); + } while(p != -1); + sb.append(')'); + sb.append(map(returnType, defaultPackage)); + return new Method(methodName, sb.toString()); +} + +private static String map(final String type, final boolean defaultPackage){ + if(type.equals("")) + { + return type; + } + + StringBuffer sb = new StringBuffer(); + int index = 0; + while((index = type.indexOf("[]", index) + 1) > 0) + { + sb.append('['); + } + + String t = type.substring(0, type.length() - sb.length() * 2); + String desc = (String) DESCRIPTORS.get(t); + if(desc != null) + { + sb.append(desc); + } + else + { + sb.append('L'); + if(t.indexOf('.') < 0) + { + if(!defaultPackage) + { + sb.append("java/lang/"); + } + sb.append(t); + } + else + { + sb.append(t.replace('.', '/')); + } + sb.append(';'); + } + return sb.toString(); +} + +/** + * Returns the name of the method described by this object. + * + * @return the name of the method described by this object. + */ +public String getName(){ + return name; +} + +/** + * Returns the descriptor of the method described by this object. + * + * @return the descriptor of the method described by this object. + */ +public String getDescriptor(){ + return desc; +} + +/** + * Returns the return type of the method described by this object. + * + * @return the return type of the method described by this object. + */ +public Type getReturnType(){ + return Type.getReturnType(desc); +} + +/** + * Returns the argument types of the method described by this object. + * + * @return the argument types of the method described by this object. + */ +public Type[] getArgumentTypes(){ + return Type.getArgumentTypes(desc); +} + +public String toString(){ + return name + desc; +} + +public boolean equals(final Object o){ + if(!(o instanceof Method)) + { + return false; + } + Method other = (Method) o; + return name.equals(other.name) && desc.equals(other.desc); +} + +public int hashCode(){ + return name.hashCode() ^ desc.hashCode(); +} } \ No newline at end of file diff --git a/src/jvm/clojure/asm/commons/SerialVersionUIDAdder.java b/src/jvm/clojure/asm/commons/SerialVersionUIDAdder.java index 62f381b..42271b0 100644 --- a/src/jvm/clojure/asm/commons/SerialVersionUIDAdder.java +++ b/src/jvm/clojure/asm/commons/SerialVersionUIDAdder.java @@ -1,508 +1,508 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm.commons; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.security.MessageDigest; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; - -import clojure.asm.ClassAdapter; -import clojure.asm.ClassVisitor; -import clojure.asm.FieldVisitor; -import clojure.asm.MethodVisitor; -import clojure.asm.Opcodes; - -/** - * A {@link ClassAdapter} that adds a serial version unique identifier to a - * class if missing. Here is typical usage of this class: - *

- *

- *   ClassWriter cw = new ClassWriter(...);
- *   ClassVisitor sv = new SerialVersionUIDAdder(cw);
- *   ClassVisitor ca = new MyClassAdapter(sv);
- *   new ClassReader(orginalClass).accept(ca, false);
- * 
- *

- * The SVUID algorithm can be found http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html: - *

- *

- * The serialVersionUID is computed using the signature of a stream of bytes
- * that reflect the class definition. The National Institute of Standards and
- * Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a
- * signature for the stream. The first two 32-bit quantities are used to form a
- * 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data
- * types to a sequence of bytes. The values input to the stream are defined by
- * the Java Virtual Machine (VM) specification for classes.
- * 

- * The sequence of items in the stream is as follows: - *

- * 1. The class name written using UTF encoding. - * 2. The class modifiers written as a 32-bit integer. - * 3. The name of each interface sorted by name written using UTF encoding. - * 4. For each field of the class sorted by field name (except private static - * and private transient fields): - * 1. The name of the field in UTF encoding. - * 2. The modifiers of the field written as a 32-bit integer. - * 3. The descriptor of the field in UTF encoding - * 5. If a class initializer exists, write out the following: - * 1. The name of the method, <clinit>, in UTF encoding. - * 2. The modifier of the method, java.lang.reflect.Modifier.STATIC, - * written as a 32-bit integer. - * 3. The descriptor of the method, ()V, in UTF encoding. - * 6. For each non-private constructor sorted by method name and signature: - * 1. The name of the method, <init>, in UTF encoding. - * 2. The modifiers of the method written as a 32-bit integer. - * 3. The descriptor of the method in UTF encoding. - * 7. For each non-private method sorted by method name and signature: - * 1. The name of the method in UTF encoding. - * 2. The modifiers of the method written as a 32-bit integer. - * 3. The descriptor of the method in UTF encoding. - * 8. The SHA-1 algorithm is executed on the stream of bytes produced by - * DataOutputStream and produces five 32-bit values sha[0..4]. - *

- * 9. The hash value is assembled from the first and second 32-bit values of - * the SHA-1 message digest. If the result of the message digest, the five - * 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named - * sha, the hash value would be computed as follows: - *

- * long hash = ((sha[0] >>> 24) & 0xFF) | - * ((sha[0] >>> 16) & 0xFF) << 8 | - * ((sha[0] >>> 8) & 0xFF) << 16 | - * ((sha[0] >>> 0) & 0xFF) << 24 | - * ((sha[1] >>> 24) & 0xFF) << 32 | - * ((sha[1] >>> 16) & 0xFF) << 40 | - * ((sha[1] >>> 8) & 0xFF) << 48 | - * ((sha[1] >>> 0) & 0xFF) << 56; - *

- * - * @author Rajendra Inamdar, Vishal Vishnoi - */ -public class SerialVersionUIDAdder extends ClassAdapter{ - -/** - * Flag that indicates if we need to compute SVUID. - */ -protected boolean computeSVUID; - -/** - * Set to true if the class already has SVUID. - */ -protected boolean hasSVUID; - -/** - * Classes access flags. - */ -protected int access; - -/** - * Internal name of the class - */ -protected String name; - -/** - * Interfaces implemented by the class. - */ -protected String[] interfaces; - -/** - * Collection of fields. (except private static and private transient - * fields) - */ -protected Collection svuidFields; - -/** - * Set to true if the class has static initializer. - */ -protected boolean hasStaticInitializer; - -/** - * Collection of non-private constructors. - */ -protected Collection svuidConstructors; - -/** - * Collection of non-private methods. - */ -protected Collection svuidMethods; - -/** - * Creates a new {@link SerialVersionUIDAdder}. - * - * @param cv a {@link ClassVisitor} to which this visitor will delegate - * calls. - */ -public SerialVersionUIDAdder(final ClassVisitor cv){ - super(cv); - svuidFields = new ArrayList(); - svuidConstructors = new ArrayList(); - svuidMethods = new ArrayList(); -} - -// ------------------------------------------------------------------------ -// Overriden methods -// ------------------------------------------------------------------------ - -/* - * Visit class header and get class name, access , and intefraces - * informatoin (step 1,2, and 3) for SVUID computation. - */ - -public void visit( - final int version, - final int access, - final String name, - final String signature, - final String superName, - final String[] interfaces){ - computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0; - - if(computeSVUID) - { - this.name = name; - this.access = access; - this.interfaces = interfaces; - } - - super.visit(version, access, name, signature, superName, interfaces); -} - -/* - * Visit the methods and get constructor and method information (step 5 and - * 7). Also determince if there is a class initializer (step 6). - */ -public MethodVisitor visitMethod( - final int access, - final String name, - final String desc, - final String signature, - final String[] exceptions){ - if(computeSVUID) - { - if(name.equals("")) - { - hasStaticInitializer = true; - } - /* - * Remembers non private constructors and methods for SVUID - * computation For constructor and method modifiers, only the - * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, - * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags - * are used. - */ - int mods = access - & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE - | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC - | Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED - | Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT); - - // all non private methods - if((access & Opcodes.ACC_PRIVATE) == 0) - { - if(name.equals("")) - { - svuidConstructors.add(new Item(name, mods, desc)); - } - else if(!name.equals("")) - { - svuidMethods.add(new Item(name, mods, desc)); - } - } - } - - return cv.visitMethod(access, name, desc, signature, exceptions); -} - -/* - * Gets class field information for step 4 of the alogrithm. Also determines - * if the class already has a SVUID. - */ -public FieldVisitor visitField( - final int access, - final String name, - final String desc, - final String signature, - final Object value){ - if(computeSVUID) - { - if(name.equals("serialVersionUID")) - { - // since the class already has SVUID, we won't be computing it. - computeSVUID = false; - hasSVUID = true; - } - /* - * Remember field for SVUID computation For field modifiers, only - * the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, - * ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when - * computing serialVersionUID values. - */ - int mods = access - & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE - | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC - | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT); - - if((access & Opcodes.ACC_PRIVATE) == 0 - || (access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0) - { - svuidFields.add(new Item(name, mods, desc)); - } - } - - return super.visitField(access, name, desc, signature, value); -} - -/* - * Add the SVUID if class doesn't have one - */ -public void visitEnd(){ - // compute SVUID and add it to the class - if(computeSVUID && !hasSVUID) - { - try - { - cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, - "serialVersionUID", - "J", - null, - new Long(computeSVUID())); - } - catch(Throwable e) - { - throw new RuntimeException("Error while computing SVUID for " - + name, e); - } - } - - super.visitEnd(); -} - -// ------------------------------------------------------------------------ -// Utility methods -// ------------------------------------------------------------------------ - -/** - * Returns the value of SVUID if the class doesn't have one already. Please - * note that 0 is returned if the class already has SVUID, thus use - * isHasSVUID to determine if the class already had an SVUID. - * - * @return Returns the serial version UID - * @throws IOException - */ -protected long computeSVUID() throws IOException{ - ByteArrayOutputStream bos = null; - DataOutputStream dos = null; - long svuid = 0; - - try - { - bos = new ByteArrayOutputStream(); - dos = new DataOutputStream(bos); - - /* - * 1. The class name written using UTF encoding. - */ - dos.writeUTF(name.replace('/', '.')); - - /* - * 2. The class modifiers written as a 32-bit integer. - */ - dos.writeInt(access - & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL - | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT)); - - /* - * 3. The name of each interface sorted by name written using UTF - * encoding. - */ - Arrays.sort(interfaces); - for(int i = 0; i < interfaces.length; i++) - { - dos.writeUTF(interfaces[i].replace('/', '.')); - } - - /* - * 4. For each field of the class sorted by field name (except - * private static and private transient fields): - * - * 1. The name of the field in UTF encoding. 2. The modifiers of the - * field written as a 32-bit integer. 3. The descriptor of the field - * in UTF encoding - * - * Note that field signatutes are not dot separated. Method and - * constructor signatures are dot separated. Go figure... - */ - writeItems(svuidFields, dos, false); - - /* - * 5. If a class initializer exists, write out the following: 1. The - * name of the method, , in UTF encoding. 2. The modifier of - * the method, java.lang.reflect.Modifier.STATIC, written as a - * 32-bit integer. 3. The descriptor of the method, ()V, in UTF - * encoding. - */ - if(hasStaticInitializer) - { - dos.writeUTF(""); - dos.writeInt(Opcodes.ACC_STATIC); - dos.writeUTF("()V"); - } // if.. - - /* - * 6. For each non-private constructor sorted by method name and - * signature: 1. The name of the method, , in UTF encoding. 2. - * The modifiers of the method written as a 32-bit integer. 3. The - * descriptor of the method in UTF encoding. - */ - writeItems(svuidConstructors, dos, true); - - /* - * 7. For each non-private method sorted by method name and - * signature: 1. The name of the method in UTF encoding. 2. The - * modifiers of the method written as a 32-bit integer. 3. The - * descriptor of the method in UTF encoding. - */ - writeItems(svuidMethods, dos, true); - - dos.flush(); - - /* - * 8. The SHA-1 algorithm is executed on the stream of bytes - * produced by DataOutputStream and produces five 32-bit values - * sha[0..4]. - */ - byte[] hashBytes = computeSHAdigest(bos.toByteArray()); - - /* - * 9. The hash value is assembled from the first and second 32-bit - * values of the SHA-1 message digest. If the result of the message - * digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of - * five int values named sha, the hash value would be computed as - * follows: - * - * long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF) << - * 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) << - * 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) << - * 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) << - * 56; - */ - for(int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) - { - svuid = (svuid << 8) | (hashBytes[i] & 0xFF); - } - } - finally - { - // close the stream (if open) - if(dos != null) - { - dos.close(); - } - } - - return svuid; -} - -/** - * Returns the SHA-1 message digest of the given value. - * - * @param value the value whose SHA message digest must be computed. - * @return the SHA-1 message digest of the given value. - */ -protected byte[] computeSHAdigest(final byte[] value){ - try - { - return MessageDigest.getInstance("SHA").digest(value); - } - catch(Exception e) - { - throw new UnsupportedOperationException(e); - } -} - -/** - * Sorts the items in the collection and writes it to the data output stream - * - * @param itemCollection collection of items - * @param dos a DataOutputStream value - * @param dotted a boolean value - * @throws IOException if an error occurs - */ -private void writeItems( - final Collection itemCollection, - final DataOutputStream dos, - final boolean dotted) throws IOException{ - int size = itemCollection.size(); - Item items[] = (Item[]) itemCollection.toArray(new Item[size]); - Arrays.sort(items); - for(int i = 0; i < size; i++) - { - dos.writeUTF(items[i].name); - dos.writeInt(items[i].access); - dos.writeUTF(dotted - ? items[i].desc.replace('/', '.') - : items[i].desc); - } -} - -// ------------------------------------------------------------------------ -// Inner classes -// ------------------------------------------------------------------------ - -static class Item implements Comparable{ - - String name; - - int access; - - String desc; - - Item(final String name, final int access, final String desc){ - this.name = name; - this.access = access; - this.desc = desc; - } - - public int compareTo(final Object o){ - Item other = (Item) o; - int retVal = name.compareTo(other.name); - if(retVal == 0) - { - retVal = desc.compareTo(other.desc); - } - return retVal; - } -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm.commons; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +import clojure.asm.ClassAdapter; +import clojure.asm.ClassVisitor; +import clojure.asm.FieldVisitor; +import clojure.asm.MethodVisitor; +import clojure.asm.Opcodes; + +/** + * A {@link ClassAdapter} that adds a serial version unique identifier to a + * class if missing. Here is typical usage of this class: + *

+ *

+ *   ClassWriter cw = new ClassWriter(...);
+ *   ClassVisitor sv = new SerialVersionUIDAdder(cw);
+ *   ClassVisitor ca = new MyClassAdapter(sv);
+ *   new ClassReader(orginalClass).accept(ca, false);
+ * 
+ *

+ * The SVUID algorithm can be found http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html: + *

+ *

+ * The serialVersionUID is computed using the signature of a stream of bytes
+ * that reflect the class definition. The National Institute of Standards and
+ * Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a
+ * signature for the stream. The first two 32-bit quantities are used to form a
+ * 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data
+ * types to a sequence of bytes. The values input to the stream are defined by
+ * the Java Virtual Machine (VM) specification for classes.
+ * 

+ * The sequence of items in the stream is as follows: + *

+ * 1. The class name written using UTF encoding. + * 2. The class modifiers written as a 32-bit integer. + * 3. The name of each interface sorted by name written using UTF encoding. + * 4. For each field of the class sorted by field name (except private static + * and private transient fields): + * 1. The name of the field in UTF encoding. + * 2. The modifiers of the field written as a 32-bit integer. + * 3. The descriptor of the field in UTF encoding + * 5. If a class initializer exists, write out the following: + * 1. The name of the method, <clinit>, in UTF encoding. + * 2. The modifier of the method, java.lang.reflect.Modifier.STATIC, + * written as a 32-bit integer. + * 3. The descriptor of the method, ()V, in UTF encoding. + * 6. For each non-private constructor sorted by method name and signature: + * 1. The name of the method, <init>, in UTF encoding. + * 2. The modifiers of the method written as a 32-bit integer. + * 3. The descriptor of the method in UTF encoding. + * 7. For each non-private method sorted by method name and signature: + * 1. The name of the method in UTF encoding. + * 2. The modifiers of the method written as a 32-bit integer. + * 3. The descriptor of the method in UTF encoding. + * 8. The SHA-1 algorithm is executed on the stream of bytes produced by + * DataOutputStream and produces five 32-bit values sha[0..4]. + *

+ * 9. The hash value is assembled from the first and second 32-bit values of + * the SHA-1 message digest. If the result of the message digest, the five + * 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named + * sha, the hash value would be computed as follows: + *

+ * long hash = ((sha[0] >>> 24) & 0xFF) | + * ((sha[0] >>> 16) & 0xFF) << 8 | + * ((sha[0] >>> 8) & 0xFF) << 16 | + * ((sha[0] >>> 0) & 0xFF) << 24 | + * ((sha[1] >>> 24) & 0xFF) << 32 | + * ((sha[1] >>> 16) & 0xFF) << 40 | + * ((sha[1] >>> 8) & 0xFF) << 48 | + * ((sha[1] >>> 0) & 0xFF) << 56; + *

+ * + * @author Rajendra Inamdar, Vishal Vishnoi + */ +public class SerialVersionUIDAdder extends ClassAdapter{ + +/** + * Flag that indicates if we need to compute SVUID. + */ +protected boolean computeSVUID; + +/** + * Set to true if the class already has SVUID. + */ +protected boolean hasSVUID; + +/** + * Classes access flags. + */ +protected int access; + +/** + * Internal name of the class + */ +protected String name; + +/** + * Interfaces implemented by the class. + */ +protected String[] interfaces; + +/** + * Collection of fields. (except private static and private transient + * fields) + */ +protected Collection svuidFields; + +/** + * Set to true if the class has static initializer. + */ +protected boolean hasStaticInitializer; + +/** + * Collection of non-private constructors. + */ +protected Collection svuidConstructors; + +/** + * Collection of non-private methods. + */ +protected Collection svuidMethods; + +/** + * Creates a new {@link SerialVersionUIDAdder}. + * + * @param cv a {@link ClassVisitor} to which this visitor will delegate + * calls. + */ +public SerialVersionUIDAdder(final ClassVisitor cv){ + super(cv); + svuidFields = new ArrayList(); + svuidConstructors = new ArrayList(); + svuidMethods = new ArrayList(); +} + +// ------------------------------------------------------------------------ +// Overriden methods +// ------------------------------------------------------------------------ + +/* + * Visit class header and get class name, access , and intefraces + * informatoin (step 1,2, and 3) for SVUID computation. + */ + +public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces){ + computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0; + + if(computeSVUID) + { + this.name = name; + this.access = access; + this.interfaces = interfaces; + } + + super.visit(version, access, name, signature, superName, interfaces); +} + +/* + * Visit the methods and get constructor and method information (step 5 and + * 7). Also determince if there is a class initializer (step 6). + */ +public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions){ + if(computeSVUID) + { + if(name.equals("")) + { + hasStaticInitializer = true; + } + /* + * Remembers non private constructors and methods for SVUID + * computation For constructor and method modifiers, only the + * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, + * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags + * are used. + */ + int mods = access + & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE + | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC + | Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED + | Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT); + + // all non private methods + if((access & Opcodes.ACC_PRIVATE) == 0) + { + if(name.equals("")) + { + svuidConstructors.add(new Item(name, mods, desc)); + } + else if(!name.equals("")) + { + svuidMethods.add(new Item(name, mods, desc)); + } + } + } + + return cv.visitMethod(access, name, desc, signature, exceptions); +} + +/* + * Gets class field information for step 4 of the alogrithm. Also determines + * if the class already has a SVUID. + */ +public FieldVisitor visitField( + final int access, + final String name, + final String desc, + final String signature, + final Object value){ + if(computeSVUID) + { + if(name.equals("serialVersionUID")) + { + // since the class already has SVUID, we won't be computing it. + computeSVUID = false; + hasSVUID = true; + } + /* + * Remember field for SVUID computation For field modifiers, only + * the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, + * ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when + * computing serialVersionUID values. + */ + int mods = access + & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE + | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC + | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT); + + if((access & Opcodes.ACC_PRIVATE) == 0 + || (access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0) + { + svuidFields.add(new Item(name, mods, desc)); + } + } + + return super.visitField(access, name, desc, signature, value); +} + +/* + * Add the SVUID if class doesn't have one + */ +public void visitEnd(){ + // compute SVUID and add it to the class + if(computeSVUID && !hasSVUID) + { + try + { + cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, + "serialVersionUID", + "J", + null, + new Long(computeSVUID())); + } + catch(Throwable e) + { + throw new RuntimeException("Error while computing SVUID for " + + name, e); + } + } + + super.visitEnd(); +} + +// ------------------------------------------------------------------------ +// Utility methods +// ------------------------------------------------------------------------ + +/** + * Returns the value of SVUID if the class doesn't have one already. Please + * note that 0 is returned if the class already has SVUID, thus use + * isHasSVUID to determine if the class already had an SVUID. + * + * @return Returns the serial version UID + * @throws IOException + */ +protected long computeSVUID() throws IOException{ + ByteArrayOutputStream bos = null; + DataOutputStream dos = null; + long svuid = 0; + + try + { + bos = new ByteArrayOutputStream(); + dos = new DataOutputStream(bos); + + /* + * 1. The class name written using UTF encoding. + */ + dos.writeUTF(name.replace('/', '.')); + + /* + * 2. The class modifiers written as a 32-bit integer. + */ + dos.writeInt(access + & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL + | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT)); + + /* + * 3. The name of each interface sorted by name written using UTF + * encoding. + */ + Arrays.sort(interfaces); + for(int i = 0; i < interfaces.length; i++) + { + dos.writeUTF(interfaces[i].replace('/', '.')); + } + + /* + * 4. For each field of the class sorted by field name (except + * private static and private transient fields): + * + * 1. The name of the field in UTF encoding. 2. The modifiers of the + * field written as a 32-bit integer. 3. The descriptor of the field + * in UTF encoding + * + * Note that field signatutes are not dot separated. Method and + * constructor signatures are dot separated. Go figure... + */ + writeItems(svuidFields, dos, false); + + /* + * 5. If a class initializer exists, write out the following: 1. The + * name of the method, , in UTF encoding. 2. The modifier of + * the method, java.lang.reflect.Modifier.STATIC, written as a + * 32-bit integer. 3. The descriptor of the method, ()V, in UTF + * encoding. + */ + if(hasStaticInitializer) + { + dos.writeUTF(""); + dos.writeInt(Opcodes.ACC_STATIC); + dos.writeUTF("()V"); + } // if.. + + /* + * 6. For each non-private constructor sorted by method name and + * signature: 1. The name of the method, , in UTF encoding. 2. + * The modifiers of the method written as a 32-bit integer. 3. The + * descriptor of the method in UTF encoding. + */ + writeItems(svuidConstructors, dos, true); + + /* + * 7. For each non-private method sorted by method name and + * signature: 1. The name of the method in UTF encoding. 2. The + * modifiers of the method written as a 32-bit integer. 3. The + * descriptor of the method in UTF encoding. + */ + writeItems(svuidMethods, dos, true); + + dos.flush(); + + /* + * 8. The SHA-1 algorithm is executed on the stream of bytes + * produced by DataOutputStream and produces five 32-bit values + * sha[0..4]. + */ + byte[] hashBytes = computeSHAdigest(bos.toByteArray()); + + /* + * 9. The hash value is assembled from the first and second 32-bit + * values of the SHA-1 message digest. If the result of the message + * digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of + * five int values named sha, the hash value would be computed as + * follows: + * + * long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF) << + * 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) << + * 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) << + * 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) << + * 56; + */ + for(int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) + { + svuid = (svuid << 8) | (hashBytes[i] & 0xFF); + } + } + finally + { + // close the stream (if open) + if(dos != null) + { + dos.close(); + } + } + + return svuid; +} + +/** + * Returns the SHA-1 message digest of the given value. + * + * @param value the value whose SHA message digest must be computed. + * @return the SHA-1 message digest of the given value. + */ +protected byte[] computeSHAdigest(final byte[] value){ + try + { + return MessageDigest.getInstance("SHA").digest(value); + } + catch(Exception e) + { + throw new UnsupportedOperationException(e); + } +} + +/** + * Sorts the items in the collection and writes it to the data output stream + * + * @param itemCollection collection of items + * @param dos a DataOutputStream value + * @param dotted a boolean value + * @throws IOException if an error occurs + */ +private void writeItems( + final Collection itemCollection, + final DataOutputStream dos, + final boolean dotted) throws IOException{ + int size = itemCollection.size(); + Item items[] = (Item[]) itemCollection.toArray(new Item[size]); + Arrays.sort(items); + for(int i = 0; i < size; i++) + { + dos.writeUTF(items[i].name); + dos.writeInt(items[i].access); + dos.writeUTF(dotted + ? items[i].desc.replace('/', '.') + : items[i].desc); + } +} + +// ------------------------------------------------------------------------ +// Inner classes +// ------------------------------------------------------------------------ + +static class Item implements Comparable{ + + String name; + + int access; + + String desc; + + Item(final String name, final int access, final String desc){ + this.name = name; + this.access = access; + this.desc = desc; + } + + public int compareTo(final Object o){ + Item other = (Item) o; + int retVal = name.compareTo(other.name); + if(retVal == 0) + { + retVal = desc.compareTo(other.desc); + } + return retVal; + } +} +} diff --git a/src/jvm/clojure/asm/commons/StaticInitMerger.java b/src/jvm/clojure/asm/commons/StaticInitMerger.java index 84fa45a..12471fe 100644 --- a/src/jvm/clojure/asm/commons/StaticInitMerger.java +++ b/src/jvm/clojure/asm/commons/StaticInitMerger.java @@ -1,102 +1,102 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm.commons; - -import clojure.asm.ClassAdapter; -import clojure.asm.ClassVisitor; -import clojure.asm.MethodVisitor; -import clojure.asm.Opcodes; - -/** - * A {@link ClassAdapter} that merges clinit methods into a single one. - * - * @author Eric Bruneton - */ -public class StaticInitMerger extends ClassAdapter{ - -private String name; - -private MethodVisitor clinit; - -private String prefix; - -private int counter; - -public StaticInitMerger(final String prefix, final ClassVisitor cv){ - super(cv); - this.prefix = prefix; -} - -public void visit( - final int version, - final int access, - final String name, - final String signature, - final String superName, - final String[] interfaces){ - cv.visit(version, access, name, signature, superName, interfaces); - this.name = name; -} - -public MethodVisitor visitMethod( - final int access, - final String name, - final String desc, - final String signature, - final String[] exceptions){ - MethodVisitor mv; - if(name.equals("")) - { - int a = Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC; - String n = prefix + counter++; - mv = cv.visitMethod(a, n, desc, signature, exceptions); - - if(clinit == null) - { - clinit = cv.visitMethod(a, name, desc, null, null); - } - clinit.visitMethodInsn(Opcodes.INVOKESTATIC, this.name, n, desc); - } - else - { - mv = cv.visitMethod(access, name, desc, signature, exceptions); - } - return mv; -} - -public void visitEnd(){ - if(clinit != null) - { - clinit.visitInsn(Opcodes.RETURN); - clinit.visitMaxs(0, 0); - } - cv.visitEnd(); -} -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm.commons; + +import clojure.asm.ClassAdapter; +import clojure.asm.ClassVisitor; +import clojure.asm.MethodVisitor; +import clojure.asm.Opcodes; + +/** + * A {@link ClassAdapter} that merges clinit methods into a single one. + * + * @author Eric Bruneton + */ +public class StaticInitMerger extends ClassAdapter{ + +private String name; + +private MethodVisitor clinit; + +private String prefix; + +private int counter; + +public StaticInitMerger(final String prefix, final ClassVisitor cv){ + super(cv); + this.prefix = prefix; +} + +public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces){ + cv.visit(version, access, name, signature, superName, interfaces); + this.name = name; +} + +public MethodVisitor visitMethod( + final int access, + final String name, + final String desc, + final String signature, + final String[] exceptions){ + MethodVisitor mv; + if(name.equals("")) + { + int a = Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC; + String n = prefix + counter++; + mv = cv.visitMethod(a, n, desc, signature, exceptions); + + if(clinit == null) + { + clinit = cv.visitMethod(a, name, desc, null, null); + } + clinit.visitMethodInsn(Opcodes.INVOKESTATIC, this.name, n, desc); + } + else + { + mv = cv.visitMethod(access, name, desc, signature, exceptions); + } + return mv; +} + +public void visitEnd(){ + if(clinit != null) + { + clinit.visitInsn(Opcodes.RETURN); + clinit.visitMaxs(0, 0); + } + cv.visitEnd(); +} +} diff --git a/src/jvm/clojure/asm/commons/TableSwitchGenerator.java b/src/jvm/clojure/asm/commons/TableSwitchGenerator.java index 8818e3c..1a4077d 100644 --- a/src/jvm/clojure/asm/commons/TableSwitchGenerator.java +++ b/src/jvm/clojure/asm/commons/TableSwitchGenerator.java @@ -1,55 +1,55 @@ -/*** - * ASM: a very small and fast Java bytecode manipulation framework - * Copyright (c) 2000-2005 INRIA, France Telecom - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ -package clojure.asm.commons; - -import clojure.asm.Label; - -/** - * A code generator for switch statements. - * - * @author Juozas Baliuka - * @author Chris Nokleberg - * @author Eric Bruneton - */ -public interface TableSwitchGenerator{ - -/** - * Generates the code for a switch case. - * - * @param key the switch case key. - * @param end a label that corresponds to the end of the switch statement. - */ -void generateCase(int key, Label end); - -/** - * Generates the code for the default switch case. - */ -void generateDefault(); -} +/*** + * ASM: a very small and fast Java bytecode manipulation framework + * Copyright (c) 2000-2005 INRIA, France Telecom + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +package clojure.asm.commons; + +import clojure.asm.Label; + +/** + * A code generator for switch statements. + * + * @author Juozas Baliuka + * @author Chris Nokleberg + * @author Eric Bruneton + */ +public interface TableSwitchGenerator{ + +/** + * Generates the code for a switch case. + * + * @param key the switch case key. + * @param end a label that corresponds to the end of the switch statement. + */ +void generateCase(int key, Label end); + +/** + * Generates the code for the default switch case. + */ +void generateDefault(); +} diff --git a/src/jvm/clojure/asm/commons/package.html b/src/jvm/clojure/asm/commons/package.html index 7050bf2..4af263b 100644 --- a/src/jvm/clojure/asm/commons/package.html +++ b/src/jvm/clojure/asm/commons/package.html @@ -1,48 +1,48 @@ - - - -Provides some useful class and method adapters. The preferred way of using - these adapters is by chaining them together and to custom adapters (instead of - inheriting from them). Indeed this approach provides more combination -possibilities than inheritance. For instance, suppose you want to implement an -adapter MyAdapter than needs sorted local variables and intermediate stack map -frame values taking into account the local variables sort. By using inheritance, -this would require MyAdapter to extend AnalyzerAdapter, itself extending -LocalVariablesSorter. But AnalyzerAdapter is not a subclass of -LocalVariablesSorter, so this is not possible. On the contrary, by using -delegation, you can make LocalVariablesSorter delegate to AnalyzerAdapter, -itself delegating to MyAdapter. In this case AnalyzerAdapter computes -intermediate frames based on the output of LocalVariablesSorter, and MyAdapter -can add new locals by calling the newLocal method on LocalVariablesSorter, and -can get the stack map frame state before each instruction by reading the locals -and stack fields in AnalyzerAdapter (this requires references from MyAdapter -back to LocalVariablesSorter and AnalyzerAdapter). + + + +Provides some useful class and method adapters. The preferred way of using + these adapters is by chaining them together and to custom adapters (instead of + inheriting from them). Indeed this approach provides more combination +possibilities than inheritance. For instance, suppose you want to implement an +adapter MyAdapter than needs sorted local variables and intermediate stack map +frame values taking into account the local variables sort. By using inheritance, +this would require MyAdapter to extend AnalyzerAdapter, itself extending +LocalVariablesSorter. But AnalyzerAdapter is not a subclass of +LocalVariablesSorter, so this is not possible. On the contrary, by using +delegation, you can make LocalVariablesSorter delegate to AnalyzerAdapter, +itself delegating to MyAdapter. In this case AnalyzerAdapter computes +intermediate frames based on the output of LocalVariablesSorter, and MyAdapter +can add new locals by calling the newLocal method on LocalVariablesSorter, and +can get the stack map frame state before each instruction by reading the locals +and stack fields in AnalyzerAdapter (this requires references from MyAdapter +back to LocalVariablesSorter and AnalyzerAdapter). \ No newline at end of file diff --git a/src/jvm/clojure/asm/package.html b/src/jvm/clojure/asm/package.html index bb251b1..400799d 100644 --- a/src/jvm/clojure/asm/package.html +++ b/src/jvm/clojure/asm/package.html @@ -1,87 +1,87 @@ - - - -Provides a small and fast bytecode manipulation framework. - -

- The ASM framework is organized - around the {@link clojure.asm.ClassVisitor ClassVisitor}, - {@link clojure.asm.FieldVisitor FieldVisitor} and - {@link clojure.asm.MethodVisitor MethodVisitor} interfaces, which allow - one to visit the fields and methods of a class, including the bytecode - instructions of each method. - -

- In addition to these main interfaces, ASM provides a {@link - clojure.asm.ClassReader ClassReader} class, that can parse an - existing class and make a given visitor visit it. ASM also provides - a {@link clojure.asm.ClassWriter ClassWriter} class, which is - a visitor that generates Java class files. - -

- In order to generate a class from scratch, only the {@link - clojure.asm.ClassWriter ClassWriter} class is necessary. Indeed, - in order to generate a class, one must just call its visitXXX - methods with the appropriate arguments to generate the desired fields - and methods. See the "helloworld" example in the ASM distribution for - more details about class generation. - -

- In order to modify existing classes, one must use a {@link - clojure.asm.ClassReader ClassReader} class to analyze - the original class, a class modifier, and a {@link clojure.asm.ClassWriter - ClassWriter} to construct the modified class. The class modifier - is just a {@link clojure.asm.ClassVisitor ClassVisitor} - that delegates most of the work to another {@link clojure.asm.ClassVisitor - ClassVisitor}, but that sometimes changes some parameter values, - or call additional methods, in order to implement the desired - modification process. In order to make it easier to implement such - class modifiers, ASM provides the {@link clojure.asm.ClassAdapter - ClassAdapter} and {@link clojure.asm.MethodAdapter MethodAdapter} - classes, which implement the {@link clojure.asm.ClassVisitor ClassVisitor} - and {@link clojure.asm.MethodVisitor MethodVisitor} interfaces by - delegating all work to other visitors. See the "adapt" example in the ASM - distribution for more details about class modification. - -

- The size of the core ASM library, asm.jar, is only 42KB, which is much - smaller than the size of the - BCEL library (504KB), and than the - size of the - SERP library (150KB). ASM is also - much faster than these tools. Indeed the overhead of a load time class - transformation process is of the order of 60% with ASM, 700% or more with BCEL, - and 1100% or more with SERP (see the test/perf directory in the ASM - distribution)! - - @since ASM 1.3 - - + + + +Provides a small and fast bytecode manipulation framework. + +

+ The ASM framework is organized + around the {@link clojure.asm.ClassVisitor ClassVisitor}, + {@link clojure.asm.FieldVisitor FieldVisitor} and + {@link clojure.asm.MethodVisitor MethodVisitor} interfaces, which allow + one to visit the fields and methods of a class, including the bytecode + instructions of each method. + +

+ In addition to these main interfaces, ASM provides a {@link + clojure.asm.ClassReader ClassReader} class, that can parse an + existing class and make a given visitor visit it. ASM also provides + a {@link clojure.asm.ClassWriter ClassWriter} class, which is + a visitor that generates Java class files. + +

+ In order to generate a class from scratch, only the {@link + clojure.asm.ClassWriter ClassWriter} class is necessary. Indeed, + in order to generate a class, one must just call its visitXXX + methods with the appropriate arguments to generate the desired fields + and methods. See the "helloworld" example in the ASM distribution for + more details about class generation. + +

+ In order to modify existing classes, one must use a {@link + clojure.asm.ClassReader ClassReader} class to analyze + the original class, a class modifier, and a {@link clojure.asm.ClassWriter + ClassWriter} to construct the modified class. The class modifier + is just a {@link clojure.asm.ClassVisitor ClassVisitor} + that delegates most of the work to another {@link clojure.asm.ClassVisitor + ClassVisitor}, but that sometimes changes some parameter values, + or call additional methods, in order to implement the desired + modification process. In order to make it easier to implement such + class modifiers, ASM provides the {@link clojure.asm.ClassAdapter + ClassAdapter} and {@link clojure.asm.MethodAdapter MethodAdapter} + classes, which implement the {@link clojure.asm.ClassVisitor ClassVisitor} + and {@link clojure.asm.MethodVisitor MethodVisitor} interfaces by + delegating all work to other visitors. See the "adapt" example in the ASM + distribution for more details about class modification. + +

+ The size of the core ASM library, asm.jar, is only 42KB, which is much + smaller than the size of the + BCEL library (504KB), and than the + size of the + SERP library (150KB). ASM is also + much faster than these tools. Indeed the overhead of a load time class + transformation process is of the order of 60% with ASM, 700% or more with BCEL, + and 1100% or more with SERP (see the test/perf directory in the ASM + distribution)! + + @since ASM 1.3 + + diff --git a/src/jvm/clojure/lang/Associative.java b/src/jvm/clojure/lang/Associative.java index a239994..3545dd8 100644 --- a/src/jvm/clojure/lang/Associative.java +++ b/src/jvm/clojure/lang/Associative.java @@ -1,19 +1,19 @@ -package clojure.lang; - -/** - * Copyright (c) Rich Hickey. All rights reserved. - * The use and distribution terms for this software are covered by the - * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) - * which can be found in the file epl-v10.html at the root of this distribution. - * By using this software in any fashion, you are agreeing to be bound by - * the terms of this license. - * You must not remove this notice, or any other, from this software. - */ -public interface Associative extends IPersistentCollection, ILookup{ -boolean containsKey(Object key); - -IMapEntry entryAt(Object key); - -Associative assoc(Object key, Object val); - -} +package clojure.lang; + +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) + * which can be found in the file epl-v10.html at the root of this distribution. + * By using this software in any fashion, you are agreeing to be bound by + * the terms of this license. + * You must not remove this notice, or any other, from this software. + */ +public interface Associative extends IPersistentCollection, ILookup{ +boolean containsKey(Object key); + +IMapEntry entryAt(Object key); + +Associative assoc(Object key, Object val); + +} diff --git a/src/jvm/clojure/lang/Binding.java b/src/jvm/clojure/lang/Binding.java index 4031e58..693c093 100644 --- a/src/jvm/clojure/lang/Binding.java +++ b/src/jvm/clojure/lang/Binding.java @@ -1,26 +1,26 @@ -/** - * Copyright (c) Rich Hickey. All rights reserved. - * The use and distribution terms for this software are covered by the - * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) - * which can be found in the file epl-v10.html at the root of this distribution. - * By using this software in any fashion, you are agreeing to be bound by - * the terms of this license. - * You must not remove this notice, or any other, from this software. - **/ - -package clojure.lang; - -public class Binding{ -public T val; -public final Binding rest; - -public Binding(T val){ - this.val = val; - this.rest = null; -} - -public Binding(T val, Binding rest){ - this.val = val; - this.rest = rest; -} -} +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) + * which can be found in the file epl-v10.html at the root of this distribution. + * By using this software in any fashion, you are agreeing to be bound by + * the terms of this license. + * You must not remove this notice, or any other, from this software. + **/ + +package clojure.lang; + +public class Binding{ +public T val; +public final Binding rest; + +public Binding(T val){ + this.val = val; + this.rest = null; +} + +public Binding(T val, Binding rest){ + this.val = val; + this.rest = rest; +} +} diff --git a/src/jvm/clojure/lang/IMapEntry.java b/src/jvm/clojure/lang/IMapEntry.java index 2204529..de4fdf6 100644 --- a/src/jvm/clojure/lang/IMapEntry.java +++ b/src/jvm/clojure/lang/IMapEntry.java @@ -1,19 +1,19 @@ -/** - * Copyright (c) Rich Hickey. All rights reserved. - * The use and distribution terms for this software are covered by the - * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) - * which can be found in the file epl-v10.html at the root of this distribution. - * By using this software in any fashion, you are agreeing to be bound by - * the terms of this license. - * You must not remove this notice, or any other, from this software. - */ - -package clojure.lang; - -import java.util.Map; - -public interface IMapEntry extends Map.Entry{ -Object key(); - -Object val(); -} +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) + * which can be found in the file epl-v10.html at the root of this distribution. + * By using this software in any fashion, you are agreeing to be bound by + * the terms of this license. + * You must not remove this notice, or any other, from this software. + */ + +package clojure.lang; + +import java.util.Map; + +public interface IMapEntry extends Map.Entry{ +Object key(); + +Object val(); +} diff --git a/src/jvm/clojure/lang/IObj.java b/src/jvm/clojure/lang/IObj.java index 35dbbd3..8bb2cec 100644 --- a/src/jvm/clojure/lang/IObj.java +++ b/src/jvm/clojure/lang/IObj.java @@ -1,18 +1,18 @@ -/** - * Copyright (c) Rich Hickey. All rights reserved. - * The use and distribution terms for this software are covered by the - * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) - * which can be found in the file epl-v10.html at the root of this distribution. - * By using this software in any fashion, you are agreeing to be bound by - * the terms of this license. - * You must not remove this notice, or any other, from this software. - **/ - -package clojure.lang; - - -public interface IObj extends IMeta { - - public IObj withMeta(IPersistentMap meta); - -} +/** + * Copyright (c) Rich Hickey. All rights reserved. + * The use and distribution terms for this software are covered by the + * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) + * which can be found in the file epl-v10.html at the root of this distribution. + * By using this software in any fashion, you are agreeing to be bound by + * the terms of this license. + * You must not remove this notice, or any other, from this software. + **/ + +package clojure.lang; + + +public interface IObj extends IMeta { + + public IObj withMeta(IPersistentMap meta); + +} diff --git a/src/jvm/clojure/lang/IPersistentCollection.java b/src/jvm/clojure/lang/IPersistentCollection.java index 40e0f28..1dfd6ff 100644 --- a/src/jvm/clojure/lang/IPersistentCollection.java +++ b/src/jvm/clojure/lang/IPersistentCollection.java @@ -1,23 +1,23 @@ -package clojure.lang; - -/** - * Copyright (c) Rich Hickey. All rights reserved. - * The use and distribution terms for this software are covered by the - * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) - * which can be found in the file epl-v10.html at the root of this distribution. - * By using this software in any fashion, you are agreeing to be bound by - * the terms of this license. - * You must not remove this notice, or any other, from this software. - */ - - -public interface IPersistentCollection extends Seqable { - -int count(); - -IPersistentCollection cons(Object o); - -IPersistentCollection empty(); - -boolean equiv(Object o); -} +package clojure.lang; + +/** + * Copyright (c) Rich Hickey. All rights reserved. + *