diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2c770e0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,22 @@
+# eclipse
+bin
+*.launch
+.settings
+.metadata
+.classpath
+.project
+
+# idea
+out
+*.ipr
+*.iws
+*.iml
+.idea
+
+# gradle
+build
+.gradle
+
+# other
+eclipse
+run
diff --git a/CREDITS.txt b/CREDITS.txt
new file mode 100644
index 0000000..589f482
--- /dev/null
+++ b/CREDITS.txt
@@ -0,0 +1,65 @@
+Minecraft Forge: Credits/Thank You
+
+Forge is a set of tools and modifications to the Minecraft base game code to assist
+mod developers in creating new and exciting content. It has been in development for
+several years now, but I would like to take this time thank a few people who have
+helped it along it's way.
+
+First, the people who originally created the Forge projects way back in Minecraft
+alpha. Eloraam of RedPower, and SpaceToad of Buildcraft, without their acceptiance
+of me taking over the project, who knows what Minecraft modding would be today.
+
+Secondly, someone who has worked with me, and developed some of the core features
+that allow modding to he as functional, and as simple as it is, cpw. For developing
+FML, which stabelized the client and server modding ecosystem. As well as the base
+loading system that allows us to modify Minecraft's code as elegently as possible.
+
+Mezz, who has stepped up as the issue and pull request manager. Helping to keep me
+sane as well as guiding the community into creating better additions to Forge.
+
+Searge, Bspks, Fesh0r, ProfMobious, and all the rest over on the MCP team {of which
+I am a part}. For creating some of the core tools needed to make Minecraft modding
+both possible, and as stable as can be.
+ On that note, here is some specific information of the MCP data we use:
+ * Minecraft Coder Pack (MCP) *
+ Forge Mod Loader and Minecraft Forge have permission to distribute and automatically
+ download components of MCP and distribute MCP data files. This permission is not
+ transitive and others wishing to redistribute the Minecraft Forge source independently
+ should seek permission of MCP or remove the MCP data files and request their users
+ to download MCP separately.
+
+And lastly, the countless community members who have spent time submitting bug reports,
+pull requests, and just helping out the community in general. Thank you.
+
+--LexManos
+
+=========================================================================
+
+This is Forge Mod Loader.
+
+You can find the source code at all times at https://github.com/MinecraftForge/MinecraftForge/tree/1.12.x/src/main/java/net/minecraftforge/fml
+
+This minecraft mod is a clean open source implementation of a mod loader for minecraft servers
+and minecraft clients.
+
+The code is authored by cpw.
+
+It began by partially implementing an API defined by the client side ModLoader, authored by Risugami.
+http://www.minecraftforum.net/topic/75440-
+This support has been dropped as of Minecraft release 1.7, as Risugami no longer maintains ModLoader.
+
+It also contains suggestions and hints and generous helpings of code from LexManos, author of MinecraftForge.
+http://www.minecraftforge.net/
+
+Additionally, it contains an implementation of topological sort based on that
+published at http://keithschwarz.com/interesting/code/?dir=topological-sort
+
+It also contains code from the Maven project for performing versioned dependency
+resolution. http://maven.apache.org/
+
+It also contains a partial repackaging of the javaxdelta library from http://sourceforge.net/projects/javaxdelta/
+with credit to it's authors.
+
+Forge Mod Loader downloads components from the Minecraft Coder Pack
+(http://mcp.ocean-labs.de/index.php/Main_Page) with kind permission from the MCP team.
+
diff --git a/LICENSE-Paulscode IBXM Library.txt b/LICENSE-Paulscode IBXM Library.txt
new file mode 100644
index 0000000..d4884b0
--- /dev/null
+++ b/LICENSE-Paulscode IBXM Library.txt
@@ -0,0 +1,10 @@
+IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD License.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+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.
+Neither the name of mumart 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 HOLDER 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.
+
diff --git a/LICENSE-Paulscode SoundSystem CodecIBXM.txt b/LICENSE-Paulscode SoundSystem CodecIBXM.txt
new file mode 100644
index 0000000..a68a494
--- /dev/null
+++ b/LICENSE-Paulscode SoundSystem CodecIBXM.txt
@@ -0,0 +1,40 @@
+SoundSystem CodecIBXM Class License:
+
+You are free to use this class for any purpose, commercial or otherwise.
+You may modify this class or source code, and distribute it any way you
+like, provided the following conditions are met:
+
+1) You may not falsely claim to be the author of this class or any
+ unmodified portion of it.
+2) You may not copyright this class or a modified version of it and then
+ sue me for copyright infringement.
+3) If you modify the source code, you must clearly document the changes
+ made before redistributing the modified source code, so other users know
+ it is not the original code.
+4) You are not required to give me credit for this class in any derived
+ work, but if you do, you must also mention my website:
+ http://www.paulscode.com
+5) I the author will not be responsible for any damages (physical,
+ financial, or otherwise) caused by the use if this class or any
+ portion of it.
+6) I the author do not guarantee, warrant, or make any representations,
+ either expressed or implied, regarding the use of this class or any
+ portion of it.
+
+Author: Paul Lamb
+http://www.paulscode.com
+
+
+This software is based on or using the IBXM library available from
+http://www.geocities.com/sunet2000/
+
+
+IBXM is copyright (c) 2007, Martin Cameron, and is licensed under the BSD License.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+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.
+Neither the name of mumart 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 HOLDER 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.
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..97b081b
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,526 @@
+Unless noted below, Minecraft Forge, Forge Mod Loader, and all
+parts herein are licensed under the terms of the LGPL 2.1 found
+here http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt and
+copied below.
+
+Homepage: http://MinecraftForge.net/
+ http://github.com/MinecraftForge/MinecraftForge
+
+
+A note on authorship:
+All source artifacts are property of their original author, with
+the exclusion of the contents of the patches directory and others
+copied from it from time to time. Authorship of the contents of
+the patches directory is retained by the Minecraft Forge project.
+This is because the patches are partially machine generated
+artifacts, and are changed heavily due to the way forge works.
+Individual attribution within them is impossible.
+
+Consent:
+All contributions to Forge must consent to the release of any
+patch content to the Forge project.
+
+A note on infectivity:
+The LGPL is chosen specifically so that projects may depend on Forge
+features without being infected with its license. That is the
+purpose of the LGPL. Mods and others using this code via ordinary
+Java mechanics for referencing libraries are specifically not bound
+by Forge's license for the Mod code.
+
+
+=== MCP Data ===
+This software includes data from the Minecraft Coder Pack (MCP), with kind permission
+from them. The license to MCP data is not transitive - distribution of this data by
+third parties requires independent licensing from the MCP team. This data is not
+redistributable without permission from the MCP team.
+
+=== Sharing ===
+I grant permission for some parts of FML to be redistributed outside the terms of the LGPL, for the benefit of
+the minecraft modding community. All contributions to these parts should be licensed under the same additional grant.
+
+-- Runtime patcher --
+License is granted to redistribute the runtime patcher code (src/main/java/net/minecraftforge/fml/common/patcher
+and subdirectories) under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
+
+-- ASM transformers --
+License is granted to redistribute the ASM transformer code (src/main/java/net/minecraftforge/common/asm/ and subdirectories)
+under any alternative open source license as classified by the OSI (http://opensource.org/licenses)
+
+=========================================================================
+This software includes portions from the Apache Maven project at
+http://maven.apache.org/ specifically the ComparableVersion.java code. It is
+included based on guidelines at
+http://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html
+with notices intact. The only change is a non-functional change of package name.
+
+This software contains a partial repackaging of javaxdelta, a BSD licensed program for generating
+binary differences and applying them, sourced from the subversion at http://sourceforge.net/projects/javaxdelta/
+authored by genman, heikok, pivot.
+The only changes are to replace some Trove collection types with standard Java collections, and repackaged.
+
+This software contains potions of Paulscodee IBXM library, a BSD liceensed library for
+loading and playing IBXM formated auto. No modifications havee beeen made. The associated
+licenses can be found along side this one, or at
+https://github.com/MinecraftForge/MinecraftForge/blob/1.12.x/LICENSE-Paulscode%20IBXM%20Library.txt
+https://github.com/MinecraftForge/MinecraftForge/blob/1.12.x/LICENSE-Paulscode%20SoundSystem%20CodecIBXM.txt
+=========================================================================
+
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2a8d2a3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# TaleCraft
+
+- Wiki: http://talecraft.wikia.com/wiki/Talecraft_Wiki
+- Download: http://minecraft.curseforge.com/projects/talecraft
+- Scripting Wiki: https://github.com/tiffit/TaleCraft/wiki
+- Issues: https://github.com/tiffit/TaleCraft/issues
+
+## Purpose
+The purpose of this mod is to add new map-making capabilities to Minecraft,
+in the form of various tools, blocks, entities and a embedded JavaScript engine.
+
+## Contributing
+
+1. Create a fork of the TaleCraft repository.
+2. Commit changes to that fork of yours.
+3. Create a pull-request.
+4. Wait a day or two.
+5. ???
\ No newline at end of file
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..82be8dc
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,62 @@
+-------------------------------------------
+Source installation information for modders
+-------------------------------------------
+This code follows the Minecraft Forge installation methodology. It will apply
+some small patches to the vanilla MCP source code, giving you and it access
+to some of the data and functions you need to build a successful mod.
+
+Note also that the patches are built against "unrenamed" MCP source code (aka
+srgnames) - this means that you will not be able to read them directly against
+normal code.
+
+Source pack installation information:
+
+Standalone source installation
+==============================
+
+See the Forge Documentation online for more detailed instructions:
+http://mcforge.readthedocs.io/en/latest/gettingstarted/
+
+Step 1: Open your command-line and browse to the folder where you extracted the zip file.
+
+Step 2: Once you have a command window up in the folder that the downloaded material was placed, type:
+
+Windows: "gradlew setupDecompWorkspace"
+Linux/Mac OS: "./gradlew setupDecompWorkspace"
+
+Step 3: After all that finished, you're left with a choice.
+For eclipse, run "gradlew eclipse" (./gradlew eclipse if you are on Mac/Linux)
+
+If you prefer to use IntelliJ, steps are a little different.
+1. Open IDEA, and import project.
+2. Select your build.gradle file and have it import.
+3. Once it's finished you must close IntelliJ and run the following command:
+
+"gradlew genIntellijRuns" (./gradlew genIntellijRuns if you are on Mac/Linux)
+
+Step 4: The final step is to open Eclipse and switch your workspace to /eclipse/ (if you use IDEA, it should automatically start on your project)
+
+If at any point you are missing libraries in your IDE, or you've run into problems you can run "gradlew --refresh-dependencies" to refresh the local cache. "gradlew clean" to reset everything {this does not affect your code} and then start the processs again.
+
+Should it still not work,
+Refer to #ForgeGradle on EsperNet for more information about the gradle environment.
+
+Tip:
+If you do not care about seeing Minecraft's source code you can replace "setupDecompWorkspace" with one of the following:
+"setupDevWorkspace": Will patch, deobfuscate, and gather required assets to run minecraft, but will not generate human readable source code.
+"setupCIWorkspace": Same as Dev but will not download any assets. This is useful in build servers as it is the fastest because it does the least work.
+
+Tip:
+When using Decomp workspace, the Minecraft source code is NOT added to your workspace in a editable way. Minecraft is treated like a normal Library. Sources are there for documentation and research purposes and usually can be accessed under the 'referenced libraries' section of your IDE.
+
+Forge source installation
+=========================
+MinecraftForge ships with this code and installs it as part of the forge
+installation process, no further action is required on your part.
+
+LexManos' Install Video
+=======================
+https://www.youtube.com/watch?v=8VEdtQLuLO0&feature=youtu.be
+
+For more details update more often refer to the Forge Forums:
+http://www.minecraftforge.net/forum/index.php/topic,14048.0.html
diff --git a/Script Examples/NPC/diamond_quest.js b/Script Examples/NPC/diamond_quest.js
new file mode 100644
index 0000000..c66cf06
--- /dev/null
+++ b/Script Examples/NPC/diamond_quest.js
@@ -0,0 +1,34 @@
+if(!npc.getScriptData().hasKey("quest_stage")){
+ var dialogue_ask = npc.createDialogue("ask");
+ dialogue_ask.setTitle("Diamond Quest");
+ dialogue_ask.setText("Hey, could you get me a diamond please?");
+ dialogue_ask.addOption("Sure!", null, "action.nbt={quest_stage:0s}");
+ dialogue_ask.addOption("No, sorry not now.");
+ npc.sendDialogue("ask", player);
+}else if(npc.getScriptData().getShort("quest_stage") == 0){
+ if(!player.getInventory().isHeldNull() && itemstack.getItem().getUnlocalizedName() == "item.diamond"){
+ var dialogue_thank = npc.createDialogue("thank");
+ dialogue_thank.setTitle("Diamond Quest");
+ dialogue_thank.setText("Thanks for the diamond!");
+ dialogue_thank.addOption("No problem!");
+ npc.sendDialogue("thank", player);
+ npc.getScriptData().setShort("quest_stage", 1);
+ itemstack.incStackSize(-1);
+ if(itemstack.getStackSize() <= 0){
+ player.getInventory().clearHeldItem();
+ }
+ }else{
+ var dialogue_wait = npc.createDialogue("wait");
+ dialogue_wait.setTitle("Diamond Quest");
+ dialogue_wait.setText("Still waiting for my 1 diamond!");
+ dialogue_wait.addOption("On it!");
+ npc.sendDialogue("wait", player);
+ }
+}else{
+ var dialogue_thankagain = npc.createDialogue("thankagain");
+ dialogue_thankagain.setTitle("Diamond Quest");
+ dialogue_thankagain.setText("Thanks again for the diamond!");
+ dialogue_thankagain.addOption("No problem!");
+ npc.sendDialogue("thankagain", player);
+}
+
diff --git a/Script Examples/NPC/go_to_player.js b/Script Examples/NPC/go_to_player.js
new file mode 100644
index 0000000..cd53a04
--- /dev/null
+++ b/Script Examples/NPC/go_to_player.js
@@ -0,0 +1,5 @@
+if(npc.moveToBlock(player.getX(), player.getY(), player.getZ())){
+ player.sendMessage("On my way!");
+}else{
+ player.sendMessage("I am not able to come to you at this moment!");
+}
diff --git a/Script Examples/setTimeout.js b/Script Examples/setTimeout.js
new file mode 100644
index 0000000..f360d08
--- /dev/null
+++ b/Script Examples/setTimeout.js
@@ -0,0 +1,6 @@
+system.setTimeout(
+ function() {
+ out.println("setTimeout(func,delay) FTW!")
+ },
+ 1000
+);
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..9bdc257
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,93 @@
+buildscript {
+ repositories {
+ jcenter()
+ maven { url = "http://files.minecraftforge.net/maven" }
+ }
+ dependencies {
+ classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT'
+ }
+}
+apply plugin: 'net.minecraftforge.gradle.forge'
+//Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
+
+
+version = "0.3.4"
+group = "talecraft" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
+archivesBaseName = "talecraft"
+
+sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
+compileJava {
+ sourceCompatibility = targetCompatibility = '1.8'
+}
+
+minecraft {
+ version = "1.12.2-14.23.4.2705"
+ runDir = "run"
+
+ // the mappings can be changed at any time, and must be in the following format.
+ // snapshot_YYYYMMDD snapshot are built nightly.
+ // stable_# stables are built at the discretion of the MCP team.
+ // Use non-default mappings at your own risk. they may not always work.
+ // simply re-run your setup task after changing the mappings to update your workspace.
+ mappings = "snapshot_20171003"
+ // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
+}
+
+dependencies {
+ // you may put jars on which you depend on in ./libs
+ // or you may define them like so..
+ //compile "some.group:artifact:version:classifier"
+ //compile "some.group:artifact:version"
+
+ // real examples
+ //compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
+ //compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
+
+ // the 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime.
+ //provided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
+
+ // the deobf configurations: 'deobfCompile' and 'deobfProvided' are the same as the normal compile and provided,
+ // except that these dependencies get remapped to your current MCP mappings
+ //deobfCompile 'com.mod-buildcraft:buildcraft:6.0.8:dev'
+ //deobfProvided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
+
+ // for more info...
+ // http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
+ // http://www.gradle.org/docs/current/userguide/dependency_management.html
+
+
+
+ compile files("E:\\Desktop\\DRPC Mod\\build\\libs\\discordrpc-1.2.2.jar")
+
+}
+javadoc {
+ source sourceSets.api.allJava
+}
+processResources {
+ // this will ensure that this task is redone when the versions change.
+ inputs.property "version", project.version
+ inputs.property "mcversion", project.minecraft.version
+
+ // replace stuff in mcmod.info, nothing else
+ from(sourceSets.main.resources.srcDirs) {
+ include 'mcmod.info'
+
+ // replace version and mcversion
+ expand 'version':project.version, 'mcversion':project.minecraft.version
+ }
+
+ // copy everything else except the mcmod.info
+ from(sourceSets.main.resources.srcDirs) {
+ exclude 'mcmod.info'
+ }
+}
+jar {
+ manifest {
+ attributes([
+ 'Maven-Artifact': "${project.group}:${project.archivesBaseName}:${project.version}",
+ 'Timestamp': System.currentTimeMillis(),
+ 'FMLCorePlugin': "talecraft.ASM.LoadingPlugin",
+ 'FMLCorePluginContainsFMLMod': true
+ ])
+ }
+}
diff --git a/desc.txt b/desc.txt
new file mode 100644
index 0000000..60d0c84
--- /dev/null
+++ b/desc.txt
@@ -0,0 +1,6 @@
+Note: This is an example desc.txt
+Hello! This is my sample world!
+You can do lots of cool things here!
+You can build, but <16711680>don't <16777215>destroy any of my stuff!
+I hope you <65280>have lots of fun!
+<56814>Good luck!
\ No newline at end of file
diff --git a/forge-1.12.2-14.23.5.2772-changelog.txt b/forge-1.12.2-14.23.5.2772-changelog.txt
new file mode 100644
index 0000000..1122e56
--- /dev/null
+++ b/forge-1.12.2-14.23.5.2772-changelog.txt
@@ -0,0 +1,111 @@
+Changelog:
+Build 2772:
+ tterrag: Make Forge-provided default transforms accessible to custom models
+ tterrag:
+ Make Forge blockstate variants correctly inherit AO setting from vanilla
+ models (#5190)
+ * Make Forge blockstate variants correctly inherit AO setting
+
+ * Move variant format checks into variant, check for added properties
+
+ * Small code cleanup
+ tterrag: fixed visual bug with guislider
+ tterrag:
+ Allow IModel to express itself as a vanilla parent (#5195)
+ * Fix errors caused by fancy missing model being non-vanilla parent
+
+ * Switch instanceof checks to a default IModel method
+
+ * Small code tweaks
+
+Build 2771:
+ tterrag: Add CreativeTabs#getLabelColor
+
+Build 2770:
+ tterrag:
+ Allow providing a BufferedImage for banner render generation (#5041)
+ * Adds an Event to allow providing a BufferedImage for the banner render
+ generation (cached)
+ textures, since banners don't use an atlas.
+ implementation.
+
+ * Missed some copyright; might as well absolut text match, I guess
+
+ * Remove unneeded patch change
+
+ * Fix event variable access convention
+
+ * Formatting fix
+
+ * Improve event handling registration
+
+ * Import cleanup
+
+ * Replace event approach with Supplier approach
+
+ * Better name for test mod (now that it's no longer an event); adds
+ ENABLE flag
+
+ * Moves MC code into
+ net.minecraftforge.client.MinecraftForgeClient.getImageLayer to simplify
+ patch
+
+ * Generalize naming
+
+Build 2769:
+ mezz: Fix Mesa biome entry tags in the BiomeDictionary (#5177)
+
+Build 2768:
+ LexManos: Bump version number for RB.
+
+Build 2767:
+ LexManos:
+ Change biome spawn list entries to use factory method where possible
+ (#5075)
+ LexManos: Prevent some texture loading errors from crashing the game (#5121)
+ LexManos: Patch PotionHelper to use registry delegates (#5142)
+ LexManos: Add a notification event for handling game rule changes (#5152)
+
+Build 2766:
+ LexManos:
+ Change universal bucket support to use fluid names instead of instances
+ (#5031)
+
+Build 2765:
+ LexManos: Fix NPE on clientside entities constructed with null world (#5170)
+
+Build 2764:
+ tterrag: Fix patches from #5160 running on the client and causing stutter
+
+Build 2763:
+ LexManos:
+ Class transformer optimizations (#5159)
+ * Filter packages for deobf transformation
+ * Only serialize transformed class with TerminalTransformer if bytecode
+ changed
+
+Build 2762:
+ github: Update github stale so issues can be Assigned
+
+Build 2761:
+ LexManos:
+ Fix MC-136995 - Chunk loading and unloading issue with entities placed
+ in exact positions. (#5160)
+ Scatter gun patches to improve entity tracking and position tracking.
+ Provided by Aikar through the Paper project, this commit of patches
+ combines the following patches:
+
+ https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0306-Mark-chunk-dirty-anytime-entities-change-to-guarante.patch
+
+ https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0315-Always-process-chunk-registration-after-moving.patch
+
+ https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0335-Ensure-chunks-are-always-loaded-on-hard-position-set.patch
+
+ https://github.com/PaperMC/Paper/blob/fd1bd5223a461b6d98280bb8f2d67280a30dd24a/Spigot-Server-Patches/0378-Sync-Player-Position-to-Vehicles.patch
+
+Build 2760:
+ LexManos: Fix --mods and --modListFile arguments not making it past LaunchWrapper.
+
+Build 2759:
+ LexManos: Remove BlamingTransformer (#5115)
+
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..e9b9fd5
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,3 @@
+# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
+# This is required to provide enough memory for the Minecraft decompilation process.
+org.gradle.jvmargs=-Xmx3G
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..30d399d
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..ff1349a
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Sep 14 12:28:28 PDT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..91a7e26
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/main/java/org/mozilla/classfile/ByteCode.java b/src/main/java/org/mozilla/classfile/ByteCode.java
new file mode 100644
index 0000000..df264b2
--- /dev/null
+++ b/src/main/java/org/mozilla/classfile/ByteCode.java
@@ -0,0 +1,242 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.classfile;
+
+/**
+ * This class provides opcode values expected by the JVM in Java class files.
+ *
+ * It also provides tables for internal use by the ClassFileWriter.
+ *
+ * @author Roger Lawrence
+ */
+public class ByteCode {
+
+ /**
+ * The byte opcodes defined by the Java Virtual Machine.
+ */
+ public static final int
+ NOP = 0x00,
+ ACONST_NULL = 0x01,
+ ICONST_M1 = 0x02,
+ ICONST_0 = 0x03,
+ ICONST_1 = 0x04,
+ ICONST_2 = 0x05,
+ ICONST_3 = 0x06,
+ ICONST_4 = 0x07,
+ ICONST_5 = 0x08,
+ LCONST_0 = 0x09,
+ LCONST_1 = 0x0A,
+ FCONST_0 = 0x0B,
+ FCONST_1 = 0x0C,
+ FCONST_2 = 0x0D,
+ DCONST_0 = 0x0E,
+ DCONST_1 = 0x0F,
+ BIPUSH = 0x10,
+ SIPUSH = 0x11,
+ LDC = 0x12,
+ LDC_W = 0x13,
+ LDC2_W = 0x14,
+ ILOAD = 0x15,
+ LLOAD = 0x16,
+ FLOAD = 0x17,
+ DLOAD = 0x18,
+ ALOAD = 0x19,
+ ILOAD_0 = 0x1A,
+ ILOAD_1 = 0x1B,
+ ILOAD_2 = 0x1C,
+ ILOAD_3 = 0x1D,
+ LLOAD_0 = 0x1E,
+ LLOAD_1 = 0x1F,
+ LLOAD_2 = 0x20,
+ LLOAD_3 = 0x21,
+ FLOAD_0 = 0x22,
+ FLOAD_1 = 0x23,
+ FLOAD_2 = 0x24,
+ FLOAD_3 = 0x25,
+ DLOAD_0 = 0x26,
+ DLOAD_1 = 0x27,
+ DLOAD_2 = 0x28,
+ DLOAD_3 = 0x29,
+ ALOAD_0 = 0x2A,
+ ALOAD_1 = 0x2B,
+ ALOAD_2 = 0x2C,
+ ALOAD_3 = 0x2D,
+ IALOAD = 0x2E,
+ LALOAD = 0x2F,
+ FALOAD = 0x30,
+ DALOAD = 0x31,
+ AALOAD = 0x32,
+ BALOAD = 0x33,
+ CALOAD = 0x34,
+ SALOAD = 0x35,
+ ISTORE = 0x36,
+ LSTORE = 0x37,
+ FSTORE = 0x38,
+ DSTORE = 0x39,
+ ASTORE = 0x3A,
+ ISTORE_0 = 0x3B,
+ ISTORE_1 = 0x3C,
+ ISTORE_2 = 0x3D,
+ ISTORE_3 = 0x3E,
+ LSTORE_0 = 0x3F,
+ LSTORE_1 = 0x40,
+ LSTORE_2 = 0x41,
+ LSTORE_3 = 0x42,
+ FSTORE_0 = 0x43,
+ FSTORE_1 = 0x44,
+ FSTORE_2 = 0x45,
+ FSTORE_3 = 0x46,
+ DSTORE_0 = 0x47,
+ DSTORE_1 = 0x48,
+ DSTORE_2 = 0x49,
+ DSTORE_3 = 0x4A,
+ ASTORE_0 = 0x4B,
+ ASTORE_1 = 0x4C,
+ ASTORE_2 = 0x4D,
+ ASTORE_3 = 0x4E,
+ IASTORE = 0x4F,
+ LASTORE = 0x50,
+ FASTORE = 0x51,
+ DASTORE = 0x52,
+ AASTORE = 0x53,
+ BASTORE = 0x54,
+ CASTORE = 0x55,
+ SASTORE = 0x56,
+ POP = 0x57,
+ POP2 = 0x58,
+ DUP = 0x59,
+ DUP_X1 = 0x5A,
+ DUP_X2 = 0x5B,
+ DUP2 = 0x5C,
+ DUP2_X1 = 0x5D,
+ DUP2_X2 = 0x5E,
+ SWAP = 0x5F,
+ IADD = 0x60,
+ LADD = 0x61,
+ FADD = 0x62,
+ DADD = 0x63,
+ ISUB = 0x64,
+ LSUB = 0x65,
+ FSUB = 0x66,
+ DSUB = 0x67,
+ IMUL = 0x68,
+ LMUL = 0x69,
+ FMUL = 0x6A,
+ DMUL = 0x6B,
+ IDIV = 0x6C,
+ LDIV = 0x6D,
+ FDIV = 0x6E,
+ DDIV = 0x6F,
+ IREM = 0x70,
+ LREM = 0x71,
+ FREM = 0x72,
+ DREM = 0x73,
+ INEG = 0x74,
+ LNEG = 0x75,
+ FNEG = 0x76,
+ DNEG = 0x77,
+ ISHL = 0x78,
+ LSHL = 0x79,
+ ISHR = 0x7A,
+ LSHR = 0x7B,
+ IUSHR = 0x7C,
+ LUSHR = 0x7D,
+ IAND = 0x7E,
+ LAND = 0x7F,
+ IOR = 0x80,
+ LOR = 0x81,
+ IXOR = 0x82,
+ LXOR = 0x83,
+ IINC = 0x84,
+ I2L = 0x85,
+ I2F = 0x86,
+ I2D = 0x87,
+ L2I = 0x88,
+ L2F = 0x89,
+ L2D = 0x8A,
+ F2I = 0x8B,
+ F2L = 0x8C,
+ F2D = 0x8D,
+ D2I = 0x8E,
+ D2L = 0x8F,
+ D2F = 0x90,
+ I2B = 0x91,
+ I2C = 0x92,
+ I2S = 0x93,
+ LCMP = 0x94,
+ FCMPL = 0x95,
+ FCMPG = 0x96,
+ DCMPL = 0x97,
+ DCMPG = 0x98,
+ IFEQ = 0x99,
+ IFNE = 0x9A,
+ IFLT = 0x9B,
+ IFGE = 0x9C,
+ IFGT = 0x9D,
+ IFLE = 0x9E,
+ IF_ICMPEQ = 0x9F,
+ IF_ICMPNE = 0xA0,
+ IF_ICMPLT = 0xA1,
+ IF_ICMPGE = 0xA2,
+ IF_ICMPGT = 0xA3,
+ IF_ICMPLE = 0xA4,
+ IF_ACMPEQ = 0xA5,
+ IF_ACMPNE = 0xA6,
+ GOTO = 0xA7,
+ JSR = 0xA8,
+ RET = 0xA9,
+ TABLESWITCH = 0xAA,
+ LOOKUPSWITCH = 0xAB,
+ IRETURN = 0xAC,
+ LRETURN = 0xAD,
+ FRETURN = 0xAE,
+ DRETURN = 0xAF,
+ ARETURN = 0xB0,
+ RETURN = 0xB1,
+ GETSTATIC = 0xB2,
+ PUTSTATIC = 0xB3,
+ GETFIELD = 0xB4,
+ PUTFIELD = 0xB5,
+ INVOKEVIRTUAL = 0xB6,
+ INVOKESPECIAL = 0xB7,
+ INVOKESTATIC = 0xB8,
+ INVOKEINTERFACE = 0xB9,
+ NEW = 0xBB,
+ NEWARRAY = 0xBC,
+ ANEWARRAY = 0xBD,
+ ARRAYLENGTH = 0xBE,
+ ATHROW = 0xBF,
+ CHECKCAST = 0xC0,
+ INSTANCEOF = 0xC1,
+ MONITORENTER = 0xC2,
+ MONITOREXIT = 0xC3,
+ WIDE = 0xC4,
+ MULTIANEWARRAY = 0xC5,
+ IFNULL = 0xC6,
+ IFNONNULL = 0xC7,
+ GOTO_W = 0xC8,
+ JSR_W = 0xC9,
+ BREAKPOINT = 0xCA,
+
+ IMPDEP1 = 0xFE,
+ IMPDEP2 = 0xFF;
+
+ /**
+ * Types for the NEWARRAY opcode.
+ */
+ public static final byte
+ T_BOOLEAN = 4,
+ T_CHAR = 5,
+ T_FLOAT = 6,
+ T_DOUBLE = 7,
+ T_BYTE = 8,
+ T_SHORT = 9,
+ T_INT = 10,
+ T_LONG = 11;
+
+
+}
diff --git a/src/main/java/org/mozilla/classfile/ClassFileWriter.java b/src/main/java/org/mozilla/classfile/ClassFileWriter.java
new file mode 100644
index 0000000..fcb2ab8
--- /dev/null
+++ b/src/main/java/org/mozilla/classfile/ClassFileWriter.java
@@ -0,0 +1,5188 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.classfile;
+
+import org.mozilla.javascript.ObjToIntMap;
+import org.mozilla.javascript.ObjArray;
+import org.mozilla.javascript.UintMap;
+
+import java.io.*;
+import java.util.Arrays;
+
+/**
+ * ClassFileWriter
+ *
+ * A ClassFileWriter is used to write a Java class file. Methods are
+ * provided to create fields and methods, and within methods to write
+ * Java bytecodes.
+ *
+ * @author Roger Lawrence
+ */
+public class ClassFileWriter {
+
+ /**
+ * Thrown for cases where the error in generating the class file is
+ * due to a program size constraints rather than a likely bug in the
+ * compiler.
+ */
+ public static class ClassFileFormatException extends RuntimeException {
+
+ private static final long serialVersionUID = 1263998431033790599L;
+
+ ClassFileFormatException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * Construct a ClassFileWriter for a class.
+ *
+ * @param className the name of the class to write, including
+ * full package qualification.
+ * @param superClassName the name of the superclass of the class
+ * to write, including full package qualification.
+ * @param sourceFileName the name of the source file to use for
+ * producing debug information, or null if debug information
+ * is not desired
+ */
+ public ClassFileWriter(String className, String superClassName,
+ String sourceFileName)
+ {
+ generatedClassName = className;
+ itsConstantPool = new ConstantPool(this);
+ itsThisClassIndex = itsConstantPool.addClass(className);
+ itsSuperClassIndex = itsConstantPool.addClass(superClassName);
+ if (sourceFileName != null)
+ itsSourceFileNameIndex = itsConstantPool.addUtf8(sourceFileName);
+ // All "new" implementations are supposed to output ACC_SUPER as a
+ // class flag. This is specified in the first JVM spec, so it should
+ // be old enough that it's okay to always set it.
+ itsFlags = ACC_PUBLIC | ACC_SUPER;
+ }
+
+ public final String getClassName()
+ {
+ return generatedClassName;
+ }
+
+ /**
+ * Add an interface implemented by this class.
+ *
+ * This method may be called multiple times for classes that
+ * implement multiple interfaces.
+ *
+ * @param interfaceName a name of an interface implemented
+ * by the class being written, including full package
+ * qualification.
+ */
+ public void addInterface(String interfaceName) {
+ short interfaceIndex = itsConstantPool.addClass(interfaceName);
+ itsInterfaces.add(Short.valueOf(interfaceIndex));
+ }
+
+ public static final short
+ ACC_PUBLIC = 0x0001,
+ ACC_PRIVATE = 0x0002,
+ ACC_PROTECTED = 0x0004,
+ ACC_STATIC = 0x0008,
+ ACC_FINAL = 0x0010,
+ ACC_SUPER = 0x0020,
+ ACC_SYNCHRONIZED = 0x0020,
+ ACC_VOLATILE = 0x0040,
+ ACC_TRANSIENT = 0x0080,
+ ACC_NATIVE = 0x0100,
+ ACC_ABSTRACT = 0x0400;
+
+ /**
+ * Set the class's flags.
+ *
+ * Flags must be a set of the following flags, bitwise or'd
+ * together:
+ * ACC_PUBLIC
+ * ACC_PRIVATE
+ * ACC_PROTECTED
+ * ACC_FINAL
+ * ACC_ABSTRACT
+ * TODO: check that this is the appropriate set
+ * @param flags the set of class flags to set
+ */
+ public void setFlags(short flags) {
+ itsFlags = flags;
+ }
+
+ static String getSlashedForm(String name)
+ {
+ return name.replace('.', '/');
+ }
+
+ /**
+ * Convert Java class name in dot notation into
+ * "Lname-with-dots-replaced-by-slashes;" form suitable for use as
+ * JVM type signatures.
+ */
+ public static String classNameToSignature(String name)
+ {
+ int nameLength = name.length();
+ int colonPos = 1 + nameLength;
+ char[] buf = new char[colonPos + 1];
+ buf[0] = 'L';
+ buf[colonPos] = ';';
+ name.getChars(0, nameLength, buf, 1);
+ for (int i = 1; i != colonPos; ++i) {
+ if (buf[i] == '.') {
+ buf[i] = '/';
+ }
+ }
+ return new String(buf, 0, colonPos + 1);
+ }
+
+ /**
+ * Add a field to the class.
+ *
+ * @param fieldName the name of the field
+ * @param type the type of the field using ...
+ * @param flags the attributes of the field, such as ACC_PUBLIC, etc.
+ * bitwise or'd together
+ */
+ public void addField(String fieldName, String type, short flags) {
+ short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
+ short typeIndex = itsConstantPool.addUtf8(type);
+ itsFields.add(new ClassFileField(fieldNameIndex, typeIndex, flags));
+ }
+
+ /**
+ * Add a field to the class.
+ *
+ * @param fieldName the name of the field
+ * @param type the type of the field using ...
+ * @param flags the attributes of the field, such as ACC_PUBLIC, etc.
+ * bitwise or'd together
+ * @param value an initial integral value
+ */
+ public void addField(String fieldName, String type, short flags,
+ int value)
+ {
+ short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
+ short typeIndex = itsConstantPool.addUtf8(type);
+ ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex,
+ flags);
+ field.setAttributes(itsConstantPool.addUtf8("ConstantValue"),
+ (short)0,
+ (short)0,
+ itsConstantPool.addConstant(value));
+ itsFields.add(field);
+ }
+
+ /**
+ * Add a field to the class.
+ *
+ * @param fieldName the name of the field
+ * @param type the type of the field using ...
+ * @param flags the attributes of the field, such as ACC_PUBLIC, etc.
+ * bitwise or'd together
+ * @param value an initial long value
+ */
+ public void addField(String fieldName, String type, short flags,
+ long value)
+ {
+ short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
+ short typeIndex = itsConstantPool.addUtf8(type);
+ ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex,
+ flags);
+ field.setAttributes(itsConstantPool.addUtf8("ConstantValue"),
+ (short)0,
+ (short)2,
+ itsConstantPool.addConstant(value));
+ itsFields.add(field);
+ }
+
+ /**
+ * Add a field to the class.
+ *
+ * @param fieldName the name of the field
+ * @param type the type of the field using ...
+ * @param flags the attributes of the field, such as ACC_PUBLIC, etc.
+ * bitwise or'd together
+ * @param value an initial double value
+ */
+ public void addField(String fieldName, String type, short flags,
+ double value)
+ {
+ short fieldNameIndex = itsConstantPool.addUtf8(fieldName);
+ short typeIndex = itsConstantPool.addUtf8(type);
+ ClassFileField field = new ClassFileField(fieldNameIndex, typeIndex,
+ flags);
+ field.setAttributes(itsConstantPool.addUtf8("ConstantValue"),
+ (short)0,
+ (short)2,
+ itsConstantPool.addConstant(value));
+ itsFields.add(field);
+ }
+
+ /**
+ * Add Information about java variable to use when generating the local
+ * variable table.
+ *
+ * @param name variable name.
+ * @param type variable type as bytecode descriptor string.
+ * @param startPC the starting bytecode PC where this variable is live,
+ * or -1 if it does not have a Java register.
+ * @param register the Java register number of variable
+ * or -1 if it does not have a Java register.
+ */
+ public void addVariableDescriptor(String name, String type, int startPC, int register)
+ {
+ int nameIndex = itsConstantPool.addUtf8(name);
+ int descriptorIndex = itsConstantPool.addUtf8(type);
+ int [] chunk = { nameIndex, descriptorIndex, startPC, register };
+ if (itsVarDescriptors == null) {
+ itsVarDescriptors = new ObjArray();
+ }
+ itsVarDescriptors.add(chunk);
+ }
+
+ /**
+ * Add a method and begin adding code.
+ *
+ * This method must be called before other methods for adding code,
+ * exception tables, etc. can be invoked.
+ *
+ * @param methodName the name of the method
+ * @param type a string representing the type
+ * @param flags the attributes of the field, such as ACC_PUBLIC, etc.
+ * bitwise or'd together
+ */
+ public void startMethod(String methodName, String type, short flags) {
+ short methodNameIndex = itsConstantPool.addUtf8(methodName);
+ short typeIndex = itsConstantPool.addUtf8(type);
+ itsCurrentMethod = new ClassFileMethod(methodName, methodNameIndex,
+ type, typeIndex, flags);
+ itsJumpFroms = new UintMap();
+ itsMethods.add(itsCurrentMethod);
+ addSuperBlockStart(0);
+ }
+
+ /**
+ * Complete generation of the method.
+ *
+ * After this method is called, no more code can be added to the
+ * method begun with startMethod.
+ *
+ * @param maxLocals the maximum number of local variable slots
+ * (a.k.a. Java registers) used by the method
+ */
+ public void stopMethod(short maxLocals) {
+ if (itsCurrentMethod == null)
+ throw new IllegalStateException("No method to stop");
+
+ fixLabelGotos();
+
+ itsMaxLocals = maxLocals;
+
+ StackMapTable stackMap = null;
+ if (GenerateStackMap) {
+ finalizeSuperBlockStarts();
+ stackMap = new StackMapTable();
+ stackMap.generate();
+ }
+
+ int lineNumberTableLength = 0;
+ if (itsLineNumberTable != null) {
+ // 6 bytes for the attribute header
+ // 2 bytes for the line number count
+ // 4 bytes for each entry
+ lineNumberTableLength = 6 + 2 + (itsLineNumberTableTop * 4);
+ }
+
+ int variableTableLength = 0;
+ if (itsVarDescriptors != null) {
+ // 6 bytes for the attribute header
+ // 2 bytes for the variable count
+ // 10 bytes for each entry
+ variableTableLength = 6 + 2 + (itsVarDescriptors.size() * 10);
+ }
+
+ int stackMapTableLength = 0;
+ if (stackMap != null) {
+ int stackMapWriteSize = stackMap.computeWriteSize();
+ if (stackMapWriteSize > 0) {
+ stackMapTableLength = 6 + stackMapWriteSize;
+ }
+ }
+
+ int attrLength = 2 + // attribute_name_index
+ 4 + // attribute_length
+ 2 + // max_stack
+ 2 + // max_locals
+ 4 + // code_length
+ itsCodeBufferTop +
+ 2 + // exception_table_length
+ (itsExceptionTableTop * 8) +
+ 2 + // attributes_count
+ lineNumberTableLength +
+ variableTableLength +
+ stackMapTableLength;
+
+ if (attrLength > 65536) {
+ // See http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html,
+ // section 4.10, "The amount of code per non-native, non-abstract
+ // method is limited to 65536 bytes...
+ throw new ClassFileFormatException(
+ "generated bytecode for method exceeds 64K limit.");
+ }
+ byte[] codeAttribute = new byte[attrLength];
+ int index = 0;
+ int codeAttrIndex = itsConstantPool.addUtf8("Code");
+ index = putInt16(codeAttrIndex, codeAttribute, index);
+ attrLength -= 6; // discount the attribute header
+ index = putInt32(attrLength, codeAttribute, index);
+ index = putInt16(itsMaxStack, codeAttribute, index);
+ index = putInt16(itsMaxLocals, codeAttribute, index);
+ index = putInt32(itsCodeBufferTop, codeAttribute, index);
+ System.arraycopy(itsCodeBuffer, 0, codeAttribute, index,
+ itsCodeBufferTop);
+ index += itsCodeBufferTop;
+
+ if (itsExceptionTableTop > 0) {
+ index = putInt16(itsExceptionTableTop, codeAttribute, index);
+ for (int i = 0; i < itsExceptionTableTop; i++) {
+ ExceptionTableEntry ete = itsExceptionTable[i];
+ short startPC = (short)getLabelPC(ete.itsStartLabel);
+ short endPC = (short)getLabelPC(ete.itsEndLabel);
+ short handlerPC = (short)getLabelPC(ete.itsHandlerLabel);
+ short catchType = ete.itsCatchType;
+ if (startPC == -1)
+ throw new IllegalStateException("start label not defined");
+ if (endPC == -1)
+ throw new IllegalStateException("end label not defined");
+ if (handlerPC == -1)
+ throw new IllegalStateException(
+ "handler label not defined");
+
+ index = putInt16(startPC, codeAttribute, index);
+ index = putInt16(endPC, codeAttribute, index);
+ index = putInt16(handlerPC, codeAttribute, index);
+ index = putInt16(catchType, codeAttribute, index);
+ }
+ }
+ else {
+ // write 0 as exception table length
+ index = putInt16(0, codeAttribute, index);
+ }
+
+ int attributeCount = 0;
+ if (itsLineNumberTable != null)
+ attributeCount++;
+ if (itsVarDescriptors != null)
+ attributeCount++;
+ if (stackMapTableLength > 0) {
+ attributeCount++;
+ }
+ index = putInt16(attributeCount, codeAttribute, index);
+
+ if (itsLineNumberTable != null) {
+ int lineNumberTableAttrIndex
+ = itsConstantPool.addUtf8("LineNumberTable");
+ index = putInt16(lineNumberTableAttrIndex, codeAttribute, index);
+ int tableAttrLength = 2 + (itsLineNumberTableTop * 4);
+ index = putInt32(tableAttrLength, codeAttribute, index);
+ index = putInt16(itsLineNumberTableTop, codeAttribute, index);
+ for (int i = 0; i < itsLineNumberTableTop; i++) {
+ index = putInt32(itsLineNumberTable[i], codeAttribute, index);
+ }
+ }
+
+ if (itsVarDescriptors != null) {
+ int variableTableAttrIndex
+ = itsConstantPool.addUtf8("LocalVariableTable");
+ index = putInt16(variableTableAttrIndex, codeAttribute, index);
+ int varCount = itsVarDescriptors.size();
+ int tableAttrLength = 2 + (varCount * 10);
+ index = putInt32(tableAttrLength, codeAttribute, index);
+ index = putInt16(varCount, codeAttribute, index);
+ for (int i = 0; i < varCount; i++) {
+ int[] chunk = (int[])itsVarDescriptors.get(i);
+ int nameIndex = chunk[0];
+ int descriptorIndex = chunk[1];
+ int startPC = chunk[2];
+ int register = chunk[3];
+ int length = itsCodeBufferTop - startPC;
+
+ index = putInt16(startPC, codeAttribute, index);
+ index = putInt16(length, codeAttribute, index);
+ index = putInt16(nameIndex, codeAttribute, index);
+ index = putInt16(descriptorIndex, codeAttribute, index);
+ index = putInt16(register, codeAttribute, index);
+ }
+ }
+
+ if (stackMapTableLength > 0) {
+ int stackMapTableAttrIndex =
+ itsConstantPool.addUtf8("StackMapTable");
+ @SuppressWarnings("unused")
+ int start = index;
+ index = putInt16(stackMapTableAttrIndex, codeAttribute, index);
+ index = stackMap.write(codeAttribute, index);
+ }
+
+ itsCurrentMethod.setCodeAttribute(codeAttribute);
+
+ itsExceptionTable = null;
+ itsExceptionTableTop = 0;
+ itsLineNumberTableTop = 0;
+ itsCodeBufferTop = 0;
+ itsCurrentMethod = null;
+ itsMaxStack = 0;
+ itsStackTop = 0;
+ itsLabelTableTop = 0;
+ itsFixupTableTop = 0;
+ itsVarDescriptors = null;
+ itsSuperBlockStarts = null;
+ itsSuperBlockStartsTop = 0;
+ itsJumpFroms = null;
+ }
+
+ /**
+ * Add the single-byte opcode to the current method.
+ *
+ * @param theOpCode the opcode of the bytecode
+ */
+ public void add(int theOpCode) {
+ if (opcodeCount(theOpCode) != 0)
+ throw new IllegalArgumentException("Unexpected operands");
+ int newStack = itsStackTop + stackChange(theOpCode);
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+ if (DEBUGCODE)
+ System.out.println("Add " + bytecodeStr(theOpCode));
+ addToCodeBuffer(theOpCode);
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(theOpCode)
+ +" stack = "+itsStackTop);
+ }
+ if (theOpCode == ByteCode.ATHROW) {
+ addSuperBlockStart(itsCodeBufferTop);
+ }
+ }
+
+ /**
+ * Add a single-operand opcode to the current method.
+ *
+ * @param theOpCode the opcode of the bytecode
+ * @param theOperand the operand of the bytecode
+ */
+ public void add(int theOpCode, int theOperand) {
+ if (DEBUGCODE) {
+ System.out.println("Add "+bytecodeStr(theOpCode)
+ +", "+Integer.toHexString(theOperand));
+ }
+ int newStack = itsStackTop + stackChange(theOpCode);
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+
+ switch (theOpCode) {
+ case ByteCode.GOTO :
+ // This is necessary because dead code is seemingly being
+ // generated and Sun's verifier is expecting type state to be
+ // placed even at dead blocks of code.
+ addSuperBlockStart(itsCodeBufferTop + 3);
+ // fallthru...
+ case ByteCode.IFEQ :
+ case ByteCode.IFNE :
+ case ByteCode.IFLT :
+ case ByteCode.IFGE :
+ case ByteCode.IFGT :
+ case ByteCode.IFLE :
+ case ByteCode.IF_ICMPEQ :
+ case ByteCode.IF_ICMPNE :
+ case ByteCode.IF_ICMPLT :
+ case ByteCode.IF_ICMPGE :
+ case ByteCode.IF_ICMPGT :
+ case ByteCode.IF_ICMPLE :
+ case ByteCode.IF_ACMPEQ :
+ case ByteCode.IF_ACMPNE :
+ case ByteCode.JSR :
+ case ByteCode.IFNULL :
+ case ByteCode.IFNONNULL : {
+ if ((theOperand & 0x80000000) != 0x80000000) {
+ if ((theOperand < 0) || (theOperand > 65535))
+ throw new IllegalArgumentException(
+ "Bad label for branch");
+ }
+ int branchPC = itsCodeBufferTop;
+ addToCodeBuffer(theOpCode);
+ if ((theOperand & 0x80000000) != 0x80000000) {
+ // hard displacement
+ addToCodeInt16(theOperand);
+ int target = theOperand + branchPC;
+ addSuperBlockStart(target);
+ itsJumpFroms.put(target, branchPC);
+ }
+ else { // a label
+ int targetPC = getLabelPC(theOperand);
+ if (DEBUGLABELS) {
+ int theLabel = theOperand & 0x7FFFFFFF;
+ System.out.println("Fixing branch to " +
+ theLabel + " at " + targetPC +
+ " from " + branchPC);
+ }
+ if (targetPC != -1) {
+ int offset = targetPC - branchPC;
+ addToCodeInt16(offset);
+ addSuperBlockStart(targetPC);
+ itsJumpFroms.put(targetPC, branchPC);
+ }
+ else {
+ addLabelFixup(theOperand, branchPC + 1);
+ addToCodeInt16(0);
+ }
+ }
+ }
+ break;
+
+ case ByteCode.BIPUSH :
+ if ((byte)theOperand != theOperand)
+ throw new IllegalArgumentException("out of range byte");
+ addToCodeBuffer(theOpCode);
+ addToCodeBuffer((byte)theOperand);
+ break;
+
+ case ByteCode.SIPUSH :
+ if ((short)theOperand != theOperand)
+ throw new IllegalArgumentException("out of range short");
+ addToCodeBuffer(theOpCode);
+ addToCodeInt16(theOperand);
+ break;
+
+ case ByteCode.NEWARRAY :
+ if (!(0 <= theOperand && theOperand < 256))
+ throw new IllegalArgumentException("out of range index");
+ addToCodeBuffer(theOpCode);
+ addToCodeBuffer(theOperand);
+ break;
+
+ case ByteCode.GETFIELD :
+ case ByteCode.PUTFIELD :
+ if (!(0 <= theOperand && theOperand < 65536))
+ throw new IllegalArgumentException("out of range field");
+ addToCodeBuffer(theOpCode);
+ addToCodeInt16(theOperand);
+ break;
+
+ case ByteCode.LDC :
+ case ByteCode.LDC_W :
+ case ByteCode.LDC2_W :
+ if (!(0 <= theOperand && theOperand < 65536))
+ throw new IllegalArgumentException("out of range index");
+ if (theOperand >= 256
+ || theOpCode == ByteCode.LDC_W
+ || theOpCode == ByteCode.LDC2_W)
+ {
+ if (theOpCode == ByteCode.LDC) {
+ addToCodeBuffer(ByteCode.LDC_W);
+ } else {
+ addToCodeBuffer(theOpCode);
+ }
+ addToCodeInt16(theOperand);
+ } else {
+ addToCodeBuffer(theOpCode);
+ addToCodeBuffer(theOperand);
+ }
+ break;
+
+ case ByteCode.RET :
+ case ByteCode.ILOAD :
+ case ByteCode.LLOAD :
+ case ByteCode.FLOAD :
+ case ByteCode.DLOAD :
+ case ByteCode.ALOAD :
+ case ByteCode.ISTORE :
+ case ByteCode.LSTORE :
+ case ByteCode.FSTORE :
+ case ByteCode.DSTORE :
+ case ByteCode.ASTORE :
+ if (!(0 <= theOperand && theOperand < 65536))
+ throw new ClassFileFormatException("out of range variable");
+ if (theOperand >= 256) {
+ addToCodeBuffer(ByteCode.WIDE);
+ addToCodeBuffer(theOpCode);
+ addToCodeInt16(theOperand);
+ }
+ else {
+ addToCodeBuffer(theOpCode);
+ addToCodeBuffer(theOperand);
+ }
+ break;
+
+ default :
+ throw new IllegalArgumentException(
+ "Unexpected opcode for 1 operand");
+ }
+
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(theOpCode)
+ +" stack = "+itsStackTop);
+ }
+ }
+
+ /**
+ * Generate the load constant bytecode for the given integer.
+ *
+ * @param k the constant
+ */
+ public void addLoadConstant(int k) {
+ switch (k) {
+ case 0: add(ByteCode.ICONST_0); break;
+ case 1: add(ByteCode.ICONST_1); break;
+ case 2: add(ByteCode.ICONST_2); break;
+ case 3: add(ByteCode.ICONST_3); break;
+ case 4: add(ByteCode.ICONST_4); break;
+ case 5: add(ByteCode.ICONST_5); break;
+ default:
+ add(ByteCode.LDC, itsConstantPool.addConstant(k));
+ break;
+ }
+ }
+
+ /**
+ * Generate the load constant bytecode for the given long.
+ *
+ * @param k the constant
+ */
+ public void addLoadConstant(long k) {
+ add(ByteCode.LDC2_W, itsConstantPool.addConstant(k));
+ }
+
+ /**
+ * Generate the load constant bytecode for the given float.
+ *
+ * @param k the constant
+ */
+ public void addLoadConstant(float k) {
+ add(ByteCode.LDC, itsConstantPool.addConstant(k));
+ }
+
+ /**
+ * Generate the load constant bytecode for the given double.
+ *
+ * @param k the constant
+ */
+ public void addLoadConstant(double k) {
+ add(ByteCode.LDC2_W, itsConstantPool.addConstant(k));
+ }
+
+ /**
+ * Generate the load constant bytecode for the given string.
+ *
+ * @param k the constant
+ */
+ public void addLoadConstant(String k) {
+ add(ByteCode.LDC, itsConstantPool.addConstant(k));
+ }
+
+ /**
+ * Add the given two-operand bytecode to the current method.
+ *
+ * @param theOpCode the opcode of the bytecode
+ * @param theOperand1 the first operand of the bytecode
+ * @param theOperand2 the second operand of the bytecode
+ */
+ public void add(int theOpCode, int theOperand1, int theOperand2) {
+ if (DEBUGCODE) {
+ System.out.println("Add "+bytecodeStr(theOpCode)
+ +", "+Integer.toHexString(theOperand1)
+ +", "+Integer.toHexString(theOperand2));
+ }
+ int newStack = itsStackTop + stackChange(theOpCode);
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+
+ if (theOpCode == ByteCode.IINC) {
+ if (!(0 <= theOperand1 && theOperand1 < 65536))
+ throw new ClassFileFormatException("out of range variable");
+ if (!(0 <= theOperand2 && theOperand2 < 65536))
+ throw new ClassFileFormatException("out of range increment");
+
+ if (theOperand1 > 255 || theOperand2 < -128 || theOperand2 > 127) {
+ addToCodeBuffer(ByteCode.WIDE);
+ addToCodeBuffer(ByteCode.IINC);
+ addToCodeInt16(theOperand1);
+ addToCodeInt16(theOperand2);
+ }
+ else {
+ addToCodeBuffer(ByteCode.IINC);
+ addToCodeBuffer(theOperand1);
+ addToCodeBuffer(theOperand2);
+ }
+ }
+ else if (theOpCode == ByteCode.MULTIANEWARRAY) {
+ if (!(0 <= theOperand1 && theOperand1 < 65536))
+ throw new IllegalArgumentException("out of range index");
+ if (!(0 <= theOperand2 && theOperand2 < 256))
+ throw new IllegalArgumentException("out of range dimensions");
+
+ addToCodeBuffer(ByteCode.MULTIANEWARRAY);
+ addToCodeInt16(theOperand1);
+ addToCodeBuffer(theOperand2);
+ }
+ else {
+ throw new IllegalArgumentException(
+ "Unexpected opcode for 2 operands");
+ }
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(theOpCode)
+ +" stack = "+itsStackTop);
+ }
+
+ }
+
+ public void add(int theOpCode, String className) {
+ if (DEBUGCODE) {
+ System.out.println("Add "+bytecodeStr(theOpCode)
+ +", "+className);
+ }
+ int newStack = itsStackTop + stackChange(theOpCode);
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+ switch (theOpCode) {
+ case ByteCode.NEW :
+ case ByteCode.ANEWARRAY :
+ case ByteCode.CHECKCAST :
+ case ByteCode.INSTANCEOF : {
+ short classIndex = itsConstantPool.addClass(className);
+ addToCodeBuffer(theOpCode);
+ addToCodeInt16(classIndex);
+ }
+ break;
+
+ default :
+ throw new IllegalArgumentException(
+ "bad opcode for class reference");
+ }
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(theOpCode)
+ +" stack = "+itsStackTop);
+ }
+ }
+
+
+ public void add(int theOpCode, String className, String fieldName,
+ String fieldType)
+ {
+ if (DEBUGCODE) {
+ System.out.println("Add "+bytecodeStr(theOpCode)
+ +", "+className+", "+fieldName+", "+fieldType);
+ }
+ int newStack = itsStackTop + stackChange(theOpCode);
+ char fieldTypeChar = fieldType.charAt(0);
+ int fieldSize = (fieldTypeChar == 'J' || fieldTypeChar == 'D')
+ ? 2 : 1;
+ switch (theOpCode) {
+ case ByteCode.GETFIELD :
+ case ByteCode.GETSTATIC :
+ newStack += fieldSize;
+ break;
+ case ByteCode.PUTSTATIC :
+ case ByteCode.PUTFIELD :
+ newStack -= fieldSize;
+ break;
+ default :
+ throw new IllegalArgumentException(
+ "bad opcode for field reference");
+ }
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+ short fieldRefIndex = itsConstantPool.addFieldRef(className,
+ fieldName, fieldType);
+ addToCodeBuffer(theOpCode);
+ addToCodeInt16(fieldRefIndex);
+
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(theOpCode)
+ +" stack = "+itsStackTop);
+ }
+ }
+
+ public void addInvoke(int theOpCode, String className, String methodName,
+ String methodType)
+ {
+ if (DEBUGCODE) {
+ System.out.println("Add "+bytecodeStr(theOpCode)
+ +", "+className+", "+methodName+", "
+ +methodType);
+ }
+ int parameterInfo = sizeOfParameters(methodType);
+ int parameterCount = parameterInfo >>> 16;
+ int stackDiff = (short)parameterInfo;
+
+ int newStack = itsStackTop + stackDiff;
+ newStack += stackChange(theOpCode); // adjusts for 'this'
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+
+ switch (theOpCode) {
+ case ByteCode.INVOKEVIRTUAL :
+ case ByteCode.INVOKESPECIAL :
+ case ByteCode.INVOKESTATIC :
+ case ByteCode.INVOKEINTERFACE : {
+ addToCodeBuffer(theOpCode);
+ if (theOpCode == ByteCode.INVOKEINTERFACE) {
+ short ifMethodRefIndex
+ = itsConstantPool.addInterfaceMethodRef(
+ className, methodName,
+ methodType);
+ addToCodeInt16(ifMethodRefIndex);
+ addToCodeBuffer(parameterCount + 1);
+ addToCodeBuffer(0);
+ }
+ else {
+ short methodRefIndex = itsConstantPool.addMethodRef(
+ className, methodName,
+ methodType);
+ addToCodeInt16(methodRefIndex);
+ }
+ }
+ break;
+
+ default :
+ throw new IllegalArgumentException(
+ "bad opcode for method reference");
+ }
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(theOpCode)
+ +" stack = "+itsStackTop);
+ }
+ }
+
+ /**
+ * Generate code to load the given integer on stack.
+ *
+ * @param k the constant
+ */
+ public void addPush(int k)
+ {
+ if ((byte)k == k) {
+ if (k == -1) {
+ add(ByteCode.ICONST_M1);
+ } else if (0 <= k && k <= 5) {
+ add((byte)(ByteCode.ICONST_0 + k));
+ } else {
+ add(ByteCode.BIPUSH, (byte)k);
+ }
+ } else if ((short)k == k) {
+ add(ByteCode.SIPUSH, (short)k);
+ } else {
+ addLoadConstant(k);
+ }
+ }
+
+ public void addPush(boolean k)
+ {
+ add(k ? ByteCode.ICONST_1 : ByteCode.ICONST_0);
+ }
+
+ /**
+ * Generate code to load the given long on stack.
+ *
+ * @param k the constant
+ */
+ public void addPush(long k)
+ {
+ int ik = (int)k;
+ if (ik == k) {
+ addPush(ik);
+ add(ByteCode.I2L);
+ } else {
+ addLoadConstant(k);
+ }
+ }
+
+ /**
+ * Generate code to load the given double on stack.
+ *
+ * @param k the constant
+ */
+ public void addPush(double k)
+ {
+ if (k == 0.0) {
+ // zero
+ add(ByteCode.DCONST_0);
+ if (1.0 / k < 0) {
+ // Negative zero
+ add(ByteCode.DNEG);
+ }
+ } else if (k == 1.0 || k == -1.0) {
+ add(ByteCode.DCONST_1);
+ if (k < 0) {
+ add(ByteCode.DNEG);
+ }
+ } else {
+ addLoadConstant(k);
+ }
+ }
+
+ /**
+ * Generate the code to leave on stack the given string even if the
+ * string encoding exeeds the class file limit for single string constant
+ *
+ * @param k the constant
+ */
+ public void addPush(String k) {
+ int length = k.length();
+ int limit = itsConstantPool.getUtfEncodingLimit(k, 0, length);
+ if (limit == length) {
+ addLoadConstant(k);
+ return;
+ }
+ // Split string into picies fitting the UTF limit and generate code for
+ // StringBuffer sb = new StringBuffer(length);
+ // sb.append(loadConstant(piece_1));
+ // ...
+ // sb.append(loadConstant(piece_N));
+ // sb.toString();
+ final String SB = "java/lang/StringBuffer";
+ add(ByteCode.NEW, SB);
+ add(ByteCode.DUP);
+ addPush(length);
+ addInvoke(ByteCode.INVOKESPECIAL, SB, "", "(I)V");
+ int cursor = 0;
+ for (;;) {
+ add(ByteCode.DUP);
+ String s = k.substring(cursor, limit);
+ addLoadConstant(s);
+ addInvoke(ByteCode.INVOKEVIRTUAL, SB, "append",
+ "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
+ add(ByteCode.POP);
+ if (limit == length) {
+ break;
+ }
+ cursor = limit;
+ limit = itsConstantPool.getUtfEncodingLimit(k, limit, length);
+ }
+ addInvoke(ByteCode.INVOKEVIRTUAL, SB, "toString",
+ "()Ljava/lang/String;");
+ }
+
+ /**
+ * Check if k fits limit on string constant size imposed by class file
+ * format.
+ *
+ * @param k the string constant
+ */
+ public boolean isUnderStringSizeLimit(String k)
+ {
+ return itsConstantPool.isUnderUtfEncodingLimit(k);
+ }
+
+ /**
+ * Store integer from stack top into the given local.
+ *
+ * @param local number of local register
+ */
+ public void addIStore(int local)
+ {
+ xop(ByteCode.ISTORE_0, ByteCode.ISTORE, local);
+ }
+
+ /**
+ * Store long from stack top into the given local.
+ *
+ * @param local number of local register
+ */
+ public void addLStore(int local)
+ {
+ xop(ByteCode.LSTORE_0, ByteCode.LSTORE, local);
+ }
+
+ /**
+ * Store float from stack top into the given local.
+ *
+ * @param local number of local register
+ */
+ public void addFStore(int local)
+ {
+ xop(ByteCode.FSTORE_0, ByteCode.FSTORE, local);
+ }
+
+ /**
+ * Store double from stack top into the given local.
+ *
+ * @param local number of local register
+ */
+ public void addDStore(int local)
+ {
+ xop(ByteCode.DSTORE_0, ByteCode.DSTORE, local);
+ }
+
+ /**
+ * Store object from stack top into the given local.
+ *
+ * @param local number of local register
+ */
+ public void addAStore(int local)
+ {
+ xop(ByteCode.ASTORE_0, ByteCode.ASTORE, local);
+ }
+
+ /**
+ * Load integer from the given local into stack.
+ *
+ * @param local number of local register
+ */
+ public void addILoad(int local)
+ {
+ xop(ByteCode.ILOAD_0, ByteCode.ILOAD, local);
+ }
+
+ /**
+ * Load long from the given local into stack.
+ *
+ * @param local number of local register
+ */
+ public void addLLoad(int local)
+ {
+ xop(ByteCode.LLOAD_0, ByteCode.LLOAD, local);
+ }
+
+ /**
+ * Load float from the given local into stack.
+ *
+ * @param local number of local register
+ */
+ public void addFLoad(int local)
+ {
+ xop(ByteCode.FLOAD_0, ByteCode.FLOAD, local);
+ }
+
+ /**
+ * Load double from the given local into stack.
+ *
+ * @param local number of local register
+ */
+ public void addDLoad(int local)
+ {
+ xop(ByteCode.DLOAD_0, ByteCode.DLOAD, local);
+ }
+
+ /**
+ * Load object from the given local into stack.
+ *
+ * @param local number of local register
+ */
+ public void addALoad(int local)
+ {
+ xop(ByteCode.ALOAD_0, ByteCode.ALOAD, local);
+ }
+
+ /**
+ * Load "this" into stack.
+ */
+ public void addLoadThis()
+ {
+ add(ByteCode.ALOAD_0);
+ }
+
+ private void xop(int shortOp, int op, int local)
+ {
+ switch (local) {
+ case 0:
+ add(shortOp);
+ break;
+ case 1:
+ add(shortOp + 1);
+ break;
+ case 2:
+ add(shortOp + 2);
+ break;
+ case 3:
+ add(shortOp + 3);
+ break;
+ default:
+ add(op, local);
+ }
+ }
+
+ public int addTableSwitch(int low, int high)
+ {
+ if (DEBUGCODE) {
+ System.out.println("Add "+bytecodeStr(ByteCode.TABLESWITCH)
+ +" "+low+" "+high);
+ }
+ if (low > high)
+ throw new ClassFileFormatException("Bad bounds: "+low+' '+ high);
+
+ int newStack = itsStackTop + stackChange(ByteCode.TABLESWITCH);
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+
+ int entryCount = high - low + 1;
+ int padSize = 3 & ~itsCodeBufferTop; // == 3 - itsCodeBufferTop % 4
+
+ int N = addReservedCodeSpace(1 + padSize + 4 * (1 + 2 + entryCount));
+ int switchStart = N;
+ itsCodeBuffer[N++] = (byte)ByteCode.TABLESWITCH;
+ while (padSize != 0) {
+ itsCodeBuffer[N++] = 0;
+ --padSize;
+ }
+ N += 4; // skip default offset
+ N = putInt32(low, itsCodeBuffer, N);
+ putInt32(high, itsCodeBuffer, N);
+
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+bytecodeStr(ByteCode.TABLESWITCH)
+ +" stack = "+itsStackTop);
+ }
+
+ return switchStart;
+ }
+
+ public final void markTableSwitchDefault(int switchStart)
+ {
+ addSuperBlockStart(itsCodeBufferTop);
+ itsJumpFroms.put(itsCodeBufferTop, switchStart);
+ setTableSwitchJump(switchStart, -1, itsCodeBufferTop);
+ }
+
+ public final void markTableSwitchCase(int switchStart, int caseIndex)
+ {
+ addSuperBlockStart(itsCodeBufferTop);
+ itsJumpFroms.put(itsCodeBufferTop, switchStart);
+ setTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop);
+ }
+
+ public final void markTableSwitchCase(int switchStart, int caseIndex,
+ int stackTop)
+ {
+ if (!(0 <= stackTop && stackTop <= itsMaxStack))
+ throw new IllegalArgumentException("Bad stack index: "+stackTop);
+ itsStackTop = (short)stackTop;
+ addSuperBlockStart(itsCodeBufferTop);
+ itsJumpFroms.put(itsCodeBufferTop, switchStart);
+ setTableSwitchJump(switchStart, caseIndex, itsCodeBufferTop);
+ }
+
+ /**
+ * Set a jump case for a tableswitch instruction. The jump target should
+ * be marked as a super block start for stack map generation.
+ */
+ public void setTableSwitchJump(int switchStart, int caseIndex,
+ int jumpTarget)
+ {
+ if (!(0 <= jumpTarget && jumpTarget <= itsCodeBufferTop))
+ throw new IllegalArgumentException("Bad jump target: "+jumpTarget);
+ if (!(caseIndex >= -1))
+ throw new IllegalArgumentException("Bad case index: "+caseIndex);
+
+ int padSize = 3 & ~switchStart; // == 3 - switchStart % 4
+ int caseOffset;
+ if (caseIndex < 0) {
+ // default label
+ caseOffset = switchStart + 1 + padSize;
+ } else {
+ caseOffset = switchStart + 1 + padSize + 4 * (3 + caseIndex);
+ }
+ if (!(0 <= switchStart
+ && switchStart <= itsCodeBufferTop - 4 * 4 - padSize - 1))
+ {
+ throw new IllegalArgumentException(
+ switchStart+" is outside a possible range of tableswitch"
+ +" in already generated code");
+ }
+ if ((0xFF & itsCodeBuffer[switchStart]) != ByteCode.TABLESWITCH) {
+ throw new IllegalArgumentException(
+ switchStart+" is not offset of tableswitch statement");
+ }
+ if (!(0 <= caseOffset && caseOffset + 4 <= itsCodeBufferTop)) {
+ // caseIndex >= -1 does not guarantee that caseOffset >= 0 due
+ // to a possible overflow.
+ throw new ClassFileFormatException(
+ "Too big case index: "+caseIndex);
+ }
+ // ALERT: perhaps check against case bounds?
+ putInt32(jumpTarget - switchStart, itsCodeBuffer, caseOffset);
+ }
+
+ public int acquireLabel()
+ {
+ int top = itsLabelTableTop;
+ if (itsLabelTable == null || top == itsLabelTable.length) {
+ if (itsLabelTable == null) {
+ itsLabelTable = new int[MIN_LABEL_TABLE_SIZE];
+ }else {
+ int[] tmp = new int[itsLabelTable.length * 2];
+ System.arraycopy(itsLabelTable, 0, tmp, 0, top);
+ itsLabelTable = tmp;
+ }
+ }
+ itsLabelTableTop = top + 1;
+ itsLabelTable[top] = -1;
+ return top | 0x80000000;
+ }
+
+ public void markLabel(int label)
+ {
+ if (!(label < 0))
+ throw new IllegalArgumentException("Bad label, no biscuit");
+
+ label &= 0x7FFFFFFF;
+ if (label > itsLabelTableTop)
+ throw new IllegalArgumentException("Bad label");
+
+ if (itsLabelTable[label] != -1) {
+ throw new IllegalStateException("Can only mark label once");
+ }
+
+ itsLabelTable[label] = itsCodeBufferTop;
+ }
+
+ public void markLabel(int label, short stackTop)
+ {
+ markLabel(label);
+ itsStackTop = stackTop;
+ }
+
+ public void markHandler(int theLabel) {
+ itsStackTop = 1;
+ markLabel(theLabel);
+ }
+
+ public int getLabelPC(int label)
+ {
+ if (!(label < 0))
+ throw new IllegalArgumentException("Bad label, no biscuit");
+ label &= 0x7FFFFFFF;
+ if (!(label < itsLabelTableTop))
+ throw new IllegalArgumentException("Bad label");
+ return itsLabelTable[label];
+ }
+
+ private void addLabelFixup(int label, int fixupSite)
+ {
+ if (!(label < 0))
+ throw new IllegalArgumentException("Bad label, no biscuit");
+ label &= 0x7FFFFFFF;
+ if (!(label < itsLabelTableTop))
+ throw new IllegalArgumentException("Bad label");
+ int top = itsFixupTableTop;
+ if (itsFixupTable == null || top == itsFixupTable.length) {
+ if (itsFixupTable == null) {
+ itsFixupTable = new long[MIN_FIXUP_TABLE_SIZE];
+ }else {
+ long[] tmp = new long[itsFixupTable.length * 2];
+ System.arraycopy(itsFixupTable, 0, tmp, 0, top);
+ itsFixupTable = tmp;
+ }
+ }
+ itsFixupTableTop = top + 1;
+ itsFixupTable[top] = ((long)label << 32) | fixupSite;
+ }
+
+ private void fixLabelGotos()
+ {
+ byte[] codeBuffer = itsCodeBuffer;
+ for (int i = 0; i < itsFixupTableTop; i++) {
+ long fixup = itsFixupTable[i];
+ int label = (int)(fixup >> 32);
+ int fixupSite = (int)fixup;
+ int pc = itsLabelTable[label];
+ if (pc == -1) {
+ // Unlocated label
+ throw new RuntimeException();
+ }
+ // -1 to get delta from instruction start
+ addSuperBlockStart(pc);
+ itsJumpFroms.put(pc, fixupSite - 1);
+ int offset = pc - (fixupSite - 1);
+ if ((short)offset != offset) {
+ throw new ClassFileFormatException
+ ("Program too complex: too big jump offset");
+ }
+ codeBuffer[fixupSite] = (byte)(offset >> 8);
+ codeBuffer[fixupSite + 1] = (byte)offset;
+ }
+ itsFixupTableTop = 0;
+ }
+
+ /**
+ * Get the current offset into the code of the current method.
+ *
+ * @return an integer representing the offset
+ */
+ public int getCurrentCodeOffset() {
+ return itsCodeBufferTop;
+ }
+
+ public short getStackTop() {
+ return itsStackTop;
+ }
+
+ public void setStackTop(short n) {
+ itsStackTop = n;
+ }
+
+ public void adjustStackTop(int delta) {
+ int newStack = itsStackTop + delta;
+ if (newStack < 0 || Short.MAX_VALUE < newStack) badStack(newStack);
+ itsStackTop = (short)newStack;
+ if (newStack > itsMaxStack) itsMaxStack = (short)newStack;
+ if (DEBUGSTACK) {
+ System.out.println("After "+"adjustStackTop("+delta+")"
+ +" stack = "+itsStackTop);
+ }
+ }
+
+ private void addToCodeBuffer(int b)
+ {
+ int N = addReservedCodeSpace(1);
+ itsCodeBuffer[N] = (byte)b;
+ }
+
+ private void addToCodeInt16(int value)
+ {
+ int N = addReservedCodeSpace(2);
+ putInt16(value, itsCodeBuffer, N);
+ }
+
+ private int addReservedCodeSpace(int size)
+ {
+ if (itsCurrentMethod == null)
+ throw new IllegalArgumentException("No method to add to");
+ int oldTop = itsCodeBufferTop;
+ int newTop = oldTop + size;
+ if (newTop > itsCodeBuffer.length) {
+ int newSize = itsCodeBuffer.length * 2;
+ if (newTop > newSize) { newSize = newTop; }
+ byte[] tmp = new byte[newSize];
+ System.arraycopy(itsCodeBuffer, 0, tmp, 0, oldTop);
+ itsCodeBuffer = tmp;
+ }
+ itsCodeBufferTop = newTop;
+ return oldTop;
+ }
+
+ public void addExceptionHandler(int startLabel, int endLabel,
+ int handlerLabel, String catchClassName)
+ {
+ if ((startLabel & 0x80000000) != 0x80000000)
+ throw new IllegalArgumentException("Bad startLabel");
+ if ((endLabel & 0x80000000) != 0x80000000)
+ throw new IllegalArgumentException("Bad endLabel");
+ if ((handlerLabel & 0x80000000) != 0x80000000)
+ throw new IllegalArgumentException("Bad handlerLabel");
+
+ /*
+ * If catchClassName is null, use 0 for the catch_type_index; which
+ * means catch everything. (Even when the verifier has let you throw
+ * something other than a Throwable.)
+ */
+ short catch_type_index = (catchClassName == null)
+ ? 0
+ : itsConstantPool.addClass(catchClassName);
+ ExceptionTableEntry newEntry = new ExceptionTableEntry(
+ startLabel,
+ endLabel,
+ handlerLabel,
+ catch_type_index);
+ int N = itsExceptionTableTop;
+ if (N == 0) {
+ itsExceptionTable = new ExceptionTableEntry[ExceptionTableSize];
+ } else if (N == itsExceptionTable.length) {
+ ExceptionTableEntry[] tmp = new ExceptionTableEntry[N * 2];
+ System.arraycopy(itsExceptionTable, 0, tmp, 0, N);
+ itsExceptionTable = tmp;
+ }
+ itsExceptionTable[N] = newEntry;
+ itsExceptionTableTop = N + 1;
+
+ }
+
+ public void addLineNumberEntry(short lineNumber) {
+ if (itsCurrentMethod == null)
+ throw new IllegalArgumentException("No method to stop");
+ int N = itsLineNumberTableTop;
+ if (N == 0) {
+ itsLineNumberTable = new int[LineNumberTableSize];
+ } else if (N == itsLineNumberTable.length) {
+ int[] tmp = new int[N * 2];
+ System.arraycopy(itsLineNumberTable, 0, tmp, 0, N);
+ itsLineNumberTable = tmp;
+ }
+ itsLineNumberTable[N] = (itsCodeBufferTop << 16) + lineNumber;
+ itsLineNumberTableTop = N + 1;
+ }
+
+ /**
+ * A stack map table is a code attribute introduced in Java 6 that
+ * gives type information at key points in the method body (namely, at
+ * the beginning of each super block after the first). Each frame of a
+ * stack map table contains the state of local variable and operand stack
+ * for a given super block.
+ */
+ final class StackMapTable {
+ StackMapTable() {
+ superBlocks = null;
+ locals = stack = null;
+ workList = null;
+ rawStackMap = null;
+ localsTop = 0;
+ stackTop = 0;
+ workListTop = 0;
+ rawStackMapTop = 0;
+ wide = false;
+ }
+
+ void generate() {
+ superBlocks = new SuperBlock[itsSuperBlockStartsTop];
+ int[] initialLocals = createInitialLocals();
+
+ for (int i = 0; i < itsSuperBlockStartsTop; i++) {
+ int start = itsSuperBlockStarts[i];
+ int end;
+ if (i == itsSuperBlockStartsTop - 1) {
+ end = itsCodeBufferTop;
+ } else {
+ end = itsSuperBlockStarts[i + 1];
+ }
+ superBlocks[i] = new SuperBlock(i, start, end, initialLocals);
+ }
+
+ if (DEBUGSTACKMAP) {
+ System.out.println("super blocks: ");
+ for (int i = 0;
+ i < superBlocks.length && superBlocks[i] != null; i++) {
+ System.out.println("sb " + i + ": [" +
+ superBlocks[i].getStart() + ", " +
+ superBlocks[i].getEnd() + ")");
+ }
+ }
+
+ superBlockDeps = getSuperBlockDependencies();
+
+ verify();
+
+ if (DEBUGSTACKMAP) {
+ System.out.println("type information:");
+ for (int i = 0; i < superBlocks.length; i++) {
+ SuperBlock sb = superBlocks[i];
+ System.out.println("sb " + i + ":");
+ TypeInfo.print(sb.getLocals(), sb.getStack(),
+ itsConstantPool);
+ }
+ }
+ }
+
+ private SuperBlock getSuperBlockFromOffset(int offset) {
+ for (int i = 0; i < superBlocks.length; i++) {
+ SuperBlock sb = superBlocks[i];
+ if (sb == null) {
+ break;
+ } else if (offset >= sb.getStart() && offset < sb.getEnd()) {
+ return sb;
+ }
+ }
+ throw new IllegalArgumentException("bad offset: " + offset);
+ }
+
+ /**
+ * Determine whether or not an opcode is an actual end to a super
+ * block. This includes any returns or unconditional jumps.
+ */
+ private boolean isSuperBlockEnd(int opcode) {
+ switch (opcode) {
+ case ByteCode.ARETURN:
+ case ByteCode.FRETURN:
+ case ByteCode.IRETURN:
+ case ByteCode.LRETURN:
+ case ByteCode.RETURN:
+ case ByteCode.ATHROW:
+ case ByteCode.GOTO:
+ case ByteCode.GOTO_W:
+ case ByteCode.TABLESWITCH:
+ case ByteCode.LOOKUPSWITCH:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Calculate partial dependencies for super blocks.
+ *
+ * This is used as a workaround for dead code that is generated. Only
+ * one dependency per super block is given.
+ */
+ private SuperBlock[] getSuperBlockDependencies() {
+ SuperBlock[] deps = new SuperBlock[superBlocks.length];
+
+ for (int i = 0; i < itsExceptionTableTop; i++) {
+ ExceptionTableEntry ete = itsExceptionTable[i];
+ short startPC = (short) getLabelPC(ete.itsStartLabel);
+ short handlerPC = (short) getLabelPC(ete.itsHandlerLabel);
+ SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC);
+ SuperBlock dep = getSuperBlockFromOffset(startPC);
+ deps[handlerSB.getIndex()] = dep;
+ }
+ int[] targetPCs = itsJumpFroms.getKeys();
+ for (int i = 0; i < targetPCs.length; i++) {
+ int targetPC = targetPCs[i];
+ int branchPC = itsJumpFroms.getInt(targetPC, -1);
+ SuperBlock branchSB = getSuperBlockFromOffset(branchPC);
+ SuperBlock targetSB = getSuperBlockFromOffset(targetPC);
+ deps[targetSB.getIndex()] = branchSB;
+ }
+
+ return deps;
+ }
+
+ /**
+ * Get the target super block of a branch instruction.
+ *
+ * @param bci the index of the branch instruction in the code buffer
+ */
+ private SuperBlock getBranchTarget(int bci) {
+ int target;
+ if ((itsCodeBuffer[bci] & 0xFF) == ByteCode.GOTO_W) {
+ target = bci + getOperand(bci + 1, 4);
+ } else {
+ target = bci + (short) getOperand(bci + 1, 2);
+ }
+ return getSuperBlockFromOffset(target);
+ }
+
+ /**
+ * Determine whether or not an opcode is a conditional or unconditional
+ * jump.
+ */
+ private boolean isBranch(int opcode) {
+ switch (opcode) {
+ case ByteCode.GOTO:
+ case ByteCode.GOTO_W:
+ case ByteCode.IFEQ:
+ case ByteCode.IFGE:
+ case ByteCode.IFGT:
+ case ByteCode.IFLE:
+ case ByteCode.IFLT:
+ case ByteCode.IFNE:
+ case ByteCode.IFNONNULL:
+ case ByteCode.IFNULL:
+ case ByteCode.IF_ACMPEQ:
+ case ByteCode.IF_ACMPNE:
+ case ByteCode.IF_ICMPEQ:
+ case ByteCode.IF_ICMPGE:
+ case ByteCode.IF_ICMPGT:
+ case ByteCode.IF_ICMPLE:
+ case ByteCode.IF_ICMPLT:
+ case ByteCode.IF_ICMPNE:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private int getOperand(int offset) {
+ return getOperand(offset, 1);
+ }
+
+ /**
+ * Extract a logical operand from the byte code.
+ *
+ * This is used, for example, to get branch offsets.
+ */
+ private int getOperand(int start, int size) {
+ int result = 0;
+ if (size > 4) {
+ throw new IllegalArgumentException("bad operand size");
+ }
+ for (int i = 0; i < size; i++) {
+ result = (result << 8) | (itsCodeBuffer[start + i] & 0xFF);
+ }
+ return result;
+ }
+
+ /**
+ * Calculate initial local variable and op stack types for each super
+ * block in the method.
+ */
+ private void verify() {
+ int[] initialLocals = createInitialLocals();
+ superBlocks[0].merge(initialLocals, initialLocals.length,
+ new int[0], 0, itsConstantPool);
+
+ // Start from the top of the method and queue up block dependencies
+ // as they come along.
+ workList = new SuperBlock[] { superBlocks[0] };
+ workListTop = 1;
+ executeWorkList();
+
+ // Replace dead code with no-ops.
+ for (int i = 0; i < superBlocks.length; i++) {
+ SuperBlock sb = superBlocks[i];
+ if (!sb.isInitialized()) {
+ killSuperBlock(sb);
+ }
+ }
+ executeWorkList();
+ }
+
+ /**
+ * Replace the contents of a super block with no-ops.
+ *
+ * The above description is not strictly true; the last instruction is
+ * an athrow instruction. This technique is borrowed from ASM's
+ * developer guide: http://asm.ow2.org/doc/developer-guide.html#deadcode
+ *
+ * The proposed algorithm fills a block with nop, ending it with an
+ * athrow. The stack map generated would be empty locals with an
+ * exception on the stack. In theory, it shouldn't matter what the
+ * locals are, as long as the stack has an exception for the athrow bit.
+ * However, it turns out that if the code being modified falls into an
+ * exception handler, it causes problems. Therefore, if it does, then
+ * we steal the locals from the exception block.
+ *
+ * If the block itself is an exception handler, we remove it from the
+ * exception table to simplify block dependencies.
+ */
+ private void killSuperBlock(SuperBlock sb) {
+ int[] locals = new int[0];
+ int[] stack = new int[] { TypeInfo.OBJECT("java/lang/Throwable",
+ itsConstantPool) };
+
+ // If the super block is handled by any exception handler, use its
+ // locals as the killed block's locals. Ignore uninitialized
+ // handlers, because they will also be killed and removed from the
+ // exception table.
+ for (int i = 0; i < itsExceptionTableTop; i++) {
+ ExceptionTableEntry ete = itsExceptionTable[i];
+ int eteStart = getLabelPC(ete.itsStartLabel);
+ int eteEnd = getLabelPC(ete.itsEndLabel);
+ int handlerPC = getLabelPC(ete.itsHandlerLabel);
+ SuperBlock handlerSB = getSuperBlockFromOffset(handlerPC);
+ if ((sb.getStart() > eteStart && sb.getStart() < eteEnd) ||
+ (eteStart > sb.getStart() && eteStart < sb.getEnd()) &&
+ handlerSB.isInitialized()) {
+ locals = handlerSB.getLocals();
+ break;
+ }
+ }
+
+ // Remove any exception table entry whose handler is the killed
+ // block. This removes block dependencies to make stack maps for
+ // dead blocks easier to create.
+ for (int i = 0; i < itsExceptionTableTop; i++) {
+ ExceptionTableEntry ete = itsExceptionTable[i];
+ int eteStart = getLabelPC(ete.itsStartLabel);
+ if (eteStart == sb.getStart()) {
+ for (int j = i + 1; j < itsExceptionTableTop; j++) {
+ itsExceptionTable[j - 1] = itsExceptionTable[j];
+ }
+ itsExceptionTableTop--;
+ i--;
+ }
+ }
+
+ sb.merge(locals, locals.length, stack, stack.length,
+ itsConstantPool);
+
+ int end = sb.getEnd() - 1;
+ itsCodeBuffer[end] = (byte) ByteCode.ATHROW;
+ for (int bci = sb.getStart(); bci < end; bci++) {
+ itsCodeBuffer[bci] = (byte) ByteCode.NOP;
+ }
+ }
+
+ private void executeWorkList() {
+ while (workListTop > 0) {
+ SuperBlock work = workList[--workListTop];
+ work.setInQueue(false);
+ locals = work.getLocals();
+ stack = work.getStack();
+ localsTop = locals.length;
+ stackTop = stack.length;
+ executeBlock(work);
+ }
+ }
+
+ /**
+ * Simulate the local variable and op stack for a super block.
+ */
+ private void executeBlock(SuperBlock work) {
+ int bc = 0;
+ int next = 0;
+
+ if (DEBUGSTACKMAP) {
+ System.out.println("working on sb " + work.getIndex());
+ System.out.println("initial type state:");
+ TypeInfo.print(locals, localsTop, stack, stackTop,
+ itsConstantPool);
+ }
+
+ for (int bci = work.getStart(); bci < work.getEnd(); bci += next) {
+ bc = itsCodeBuffer[bci] & 0xFF;
+ next = execute(bci);
+
+ // If we have a branch to some super block, we need to merge
+ // the current state of the local table and op stack with what's
+ // currently stored as the initial state of the super block. If
+ // something actually changed, we need to add it to the work
+ // list.
+ if (isBranch(bc)) {
+ SuperBlock targetSB = getBranchTarget(bci);
+ if (DEBUGSTACKMAP) {
+ System.out.println("sb " + work.getIndex() +
+ " points to sb " +
+ targetSB.getIndex() +
+ " (offset " + bci + " -> " +
+ targetSB.getStart() + ")");
+ System.out.println("type state at " + bci + ":");
+ TypeInfo.print(locals, localsTop, stack, stackTop,
+ itsConstantPool);
+ }
+ flowInto(targetSB);
+ if (DEBUGSTACKMAP) {
+ System.out.println("type state of " + targetSB +
+ " after merge:");
+ TypeInfo.print(targetSB.getLocals(),
+ targetSB.getStack(), itsConstantPool);
+ }
+ } else if (bc == ByteCode.TABLESWITCH) {
+ int switchStart = bci + 1 + (3 & ~bci); // 3 - bci % 4
+ int defaultOffset = getOperand(switchStart, 4);
+ SuperBlock targetSB =
+ getSuperBlockFromOffset(bci + defaultOffset);
+ if (DEBUGSTACK) {
+ System.out.println("merging sb " + work.getIndex() +
+ " with sb " + targetSB.getIndex());
+ }
+ flowInto(targetSB);
+ int low = getOperand(switchStart + 4, 4);
+ int high = getOperand(switchStart + 8, 4);
+ int numCases = high - low + 1;
+ int caseBase = switchStart + 12;
+ for (int i = 0; i < numCases; i++) {
+ int label = bci + getOperand(caseBase + 4 * i, 4);
+ targetSB = getSuperBlockFromOffset(label);
+ if (DEBUGSTACKMAP) {
+ System.out.println("merging sb " +
+ work.getIndex() + " with sb " +
+ targetSB.getIndex());
+ }
+ flowInto(targetSB);
+ }
+ }
+
+ for (int i = 0; i < itsExceptionTableTop; i++) {
+ ExceptionTableEntry ete = itsExceptionTable[i];
+ short startPC = (short) getLabelPC(ete.itsStartLabel);
+ short endPC = (short) getLabelPC(ete.itsEndLabel);
+ if (bci < startPC || bci >= endPC) {
+ continue;
+ }
+ short handlerPC =
+ (short) getLabelPC(ete.itsHandlerLabel);
+ SuperBlock sb = getSuperBlockFromOffset(handlerPC);
+ int exceptionType;
+
+ if (ete.itsCatchType == 0) {
+ exceptionType = TypeInfo.OBJECT(
+ itsConstantPool.addClass("java/lang/Throwable"));
+ } else {
+ exceptionType = TypeInfo.OBJECT(ete.itsCatchType);
+ }
+ sb.merge(locals, localsTop, new int[] { exceptionType }, 1,
+ itsConstantPool);
+ addToWorkList(sb);
+ }
+ }
+
+ if (DEBUGSTACKMAP) {
+ System.out.println("end of sb " + work.getIndex() + ":");
+ TypeInfo.print(locals, localsTop, stack, stackTop,
+ itsConstantPool);
+ }
+
+ // Check the last instruction to see if it is a true end of a
+ // super block (ie., if the instruction is a return). If it
+ // isn't, we need to continue processing the next chunk.
+ if (!isSuperBlockEnd(bc)) {
+ int nextIndex = work.getIndex() + 1;
+ if (nextIndex < superBlocks.length) {
+ if (DEBUGSTACKMAP) {
+ System.out.println("continuing from sb " +
+ work.getIndex() + " into sb " +
+ nextIndex);
+ }
+ flowInto(superBlocks[nextIndex]);
+ }
+ }
+ }
+
+ /**
+ * Perform a merge of type state and add the super block to the work
+ * list if the merge changed anything.
+ */
+ private void flowInto(SuperBlock sb) {
+ if (sb.merge(locals, localsTop, stack, stackTop, itsConstantPool)) {
+ addToWorkList(sb);
+ }
+ }
+
+ private void addToWorkList(SuperBlock sb) {
+ if (!sb.isInQueue()) {
+ sb.setInQueue(true);
+ sb.setInitialized(true);
+ if (workListTop == workList.length) {
+ SuperBlock[] tmp = new SuperBlock[workListTop * 2];
+ System.arraycopy(workList, 0, tmp, 0, workListTop);
+ workList = tmp;
+ }
+ workList[workListTop++] = sb;
+ }
+ }
+
+ /**
+ * Execute a single byte code instruction.
+ *
+ * @param bci the index of the byte code instruction to execute
+ * @return the length of the byte code instruction
+ */
+ private int execute(int bci) {
+ int bc = itsCodeBuffer[bci] & 0xFF;
+ int type, type2, index;
+ int length = 0;
+ long lType, lType2;
+ String className;
+
+ switch (bc) {
+ case ByteCode.NOP:
+ case ByteCode.IINC:
+ case ByteCode.GOTO:
+ case ByteCode.GOTO_W:
+ // No change
+ break;
+ case ByteCode.CHECKCAST:
+ pop();
+ push(TypeInfo.OBJECT(getOperand(bci + 1, 2)));
+ break;
+ case ByteCode.IASTORE: // pop; pop; pop
+ case ByteCode.LASTORE:
+ case ByteCode.FASTORE:
+ case ByteCode.DASTORE:
+ case ByteCode.AASTORE:
+ case ByteCode.BASTORE:
+ case ByteCode.CASTORE:
+ case ByteCode.SASTORE:
+ pop();
+ case ByteCode.PUTFIELD: // pop; pop
+ case ByteCode.IF_ICMPEQ:
+ case ByteCode.IF_ICMPNE:
+ case ByteCode.IF_ICMPLT:
+ case ByteCode.IF_ICMPGE:
+ case ByteCode.IF_ICMPGT:
+ case ByteCode.IF_ICMPLE:
+ case ByteCode.IF_ACMPEQ:
+ case ByteCode.IF_ACMPNE:
+ pop();
+ case ByteCode.IFEQ: // pop
+ case ByteCode.IFNE:
+ case ByteCode.IFLT:
+ case ByteCode.IFGE:
+ case ByteCode.IFGT:
+ case ByteCode.IFLE:
+ case ByteCode.IFNULL:
+ case ByteCode.IFNONNULL:
+ case ByteCode.POP:
+ case ByteCode.MONITORENTER:
+ case ByteCode.MONITOREXIT:
+ case ByteCode.PUTSTATIC:
+ pop();
+ break;
+ case ByteCode.POP2:
+ pop2();
+ break;
+ case ByteCode.ACONST_NULL:
+ push(TypeInfo.NULL);
+ break;
+ case ByteCode.IALOAD: // pop; pop; push(INTEGER)
+ case ByteCode.BALOAD:
+ case ByteCode.CALOAD:
+ case ByteCode.SALOAD:
+ case ByteCode.IADD:
+ case ByteCode.ISUB:
+ case ByteCode.IMUL:
+ case ByteCode.IDIV:
+ case ByteCode.IREM:
+ case ByteCode.ISHL:
+ case ByteCode.ISHR:
+ case ByteCode.IUSHR:
+ case ByteCode.IAND:
+ case ByteCode.IOR:
+ case ByteCode.IXOR:
+ case ByteCode.LCMP:
+ case ByteCode.FCMPL:
+ case ByteCode.FCMPG:
+ case ByteCode.DCMPL:
+ case ByteCode.DCMPG:
+ pop();
+ case ByteCode.INEG: // pop; push(INTEGER)
+ case ByteCode.L2I:
+ case ByteCode.F2I:
+ case ByteCode.D2I:
+ case ByteCode.I2B:
+ case ByteCode.I2C:
+ case ByteCode.I2S:
+ case ByteCode.ARRAYLENGTH:
+ case ByteCode.INSTANCEOF:
+ pop();
+ case ByteCode.ICONST_M1: // push(INTEGER)
+ case ByteCode.ICONST_0:
+ case ByteCode.ICONST_1:
+ case ByteCode.ICONST_2:
+ case ByteCode.ICONST_3:
+ case ByteCode.ICONST_4:
+ case ByteCode.ICONST_5:
+ case ByteCode.ILOAD:
+ case ByteCode.ILOAD_0:
+ case ByteCode.ILOAD_1:
+ case ByteCode.ILOAD_2:
+ case ByteCode.ILOAD_3:
+ case ByteCode.BIPUSH:
+ case ByteCode.SIPUSH:
+ push(TypeInfo.INTEGER);
+ break;
+ case ByteCode.LALOAD: // pop; pop; push(LONG)
+ case ByteCode.LADD:
+ case ByteCode.LSUB:
+ case ByteCode.LMUL:
+ case ByteCode.LDIV:
+ case ByteCode.LREM:
+ case ByteCode.LSHL:
+ case ByteCode.LSHR:
+ case ByteCode.LUSHR:
+ case ByteCode.LAND:
+ case ByteCode.LOR:
+ case ByteCode.LXOR:
+ pop();
+ case ByteCode.LNEG: // pop; push(LONG)
+ case ByteCode.I2L:
+ case ByteCode.F2L:
+ case ByteCode.D2L:
+ pop();
+ case ByteCode.LCONST_0: // push(LONG)
+ case ByteCode.LCONST_1:
+ case ByteCode.LLOAD:
+ case ByteCode.LLOAD_0:
+ case ByteCode.LLOAD_1:
+ case ByteCode.LLOAD_2:
+ case ByteCode.LLOAD_3:
+ push(TypeInfo.LONG);
+ break;
+ case ByteCode.FALOAD: // pop; pop; push(FLOAT)
+ case ByteCode.FADD:
+ case ByteCode.FSUB:
+ case ByteCode.FMUL:
+ case ByteCode.FDIV:
+ case ByteCode.FREM:
+ pop();
+ case ByteCode.FNEG: // pop; push(FLOAT)
+ case ByteCode.I2F:
+ case ByteCode.L2F:
+ case ByteCode.D2F:
+ pop();
+ case ByteCode.FCONST_0: // push(FLOAT)
+ case ByteCode.FCONST_1:
+ case ByteCode.FCONST_2:
+ case ByteCode.FLOAD:
+ case ByteCode.FLOAD_0:
+ case ByteCode.FLOAD_1:
+ case ByteCode.FLOAD_2:
+ case ByteCode.FLOAD_3:
+ push(TypeInfo.FLOAT);
+ break;
+ case ByteCode.DALOAD: // pop; pop; push(DOUBLE)
+ case ByteCode.DADD:
+ case ByteCode.DSUB:
+ case ByteCode.DMUL:
+ case ByteCode.DDIV:
+ case ByteCode.DREM:
+ pop();
+ case ByteCode.DNEG: // pop; push(DOUBLE)
+ case ByteCode.I2D:
+ case ByteCode.L2D:
+ case ByteCode.F2D:
+ pop();
+ case ByteCode.DCONST_0: // push(DOUBLE)
+ case ByteCode.DCONST_1:
+ case ByteCode.DLOAD:
+ case ByteCode.DLOAD_0:
+ case ByteCode.DLOAD_1:
+ case ByteCode.DLOAD_2:
+ case ByteCode.DLOAD_3:
+ push(TypeInfo.DOUBLE);
+ break;
+ case ByteCode.ISTORE:
+ executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.INTEGER);
+ break;
+ case ByteCode.ISTORE_0:
+ case ByteCode.ISTORE_1:
+ case ByteCode.ISTORE_2:
+ case ByteCode.ISTORE_3:
+ executeStore(bc - ByteCode.ISTORE_0, TypeInfo.INTEGER);
+ break;
+ case ByteCode.LSTORE:
+ executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.LONG);
+ break;
+ case ByteCode.LSTORE_0:
+ case ByteCode.LSTORE_1:
+ case ByteCode.LSTORE_2:
+ case ByteCode.LSTORE_3:
+ executeStore(bc - ByteCode.LSTORE_0, TypeInfo.LONG);
+ break;
+ case ByteCode.FSTORE:
+ executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.FLOAT);
+ break;
+ case ByteCode.FSTORE_0:
+ case ByteCode.FSTORE_1:
+ case ByteCode.FSTORE_2:
+ case ByteCode.FSTORE_3:
+ executeStore(bc - ByteCode.FSTORE_0, TypeInfo.FLOAT);
+ break;
+ case ByteCode.DSTORE:
+ executeStore(getOperand(bci + 1, wide ? 2 : 1), TypeInfo.DOUBLE);
+ break;
+ case ByteCode.DSTORE_0:
+ case ByteCode.DSTORE_1:
+ case ByteCode.DSTORE_2:
+ case ByteCode.DSTORE_3:
+ executeStore(bc - ByteCode.DSTORE_0, TypeInfo.DOUBLE);
+ break;
+ case ByteCode.ALOAD:
+ executeALoad(getOperand(bci + 1, wide ? 2 : 1));
+ break;
+ case ByteCode.ALOAD_0:
+ case ByteCode.ALOAD_1:
+ case ByteCode.ALOAD_2:
+ case ByteCode.ALOAD_3:
+ executeALoad(bc - ByteCode.ALOAD_0);
+ break;
+ case ByteCode.ASTORE:
+ executeAStore(getOperand(bci + 1, wide ? 2 : 1));
+ break;
+ case ByteCode.ASTORE_0:
+ case ByteCode.ASTORE_1:
+ case ByteCode.ASTORE_2:
+ case ByteCode.ASTORE_3:
+ executeAStore(bc - ByteCode.ASTORE_0);
+ break;
+ case ByteCode.IRETURN:
+ case ByteCode.LRETURN:
+ case ByteCode.FRETURN:
+ case ByteCode.DRETURN:
+ case ByteCode.ARETURN:
+ case ByteCode.RETURN:
+ clearStack();
+ break;
+ case ByteCode.ATHROW:
+ type = pop();
+ clearStack();
+ push(type);
+ break;
+ case ByteCode.SWAP:
+ type = pop();
+ type2 = pop();
+ push(type);
+ push(type2);
+ break;
+ case ByteCode.LDC:
+ case ByteCode.LDC_W:
+ case ByteCode.LDC2_W:
+ if (bc == ByteCode.LDC) {
+ index = getOperand(bci + 1);
+ } else {
+ index = getOperand(bci + 1, 2);
+ }
+ byte constType = itsConstantPool.getConstantType(index);
+ switch (constType) {
+ case ConstantPool.CONSTANT_Double:
+ push(TypeInfo.DOUBLE);
+ break;
+ case ConstantPool.CONSTANT_Float:
+ push(TypeInfo.FLOAT);
+ break;
+ case ConstantPool.CONSTANT_Long:
+ push(TypeInfo.LONG);
+ break;
+ case ConstantPool.CONSTANT_Integer:
+ push(TypeInfo.INTEGER);
+ break;
+ case ConstantPool.CONSTANT_String:
+ push(TypeInfo.OBJECT("java/lang/String",
+ itsConstantPool));
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "bad const type " + constType);
+ }
+ break;
+ case ByteCode.NEW:
+ push(TypeInfo.UNINITIALIZED_VARIABLE(bci));
+ break;
+ case ByteCode.NEWARRAY:
+ pop();
+ char componentType =
+ arrayTypeToName(itsCodeBuffer[bci + 1]);
+ index = itsConstantPool.addClass("[" + componentType);
+ push(TypeInfo.OBJECT((short) index));
+ break;
+ case ByteCode.ANEWARRAY:
+ index = getOperand(bci + 1, 2);
+ className = (String) itsConstantPool.getConstantData(index);
+ pop();
+ push(TypeInfo.OBJECT("[L" + className + ';',
+ itsConstantPool));
+ break;
+ case ByteCode.INVOKEVIRTUAL:
+ case ByteCode.INVOKESPECIAL:
+ case ByteCode.INVOKESTATIC:
+ case ByteCode.INVOKEINTERFACE:
+ index = getOperand(bci + 1, 2);
+ FieldOrMethodRef m = (FieldOrMethodRef)
+ itsConstantPool.getConstantData(index);
+ String methodType = m.getType();
+ String methodName = m.getName();
+ int parameterCount = sizeOfParameters(methodType) >>> 16;
+ for (int i = 0; i < parameterCount; i++) {
+ pop();
+ }
+ if (bc != ByteCode.INVOKESTATIC) {
+ int instType = pop();
+ int tag = TypeInfo.getTag(instType);
+ if (tag == TypeInfo.UNINITIALIZED_VARIABLE(0) ||
+ tag == TypeInfo.UNINITIALIZED_THIS) {
+ if ("".equals(methodName)) {
+ int newType =
+ TypeInfo.OBJECT(itsThisClassIndex);
+ initializeTypeInfo(instType, newType);
+ } else {
+ throw new IllegalStateException("bad instance");
+ }
+ }
+ }
+ int rParen = methodType.indexOf(')');
+ String returnType = methodType.substring(rParen + 1);
+ returnType = descriptorToInternalName(returnType);
+ if (!returnType.equals("V")) {
+ push(TypeInfo.fromType(returnType, itsConstantPool));
+ }
+ break;
+ case ByteCode.GETFIELD:
+ pop();
+ case ByteCode.GETSTATIC:
+ index = getOperand(bci + 1, 2);
+ FieldOrMethodRef f = (FieldOrMethodRef)
+ itsConstantPool.getConstantData(index);
+ String fieldType = descriptorToInternalName(f.getType());
+ push(TypeInfo.fromType(fieldType, itsConstantPool));
+ break;
+ case ByteCode.DUP:
+ type = pop();
+ push(type);
+ push(type);
+ break;
+ case ByteCode.DUP_X1:
+ type = pop();
+ type2 = pop();
+ push(type);
+ push(type2);
+ push(type);
+ break;
+ case ByteCode.DUP_X2:
+ type = pop();
+ lType = pop2();
+ push(type);
+ push2(lType);
+ push(type);
+ break;
+ case ByteCode.DUP2:
+ lType = pop2();
+ push2(lType);
+ push2(lType);
+ break;
+ case ByteCode.DUP2_X1:
+ lType = pop2();
+ type = pop();
+ push2(lType);
+ push(type);
+ push2(lType);
+ break;
+ case ByteCode.DUP2_X2:
+ lType = pop2();
+ lType2 = pop2();
+ push2(lType);
+ push2(lType2);
+ push2(lType);
+ break;
+ case ByteCode.TABLESWITCH:
+ int switchStart = bci + 1 + (3 & ~bci);
+ int low = getOperand(switchStart + 4, 4);
+ int high = getOperand(switchStart + 8, 4);
+ length = 4 * (high - low + 4) + switchStart - bci;
+ pop();
+ break;
+ case ByteCode.AALOAD:
+ pop();
+ int typeIndex = pop() >>> 8;
+ className =
+ (String) itsConstantPool.getConstantData(typeIndex);
+ String arrayType = className;
+ if (arrayType.charAt(0) != '[') {
+ throw new IllegalStateException("bad array type");
+ }
+ String elementDesc = arrayType.substring(1);
+ String elementType = descriptorToInternalName(elementDesc);
+ typeIndex = itsConstantPool.addClass(elementType);
+ push(TypeInfo.OBJECT(typeIndex));
+ break;
+ case ByteCode.WIDE:
+ // Alters behaviour of next instruction
+ wide = true;
+ break;
+ case ByteCode.MULTIANEWARRAY:
+ case ByteCode.LOOKUPSWITCH:
+ // Currently not used in any part of Rhino, so ignore it
+ case ByteCode.JSR: // TODO: JSR is deprecated
+ case ByteCode.RET:
+ case ByteCode.JSR_W:
+ default:
+ throw new IllegalArgumentException("bad opcode: " + bc);
+ }
+
+ if (length == 0) {
+ length = opcodeLength(bc, wide);
+ }
+ if (wide && bc != ByteCode.WIDE) {
+ wide = false;
+ }
+ return length;
+ }
+
+ private void executeALoad(int localIndex) {
+ int type = getLocal(localIndex);
+ int tag = TypeInfo.getTag(type);
+ if (tag == TypeInfo.OBJECT_TAG ||
+ tag == TypeInfo.UNINITIALIZED_THIS ||
+ tag == TypeInfo.UNINITIALIZED_VAR_TAG ||
+ tag == TypeInfo.NULL) {
+ push(type);
+ } else {
+ throw new IllegalStateException("bad local variable type: " +
+ type + " at index: " +
+ localIndex);
+ }
+ }
+
+ private void executeAStore(int localIndex) {
+ setLocal(localIndex, pop());
+ }
+
+ private void executeStore(int localIndex, int typeInfo) {
+ pop();
+ setLocal(localIndex, typeInfo);
+ }
+
+ /**
+ * Change an UNINITIALIZED_OBJECT or UNINITIALIZED_THIS to the proper
+ * type of the object. This occurs when the proper constructor is
+ * invoked.
+ */
+ private void initializeTypeInfo(int prevType, int newType) {
+ initializeTypeInfo(prevType, newType, locals, localsTop);
+ initializeTypeInfo(prevType, newType, stack, stackTop);
+ }
+
+ private void initializeTypeInfo(int prevType, int newType, int[] data,
+ int dataTop) {
+ for (int i = 0; i < dataTop; i++) {
+ if (data[i] == prevType) {
+ data[i] = newType;
+ }
+ }
+ }
+
+ private int getLocal(int localIndex) {
+ if (localIndex < localsTop) {
+ return locals[localIndex];
+ } else {
+ return TypeInfo.TOP;
+ }
+ }
+
+ private void setLocal(int localIndex, int typeInfo) {
+ if (localIndex >= localsTop) {
+ int[] tmp = new int[localIndex + 1];
+ System.arraycopy(locals, 0, tmp, 0, localsTop);
+ locals = tmp;
+ localsTop = localIndex + 1;
+ }
+ locals[localIndex] = typeInfo;
+ }
+
+ private void push(int typeInfo) {
+ if (stackTop == stack.length) {
+ int[] tmp = new int[Math.max(stackTop * 2, 4)];
+ System.arraycopy(stack, 0, tmp, 0, stackTop);
+ stack = tmp;
+ }
+ stack[stackTop++] = typeInfo;
+ }
+
+ private int pop() {
+ return stack[--stackTop];
+ }
+
+ /**
+ * Push two words onto the op stack.
+ *
+ * This is only meant to be used as a complement to pop2(), and both
+ * methods are helpers for the more complex DUP operations.
+ */
+ private void push2(long typeInfo) {
+ push((int) (typeInfo & 0xFFFFFF));
+ typeInfo >>>= 32;
+ if (typeInfo != 0) {
+ push((int) (typeInfo & 0xFFFFFF));
+ }
+ }
+
+ /**
+ * Pop two words from the op stack.
+ *
+ * If the top of the stack is a DOUBLE or LONG, then the bottom 32 bits
+ * reflects the appropriate type and the top 32 bits are 0. Otherwise,
+ * the top 32 bits are the first word on the stack and the lower 32
+ * bits are the second word on the stack.
+ */
+ private long pop2() {
+ long type = pop();
+ if (TypeInfo.isTwoWords((int) type)) {
+ return type;
+ } else {
+ return type << 32 | (pop() & 0xFFFFFF);
+ }
+ }
+
+ private void clearStack() {
+ stackTop = 0;
+ }
+
+ /**
+ * Compute the output size of the stack map table.
+ *
+ * Because this would share much in common with actual writing of the
+ * stack map table, we instead just write the stack map table to a
+ * buffer and return the size from it. The buffer is later used in
+ * the actual writing of bytecode.
+ */
+ int computeWriteSize() {
+ // Allocate a buffer that can handle the worst case size of the
+ // stack map to prevent lots of reallocations.
+ int writeSize = getWorstCaseWriteSize();
+ rawStackMap = new byte[writeSize];
+ computeRawStackMap();
+ return rawStackMapTop + 2;
+ }
+
+ int write(byte[] data, int offset) {
+ offset = putInt32(rawStackMapTop + 2, data, offset);
+ offset = putInt16(superBlocks.length - 1, data, offset);
+ System.arraycopy(rawStackMap, 0, data, offset, rawStackMapTop);
+ return offset + rawStackMapTop;
+ }
+
+ /**
+ * Compute a space-optimal stack map table.
+ */
+ private void computeRawStackMap() {
+ SuperBlock prev = superBlocks[0];
+ int[] prevLocals = prev.getTrimmedLocals();
+ int prevOffset = -1;
+ for (int i = 1; i < superBlocks.length; i++) {
+ SuperBlock current = superBlocks[i];
+ int[] currentLocals = current.getTrimmedLocals();
+ int[] currentStack = current.getStack();
+ int offsetDelta = current.getStart() - prevOffset - 1;
+
+ if (currentStack.length == 0) {
+ int last = prevLocals.length > currentLocals.length ?
+ currentLocals.length : prevLocals.length;
+ int delta = Math.abs(prevLocals.length -
+ currentLocals.length);
+ int j;
+ // Compare locals until one is different or the end of a
+ // local variable array is reached
+ for (j = 0; j < last; j++) {
+ if (prevLocals[j] != currentLocals[j]) {
+ break;
+ }
+ }
+ if (j == currentLocals.length && delta == 0) {
+ // All of the compared locals are equal and the local
+ // arrays are of equal size
+ writeSameFrame(currentLocals, offsetDelta);
+ } else if (j == currentLocals.length && delta <= 3) {
+ // All of the compared locals are equal and the current
+ // frame has less locals than the previous frame
+ writeChopFrame(delta, offsetDelta);
+ } else if (j == prevLocals.length && delta <= 3) {
+ // All of the compared locals are equal and the current
+ // frame has more locals than the previous frame
+ writeAppendFrame(currentLocals, delta, offsetDelta);
+ } else {
+ // Not all locals were compared were equal, so a full
+ // frame is necessary
+ writeFullFrame(currentLocals, currentStack,
+ offsetDelta);
+ }
+ } else if (currentStack.length == 1) {
+ if (Arrays.equals(prevLocals, currentLocals)) {
+ writeSameLocalsOneStackItemFrame(currentLocals,
+ currentStack,
+ offsetDelta);
+ } else {
+ // Output a full frame, since no other frame types have
+ // one operand stack item.
+ writeFullFrame(currentLocals, currentStack,
+ offsetDelta);
+ }
+ } else {
+ // Any stack map frame that has more than one operand stack
+ // item has to be a full frame. All other frame types have
+ // at most one item on the stack.
+ writeFullFrame(currentLocals, currentStack, offsetDelta);
+ }
+
+ prev = current;
+ prevLocals = currentLocals;
+ prevOffset = current.getStart();
+ }
+ }
+
+ /**
+ * Get the worst case write size of the stack map table.
+ *
+ * This computes how much full frames would take, if each full frame
+ * contained the maximum number of locals and stack operands, and each
+ * verification type was 3 bytes.
+ */
+ private int getWorstCaseWriteSize() {
+ return (superBlocks.length - 1) * (7 + itsMaxLocals * 3 +
+ itsMaxStack * 3);
+ }
+
+ private void writeSameFrame(int[] locals, int offsetDelta) {
+ if (offsetDelta <= 63) {
+ // Output a same_frame frame. Despite the name,
+ // the operand stack may differ, but the current
+ // operand stack must be empty.
+ rawStackMap[rawStackMapTop++] = (byte) offsetDelta;
+ } else {
+ // Output a same_frame_extended frame. Similar to
+ // the above, except with a larger offset delta.
+ rawStackMap[rawStackMapTop++] = (byte) 251;
+ rawStackMapTop = putInt16(offsetDelta, rawStackMap,
+ rawStackMapTop);
+ }
+ }
+
+ private void writeSameLocalsOneStackItemFrame(int[] locals,
+ int[] stack,
+ int offsetDelta) {
+ if (offsetDelta <= 63) {
+ // Output a same_locals_1_stack_item frame. Similar
+ // to same_frame, only with one item on the operand
+ // stack instead of zero.
+ rawStackMap[rawStackMapTop++] = (byte) (64 + offsetDelta);
+ } else {
+ // Output a same_locals_1_stack_item_extended frame.
+ // Similar to same_frame_extended, only with one
+ // item on the operand stack instead of zero.
+ rawStackMap[rawStackMapTop++] = (byte) 247;
+ rawStackMapTop = putInt16(offsetDelta, rawStackMap,
+ rawStackMapTop);
+ }
+ writeType(stack[0]);
+ }
+
+ private void writeFullFrame(int[] locals, int[] stack,
+ int offsetDelta) {
+ rawStackMap[rawStackMapTop++] = (byte) 255;
+ rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop);
+ rawStackMapTop = putInt16(locals.length, rawStackMap,
+ rawStackMapTop);
+ rawStackMapTop = writeTypes(locals);
+ rawStackMapTop = putInt16(stack.length, rawStackMap,
+ rawStackMapTop);
+ rawStackMapTop = writeTypes(stack);
+ }
+
+ private void writeAppendFrame(int[] locals, int localsDelta,
+ int offsetDelta) {
+ int start = locals.length - localsDelta;
+ rawStackMap[rawStackMapTop++] = (byte) (251 + localsDelta);
+ rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop);
+ rawStackMapTop = writeTypes(locals, start);
+ }
+
+ private void writeChopFrame(int localsDelta, int offsetDelta) {
+ rawStackMap[rawStackMapTop++] = (byte) (251 - localsDelta);
+ rawStackMapTop = putInt16(offsetDelta, rawStackMap, rawStackMapTop);
+ }
+
+ private int writeTypes(int[] types) {
+ return writeTypes(types, 0);
+ }
+
+ private int writeTypes(int[] types, int start) {
+ @SuppressWarnings("unused")
+ int startOffset = rawStackMapTop;
+ for (int i = start; i < types.length; i++) {
+ rawStackMapTop = writeType(types[i]);
+ }
+ return rawStackMapTop;
+ }
+
+ private int writeType(int type) {
+ int tag = type & 0xFF;
+ rawStackMap[rawStackMapTop++] = (byte) tag;
+ if (tag == TypeInfo.OBJECT_TAG ||
+ tag == TypeInfo.UNINITIALIZED_VAR_TAG) {
+ rawStackMapTop = putInt16(type >>> 8, rawStackMap,
+ rawStackMapTop);
+ }
+ return rawStackMapTop;
+ }
+
+ // Intermediate operand stack and local variable state. During
+ // execution of a block, these are initialized to copies of the initial
+ // block type state and are modified by the actual stack/local
+ // emulation.
+ private int[] locals;
+ private int localsTop;
+ private int[] stack;
+ private int stackTop;
+
+ private SuperBlock[] workList;
+ private int workListTop;
+
+ private SuperBlock[] superBlocks;
+ @SuppressWarnings("unused")
+ private SuperBlock[] superBlockDeps;
+
+ private byte[] rawStackMap;
+ private int rawStackMapTop;
+
+ private boolean wide;
+
+ static final boolean DEBUGSTACKMAP = false;
+ }
+
+ /**
+ * Convert a newarray operand into an internal type.
+ */
+ private static char arrayTypeToName(int type) {
+ switch (type) {
+ case ByteCode.T_BOOLEAN:
+ return 'Z';
+ case ByteCode.T_CHAR:
+ return 'C';
+ case ByteCode.T_FLOAT:
+ return 'F';
+ case ByteCode.T_DOUBLE:
+ return 'D';
+ case ByteCode.T_BYTE:
+ return 'B';
+ case ByteCode.T_SHORT:
+ return 'S';
+ case ByteCode.T_INT:
+ return 'I';
+ case ByteCode.T_LONG:
+ return 'J';
+ default:
+ throw new IllegalArgumentException("bad operand");
+ }
+ }
+
+ /**
+ * Convert a class descriptor into an internal name.
+ *
+ * For example, descriptor Ljava/lang/Object; becomes java/lang/Object.
+ */
+ private static String classDescriptorToInternalName(String descriptor) {
+ return descriptor.substring(1, descriptor.length() - 1);
+ }
+
+ /**
+ * Convert a non-method type descriptor into an internal type.
+ *
+ * @param descriptor the simple type descriptor to convert
+ */
+ private static String descriptorToInternalName(String descriptor) {
+ switch (descriptor.charAt(0)) {
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z':
+ case 'V':
+ case '[':
+ return descriptor;
+ case 'L':
+ return classDescriptorToInternalName(descriptor);
+ default:
+ throw new IllegalArgumentException("bad descriptor:" +
+ descriptor);
+ }
+ }
+
+ /**
+ * Compute the initial local variable array for the current method.
+ *
+ * Creates an array of the size of the method's max locals, regardless of
+ * the number of parameters in the method.
+ */
+ private int[] createInitialLocals() {
+ int[] initialLocals = new int[itsMaxLocals];
+ int localsTop = 0;
+ // Instance methods require the first local variable in the array
+ // to be "this". However, if the method being created is a
+ // constructor, aka the method is , then the type of "this"
+ // should be StackMapTable.UNINITIALIZED_THIS
+ if ((itsCurrentMethod.getFlags() & ACC_STATIC) == 0) {
+ if ("".equals(itsCurrentMethod.getName())) {
+ initialLocals[localsTop++] = TypeInfo.UNINITIALIZED_THIS;
+ } else {
+ initialLocals[localsTop++] = TypeInfo.OBJECT(itsThisClassIndex);
+ }
+ }
+
+ // No error checking should be necessary, sizeOfParameters does this
+ String type = itsCurrentMethod.getType();
+ int lParenIndex = type.indexOf('(');
+ int rParenIndex = type.indexOf(')');
+ if (lParenIndex != 0 || rParenIndex < 0) {
+ throw new IllegalArgumentException("bad method type");
+ }
+ int start = lParenIndex + 1;
+ StringBuilder paramType = new StringBuilder();
+ while (start < rParenIndex) {
+ switch (type.charAt(start)) {
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z':
+ paramType.append(type.charAt(start));
+ ++start;
+ break;
+ case 'L':
+ int end = type.indexOf(';', start) + 1;
+ String name = type.substring(start, end);
+ paramType.append(name);
+ start = end;
+ break;
+ case '[':
+ paramType.append('[');
+ ++start;
+ continue;
+ }
+ String internalType =
+ descriptorToInternalName(paramType.toString());
+ int typeInfo = TypeInfo.fromType(internalType, itsConstantPool);
+ initialLocals[localsTop++] = typeInfo;
+ if (TypeInfo.isTwoWords(typeInfo)) {
+ localsTop++;
+ }
+ paramType.setLength(0);
+ }
+ return initialLocals;
+ }
+
+ /**
+ * Write the class file to the OutputStream.
+ *
+ * @param oStream the stream to write to
+ * @throws IOException if writing to the stream produces an exception
+ */
+ public void write(OutputStream oStream)
+ throws IOException
+ {
+ byte[] array = toByteArray();
+ oStream.write(array);
+ }
+
+ private int getWriteSize()
+ {
+ int size = 0;
+
+ if (itsSourceFileNameIndex != 0) {
+ itsConstantPool.addUtf8("SourceFile");
+ }
+
+ size += 8; //writeLong(FileHeaderConstant);
+ size += itsConstantPool.getWriteSize();
+ size += 2; //writeShort(itsFlags);
+ size += 2; //writeShort(itsThisClassIndex);
+ size += 2; //writeShort(itsSuperClassIndex);
+ size += 2; //writeShort(itsInterfaces.size());
+ size += 2 * itsInterfaces.size();
+
+ size += 2; //writeShort(itsFields.size());
+ for (int i = 0; i < itsFields.size(); i++) {
+ size += ((ClassFileField)(itsFields.get(i))).getWriteSize();
+ }
+
+ size += 2; //writeShort(itsMethods.size());
+ for (int i = 0; i < itsMethods.size(); i++) {
+ size += ((ClassFileMethod)(itsMethods.get(i))).getWriteSize();
+ }
+
+ if (itsSourceFileNameIndex != 0) {
+ size += 2; //writeShort(1); attributes count
+ size += 2; //writeShort(sourceFileAttributeNameIndex);
+ size += 4; //writeInt(2);
+ size += 2; //writeShort(itsSourceFileNameIndex);
+ }else {
+ size += 2; //out.writeShort(0); no attributes
+ }
+
+ return size;
+ }
+
+ /**
+ * Get the class file as array of bytesto the OutputStream.
+ */
+ public byte[] toByteArray()
+ {
+ int dataSize = getWriteSize();
+ byte[] data = new byte[dataSize];
+ int offset = 0;
+
+ short sourceFileAttributeNameIndex = 0;
+ if (itsSourceFileNameIndex != 0) {
+ sourceFileAttributeNameIndex = itsConstantPool.addUtf8(
+ "SourceFile");
+ }
+
+ offset = putInt32(FileHeaderConstant, data, offset);
+ offset = putInt16(MinorVersion, data, offset);
+ offset = putInt16(MajorVersion, data, offset);
+ offset = itsConstantPool.write(data, offset);
+ offset = putInt16(itsFlags, data, offset);
+ offset = putInt16(itsThisClassIndex, data, offset);
+ offset = putInt16(itsSuperClassIndex, data, offset);
+ offset = putInt16(itsInterfaces.size(), data, offset);
+ for (int i = 0; i < itsInterfaces.size(); i++) {
+ int interfaceIndex = ((Short)(itsInterfaces.get(i))).shortValue();
+ offset = putInt16(interfaceIndex, data, offset);
+ }
+ offset = putInt16(itsFields.size(), data, offset);
+ for (int i = 0; i < itsFields.size(); i++) {
+ ClassFileField field = (ClassFileField)itsFields.get(i);
+ offset = field.write(data, offset);
+ }
+ offset = putInt16(itsMethods.size(), data, offset);
+ for (int i = 0; i < itsMethods.size(); i++) {
+ ClassFileMethod method = (ClassFileMethod)itsMethods.get(i);
+ offset = method.write(data, offset);
+ }
+ if (itsSourceFileNameIndex != 0) {
+ offset = putInt16(1, data, offset); // attributes count
+ offset = putInt16(sourceFileAttributeNameIndex, data, offset);
+ offset = putInt32(2, data, offset);
+ offset = putInt16(itsSourceFileNameIndex, data, offset);
+ } else {
+ offset = putInt16(0, data, offset); // no attributes
+ }
+
+ if (offset != dataSize) {
+ // Check getWriteSize is consistent with write!
+ throw new RuntimeException();
+ }
+
+ return data;
+ }
+
+ static int putInt64(long value, byte[] array, int offset)
+ {
+ offset = putInt32((int)(value >>> 32), array, offset);
+ return putInt32((int)value, array, offset);
+ }
+
+ private static void badStack(int value)
+ {
+ String s;
+ if (value < 0) { s = "Stack underflow: "+value; }
+ else { s = "Too big stack: "+value; }
+ throw new IllegalStateException(s);
+ }
+
+ /*
+ Really weird. Returns an int with # parameters in hi 16 bits, and
+ stack difference removal of parameters from stack and pushing the
+ result (it does not take into account removal of this in case of
+ non-static methods).
+ If Java really supported references we wouldn't have to be this
+ perverted.
+ */
+ private static int sizeOfParameters(String pString)
+ {
+ int length = pString.length();
+ int rightParenthesis = pString.lastIndexOf(')');
+ if (3 <= length /* minimal signature takes at least 3 chars: ()V */
+ && pString.charAt(0) == '('
+ && 1 <= rightParenthesis && rightParenthesis + 1 < length)
+ {
+ boolean ok = true;
+ int index = 1;
+ int stackDiff = 0;
+ int count = 0;
+ stringLoop:
+ while (index != rightParenthesis) {
+ switch (pString.charAt(index)) {
+ default:
+ ok = false;
+ break stringLoop;
+ case 'J' :
+ case 'D' :
+ --stackDiff;
+ // fall thru
+ case 'B' :
+ case 'S' :
+ case 'C' :
+ case 'I' :
+ case 'Z' :
+ case 'F' :
+ --stackDiff;
+ ++count;
+ ++index;
+ continue;
+ case '[' :
+ ++index;
+ int c = pString.charAt(index);
+ while (c == '[') {
+ ++index;
+ c = pString.charAt(index);
+ }
+ switch (c) {
+ default:
+ ok = false;
+ break stringLoop;
+ case 'J' :
+ case 'D' :
+ case 'B' :
+ case 'S' :
+ case 'C' :
+ case 'I' :
+ case 'Z' :
+ case 'F' :
+ --stackDiff;
+ ++count;
+ ++index;
+ continue;
+ case 'L':
+ // fall thru
+ }
+ // fall thru
+ case 'L' : {
+ --stackDiff;
+ ++count;
+ ++index;
+ int semicolon = pString.indexOf(';', index);
+ if (!(index + 1 <= semicolon
+ && semicolon < rightParenthesis))
+ {
+ ok = false;
+ break stringLoop;
+ }
+ index = semicolon + 1;
+ continue;
+ }
+ }
+ }
+ if (ok) {
+ switch (pString.charAt(rightParenthesis + 1)) {
+ default:
+ ok = false;
+ break;
+ case 'J' :
+ case 'D' :
+ ++stackDiff;
+ // fall thru
+ case 'B' :
+ case 'S' :
+ case 'C' :
+ case 'I' :
+ case 'Z' :
+ case 'F' :
+ case 'L' :
+ case '[' :
+ ++stackDiff;
+ // fall thru
+ case 'V' :
+ break;
+ }
+ if (ok) {
+ return ((count << 16) | (0xFFFF & stackDiff));
+ }
+ }
+ }
+ throw new IllegalArgumentException(
+ "Bad parameter signature: "+pString);
+ }
+
+ static int putInt16(int value, byte[] array, int offset)
+ {
+ array[offset + 0] = (byte)(value >>> 8);
+ array[offset + 1] = (byte)value;
+ return offset + 2;
+ }
+
+ static int putInt32(int value, byte[] array, int offset)
+ {
+ array[offset + 0] = (byte)(value >>> 24);
+ array[offset + 1] = (byte)(value >>> 16);
+ array[offset + 2] = (byte)(value >>> 8);
+ array[offset + 3] = (byte)value;
+ return offset + 4;
+ }
+
+ /**
+ * Size of a bytecode instruction, counting the opcode and its operands.
+ *
+ * This is different from opcodeCount, since opcodeCount counts logical
+ * operands.
+ */
+ static int opcodeLength(int opcode, boolean wide) {
+ switch (opcode) {
+ case ByteCode.AALOAD:
+ case ByteCode.AASTORE:
+ case ByteCode.ACONST_NULL:
+ case ByteCode.ALOAD_0:
+ case ByteCode.ALOAD_1:
+ case ByteCode.ALOAD_2:
+ case ByteCode.ALOAD_3:
+ case ByteCode.ARETURN:
+ case ByteCode.ARRAYLENGTH:
+ case ByteCode.ASTORE_0:
+ case ByteCode.ASTORE_1:
+ case ByteCode.ASTORE_2:
+ case ByteCode.ASTORE_3:
+ case ByteCode.ATHROW:
+ case ByteCode.BALOAD:
+ case ByteCode.BASTORE:
+ case ByteCode.BREAKPOINT:
+ case ByteCode.CALOAD:
+ case ByteCode.CASTORE:
+ case ByteCode.D2F:
+ case ByteCode.D2I:
+ case ByteCode.D2L:
+ case ByteCode.DADD:
+ case ByteCode.DALOAD:
+ case ByteCode.DASTORE:
+ case ByteCode.DCMPG:
+ case ByteCode.DCMPL:
+ case ByteCode.DCONST_0:
+ case ByteCode.DCONST_1:
+ case ByteCode.DDIV:
+ case ByteCode.DLOAD_0:
+ case ByteCode.DLOAD_1:
+ case ByteCode.DLOAD_2:
+ case ByteCode.DLOAD_3:
+ case ByteCode.DMUL:
+ case ByteCode.DNEG:
+ case ByteCode.DREM:
+ case ByteCode.DRETURN:
+ case ByteCode.DSTORE_0:
+ case ByteCode.DSTORE_1:
+ case ByteCode.DSTORE_2:
+ case ByteCode.DSTORE_3:
+ case ByteCode.DSUB:
+ case ByteCode.DUP:
+ case ByteCode.DUP2:
+ case ByteCode.DUP2_X1:
+ case ByteCode.DUP2_X2:
+ case ByteCode.DUP_X1:
+ case ByteCode.DUP_X2:
+ case ByteCode.F2D:
+ case ByteCode.F2I:
+ case ByteCode.F2L:
+ case ByteCode.FADD:
+ case ByteCode.FALOAD:
+ case ByteCode.FASTORE:
+ case ByteCode.FCMPG:
+ case ByteCode.FCMPL:
+ case ByteCode.FCONST_0:
+ case ByteCode.FCONST_1:
+ case ByteCode.FCONST_2:
+ case ByteCode.FDIV:
+ case ByteCode.FLOAD_0:
+ case ByteCode.FLOAD_1:
+ case ByteCode.FLOAD_2:
+ case ByteCode.FLOAD_3:
+ case ByteCode.FMUL:
+ case ByteCode.FNEG:
+ case ByteCode.FREM:
+ case ByteCode.FRETURN:
+ case ByteCode.FSTORE_0:
+ case ByteCode.FSTORE_1:
+ case ByteCode.FSTORE_2:
+ case ByteCode.FSTORE_3:
+ case ByteCode.FSUB:
+ case ByteCode.I2B:
+ case ByteCode.I2C:
+ case ByteCode.I2D:
+ case ByteCode.I2F:
+ case ByteCode.I2L:
+ case ByteCode.I2S:
+ case ByteCode.IADD:
+ case ByteCode.IALOAD:
+ case ByteCode.IAND:
+ case ByteCode.IASTORE:
+ case ByteCode.ICONST_0:
+ case ByteCode.ICONST_1:
+ case ByteCode.ICONST_2:
+ case ByteCode.ICONST_3:
+ case ByteCode.ICONST_4:
+ case ByteCode.ICONST_5:
+ case ByteCode.ICONST_M1:
+ case ByteCode.IDIV:
+ case ByteCode.ILOAD_0:
+ case ByteCode.ILOAD_1:
+ case ByteCode.ILOAD_2:
+ case ByteCode.ILOAD_3:
+ case ByteCode.IMPDEP1:
+ case ByteCode.IMPDEP2:
+ case ByteCode.IMUL:
+ case ByteCode.INEG:
+ case ByteCode.IOR:
+ case ByteCode.IREM:
+ case ByteCode.IRETURN:
+ case ByteCode.ISHL:
+ case ByteCode.ISHR:
+ case ByteCode.ISTORE_0:
+ case ByteCode.ISTORE_1:
+ case ByteCode.ISTORE_2:
+ case ByteCode.ISTORE_3:
+ case ByteCode.ISUB:
+ case ByteCode.IUSHR:
+ case ByteCode.IXOR:
+ case ByteCode.L2D:
+ case ByteCode.L2F:
+ case ByteCode.L2I:
+ case ByteCode.LADD:
+ case ByteCode.LALOAD:
+ case ByteCode.LAND:
+ case ByteCode.LASTORE:
+ case ByteCode.LCMP:
+ case ByteCode.LCONST_0:
+ case ByteCode.LCONST_1:
+ case ByteCode.LDIV:
+ case ByteCode.LLOAD_0:
+ case ByteCode.LLOAD_1:
+ case ByteCode.LLOAD_2:
+ case ByteCode.LLOAD_3:
+ case ByteCode.LMUL:
+ case ByteCode.LNEG:
+ case ByteCode.LOR:
+ case ByteCode.LREM:
+ case ByteCode.LRETURN:
+ case ByteCode.LSHL:
+ case ByteCode.LSHR:
+ case ByteCode.LSTORE_0:
+ case ByteCode.LSTORE_1:
+ case ByteCode.LSTORE_2:
+ case ByteCode.LSTORE_3:
+ case ByteCode.LSUB:
+ case ByteCode.LUSHR:
+ case ByteCode.LXOR:
+ case ByteCode.MONITORENTER:
+ case ByteCode.MONITOREXIT:
+ case ByteCode.NOP:
+ case ByteCode.POP:
+ case ByteCode.POP2:
+ case ByteCode.RETURN:
+ case ByteCode.SALOAD:
+ case ByteCode.SASTORE:
+ case ByteCode.SWAP:
+ case ByteCode.WIDE:
+ return 1;
+ case ByteCode.BIPUSH:
+ case ByteCode.LDC:
+ case ByteCode.NEWARRAY:
+ return 2;
+ case ByteCode.ALOAD:
+ case ByteCode.ASTORE:
+ case ByteCode.DLOAD:
+ case ByteCode.DSTORE:
+ case ByteCode.FLOAD:
+ case ByteCode.FSTORE:
+ case ByteCode.ILOAD:
+ case ByteCode.ISTORE:
+ case ByteCode.LLOAD:
+ case ByteCode.LSTORE:
+ case ByteCode.RET:
+ return wide ? 3 : 2;
+
+ case ByteCode.ANEWARRAY:
+ case ByteCode.CHECKCAST:
+ case ByteCode.GETFIELD:
+ case ByteCode.GETSTATIC:
+ case ByteCode.GOTO:
+ case ByteCode.IFEQ:
+ case ByteCode.IFGE:
+ case ByteCode.IFGT:
+ case ByteCode.IFLE:
+ case ByteCode.IFLT:
+ case ByteCode.IFNE:
+ case ByteCode.IFNONNULL:
+ case ByteCode.IFNULL:
+ case ByteCode.IF_ACMPEQ:
+ case ByteCode.IF_ACMPNE:
+ case ByteCode.IF_ICMPEQ:
+ case ByteCode.IF_ICMPGE:
+ case ByteCode.IF_ICMPGT:
+ case ByteCode.IF_ICMPLE:
+ case ByteCode.IF_ICMPLT:
+ case ByteCode.IF_ICMPNE:
+ case ByteCode.INSTANCEOF:
+ case ByteCode.INVOKESPECIAL:
+ case ByteCode.INVOKESTATIC:
+ case ByteCode.INVOKEVIRTUAL:
+ case ByteCode.JSR:
+ case ByteCode.LDC_W:
+ case ByteCode.LDC2_W:
+ case ByteCode.NEW:
+ case ByteCode.PUTFIELD:
+ case ByteCode.PUTSTATIC:
+ case ByteCode.SIPUSH:
+ return 3;
+
+ case ByteCode.IINC:
+ return wide ? 5 : 3;
+
+ case ByteCode.MULTIANEWARRAY:
+ return 4;
+
+ case ByteCode.GOTO_W:
+ case ByteCode.INVOKEINTERFACE:
+ case ByteCode.JSR_W:
+ return 5;
+
+ /*
+ case ByteCode.LOOKUPSWITCH:
+ case ByteCode.TABLESWITCH:
+ return -1;
+ */
+ }
+ throw new IllegalArgumentException("Bad opcode: " + opcode);
+ }
+
+ /**
+ * Number of operands accompanying the opcode.
+ */
+ static int opcodeCount(int opcode)
+ {
+ switch (opcode) {
+ case ByteCode.AALOAD:
+ case ByteCode.AASTORE:
+ case ByteCode.ACONST_NULL:
+ case ByteCode.ALOAD_0:
+ case ByteCode.ALOAD_1:
+ case ByteCode.ALOAD_2:
+ case ByteCode.ALOAD_3:
+ case ByteCode.ARETURN:
+ case ByteCode.ARRAYLENGTH:
+ case ByteCode.ASTORE_0:
+ case ByteCode.ASTORE_1:
+ case ByteCode.ASTORE_2:
+ case ByteCode.ASTORE_3:
+ case ByteCode.ATHROW:
+ case ByteCode.BALOAD:
+ case ByteCode.BASTORE:
+ case ByteCode.BREAKPOINT:
+ case ByteCode.CALOAD:
+ case ByteCode.CASTORE:
+ case ByteCode.D2F:
+ case ByteCode.D2I:
+ case ByteCode.D2L:
+ case ByteCode.DADD:
+ case ByteCode.DALOAD:
+ case ByteCode.DASTORE:
+ case ByteCode.DCMPG:
+ case ByteCode.DCMPL:
+ case ByteCode.DCONST_0:
+ case ByteCode.DCONST_1:
+ case ByteCode.DDIV:
+ case ByteCode.DLOAD_0:
+ case ByteCode.DLOAD_1:
+ case ByteCode.DLOAD_2:
+ case ByteCode.DLOAD_3:
+ case ByteCode.DMUL:
+ case ByteCode.DNEG:
+ case ByteCode.DREM:
+ case ByteCode.DRETURN:
+ case ByteCode.DSTORE_0:
+ case ByteCode.DSTORE_1:
+ case ByteCode.DSTORE_2:
+ case ByteCode.DSTORE_3:
+ case ByteCode.DSUB:
+ case ByteCode.DUP:
+ case ByteCode.DUP2:
+ case ByteCode.DUP2_X1:
+ case ByteCode.DUP2_X2:
+ case ByteCode.DUP_X1:
+ case ByteCode.DUP_X2:
+ case ByteCode.F2D:
+ case ByteCode.F2I:
+ case ByteCode.F2L:
+ case ByteCode.FADD:
+ case ByteCode.FALOAD:
+ case ByteCode.FASTORE:
+ case ByteCode.FCMPG:
+ case ByteCode.FCMPL:
+ case ByteCode.FCONST_0:
+ case ByteCode.FCONST_1:
+ case ByteCode.FCONST_2:
+ case ByteCode.FDIV:
+ case ByteCode.FLOAD_0:
+ case ByteCode.FLOAD_1:
+ case ByteCode.FLOAD_2:
+ case ByteCode.FLOAD_3:
+ case ByteCode.FMUL:
+ case ByteCode.FNEG:
+ case ByteCode.FREM:
+ case ByteCode.FRETURN:
+ case ByteCode.FSTORE_0:
+ case ByteCode.FSTORE_1:
+ case ByteCode.FSTORE_2:
+ case ByteCode.FSTORE_3:
+ case ByteCode.FSUB:
+ case ByteCode.I2B:
+ case ByteCode.I2C:
+ case ByteCode.I2D:
+ case ByteCode.I2F:
+ case ByteCode.I2L:
+ case ByteCode.I2S:
+ case ByteCode.IADD:
+ case ByteCode.IALOAD:
+ case ByteCode.IAND:
+ case ByteCode.IASTORE:
+ case ByteCode.ICONST_0:
+ case ByteCode.ICONST_1:
+ case ByteCode.ICONST_2:
+ case ByteCode.ICONST_3:
+ case ByteCode.ICONST_4:
+ case ByteCode.ICONST_5:
+ case ByteCode.ICONST_M1:
+ case ByteCode.IDIV:
+ case ByteCode.ILOAD_0:
+ case ByteCode.ILOAD_1:
+ case ByteCode.ILOAD_2:
+ case ByteCode.ILOAD_3:
+ case ByteCode.IMPDEP1:
+ case ByteCode.IMPDEP2:
+ case ByteCode.IMUL:
+ case ByteCode.INEG:
+ case ByteCode.IOR:
+ case ByteCode.IREM:
+ case ByteCode.IRETURN:
+ case ByteCode.ISHL:
+ case ByteCode.ISHR:
+ case ByteCode.ISTORE_0:
+ case ByteCode.ISTORE_1:
+ case ByteCode.ISTORE_2:
+ case ByteCode.ISTORE_3:
+ case ByteCode.ISUB:
+ case ByteCode.IUSHR:
+ case ByteCode.IXOR:
+ case ByteCode.L2D:
+ case ByteCode.L2F:
+ case ByteCode.L2I:
+ case ByteCode.LADD:
+ case ByteCode.LALOAD:
+ case ByteCode.LAND:
+ case ByteCode.LASTORE:
+ case ByteCode.LCMP:
+ case ByteCode.LCONST_0:
+ case ByteCode.LCONST_1:
+ case ByteCode.LDIV:
+ case ByteCode.LLOAD_0:
+ case ByteCode.LLOAD_1:
+ case ByteCode.LLOAD_2:
+ case ByteCode.LLOAD_3:
+ case ByteCode.LMUL:
+ case ByteCode.LNEG:
+ case ByteCode.LOR:
+ case ByteCode.LREM:
+ case ByteCode.LRETURN:
+ case ByteCode.LSHL:
+ case ByteCode.LSHR:
+ case ByteCode.LSTORE_0:
+ case ByteCode.LSTORE_1:
+ case ByteCode.LSTORE_2:
+ case ByteCode.LSTORE_3:
+ case ByteCode.LSUB:
+ case ByteCode.LUSHR:
+ case ByteCode.LXOR:
+ case ByteCode.MONITORENTER:
+ case ByteCode.MONITOREXIT:
+ case ByteCode.NOP:
+ case ByteCode.POP:
+ case ByteCode.POP2:
+ case ByteCode.RETURN:
+ case ByteCode.SALOAD:
+ case ByteCode.SASTORE:
+ case ByteCode.SWAP:
+ case ByteCode.WIDE:
+ return 0;
+ case ByteCode.ALOAD:
+ case ByteCode.ANEWARRAY:
+ case ByteCode.ASTORE:
+ case ByteCode.BIPUSH:
+ case ByteCode.CHECKCAST:
+ case ByteCode.DLOAD:
+ case ByteCode.DSTORE:
+ case ByteCode.FLOAD:
+ case ByteCode.FSTORE:
+ case ByteCode.GETFIELD:
+ case ByteCode.GETSTATIC:
+ case ByteCode.GOTO:
+ case ByteCode.GOTO_W:
+ case ByteCode.IFEQ:
+ case ByteCode.IFGE:
+ case ByteCode.IFGT:
+ case ByteCode.IFLE:
+ case ByteCode.IFLT:
+ case ByteCode.IFNE:
+ case ByteCode.IFNONNULL:
+ case ByteCode.IFNULL:
+ case ByteCode.IF_ACMPEQ:
+ case ByteCode.IF_ACMPNE:
+ case ByteCode.IF_ICMPEQ:
+ case ByteCode.IF_ICMPGE:
+ case ByteCode.IF_ICMPGT:
+ case ByteCode.IF_ICMPLE:
+ case ByteCode.IF_ICMPLT:
+ case ByteCode.IF_ICMPNE:
+ case ByteCode.ILOAD:
+ case ByteCode.INSTANCEOF:
+ case ByteCode.INVOKEINTERFACE:
+ case ByteCode.INVOKESPECIAL:
+ case ByteCode.INVOKESTATIC:
+ case ByteCode.INVOKEVIRTUAL:
+ case ByteCode.ISTORE:
+ case ByteCode.JSR:
+ case ByteCode.JSR_W:
+ case ByteCode.LDC:
+ case ByteCode.LDC2_W:
+ case ByteCode.LDC_W:
+ case ByteCode.LLOAD:
+ case ByteCode.LSTORE:
+ case ByteCode.NEW:
+ case ByteCode.NEWARRAY:
+ case ByteCode.PUTFIELD:
+ case ByteCode.PUTSTATIC:
+ case ByteCode.RET:
+ case ByteCode.SIPUSH:
+ return 1;
+
+ case ByteCode.IINC:
+ case ByteCode.MULTIANEWARRAY:
+ return 2;
+
+ case ByteCode.LOOKUPSWITCH:
+ case ByteCode.TABLESWITCH:
+ return -1;
+ }
+ throw new IllegalArgumentException("Bad opcode: "+opcode);
+ }
+
+ /**
+ * The effect on the operand stack of a given opcode.
+ */
+ static int stackChange(int opcode)
+ {
+ // For INVOKE... accounts only for popping this (unless static),
+ // ignoring parameters and return type
+ switch (opcode) {
+ case ByteCode.DASTORE:
+ case ByteCode.LASTORE:
+ return -4;
+
+ case ByteCode.AASTORE:
+ case ByteCode.BASTORE:
+ case ByteCode.CASTORE:
+ case ByteCode.DCMPG:
+ case ByteCode.DCMPL:
+ case ByteCode.FASTORE:
+ case ByteCode.IASTORE:
+ case ByteCode.LCMP:
+ case ByteCode.SASTORE:
+ return -3;
+
+ case ByteCode.DADD:
+ case ByteCode.DDIV:
+ case ByteCode.DMUL:
+ case ByteCode.DREM:
+ case ByteCode.DRETURN:
+ case ByteCode.DSTORE:
+ case ByteCode.DSTORE_0:
+ case ByteCode.DSTORE_1:
+ case ByteCode.DSTORE_2:
+ case ByteCode.DSTORE_3:
+ case ByteCode.DSUB:
+ case ByteCode.IF_ACMPEQ:
+ case ByteCode.IF_ACMPNE:
+ case ByteCode.IF_ICMPEQ:
+ case ByteCode.IF_ICMPGE:
+ case ByteCode.IF_ICMPGT:
+ case ByteCode.IF_ICMPLE:
+ case ByteCode.IF_ICMPLT:
+ case ByteCode.IF_ICMPNE:
+ case ByteCode.LADD:
+ case ByteCode.LAND:
+ case ByteCode.LDIV:
+ case ByteCode.LMUL:
+ case ByteCode.LOR:
+ case ByteCode.LREM:
+ case ByteCode.LRETURN:
+ case ByteCode.LSTORE:
+ case ByteCode.LSTORE_0:
+ case ByteCode.LSTORE_1:
+ case ByteCode.LSTORE_2:
+ case ByteCode.LSTORE_3:
+ case ByteCode.LSUB:
+ case ByteCode.LXOR:
+ case ByteCode.POP2:
+ return -2;
+
+ case ByteCode.AALOAD:
+ case ByteCode.ARETURN:
+ case ByteCode.ASTORE:
+ case ByteCode.ASTORE_0:
+ case ByteCode.ASTORE_1:
+ case ByteCode.ASTORE_2:
+ case ByteCode.ASTORE_3:
+ case ByteCode.ATHROW:
+ case ByteCode.BALOAD:
+ case ByteCode.CALOAD:
+ case ByteCode.D2F:
+ case ByteCode.D2I:
+ case ByteCode.FADD:
+ case ByteCode.FALOAD:
+ case ByteCode.FCMPG:
+ case ByteCode.FCMPL:
+ case ByteCode.FDIV:
+ case ByteCode.FMUL:
+ case ByteCode.FREM:
+ case ByteCode.FRETURN:
+ case ByteCode.FSTORE:
+ case ByteCode.FSTORE_0:
+ case ByteCode.FSTORE_1:
+ case ByteCode.FSTORE_2:
+ case ByteCode.FSTORE_3:
+ case ByteCode.FSUB:
+ case ByteCode.GETFIELD:
+ case ByteCode.IADD:
+ case ByteCode.IALOAD:
+ case ByteCode.IAND:
+ case ByteCode.IDIV:
+ case ByteCode.IFEQ:
+ case ByteCode.IFGE:
+ case ByteCode.IFGT:
+ case ByteCode.IFLE:
+ case ByteCode.IFLT:
+ case ByteCode.IFNE:
+ case ByteCode.IFNONNULL:
+ case ByteCode.IFNULL:
+ case ByteCode.IMUL:
+ case ByteCode.INVOKEINTERFACE: //
+ case ByteCode.INVOKESPECIAL: // but needs to account for
+ case ByteCode.INVOKEVIRTUAL: // pops 'this' (unless static)
+ case ByteCode.IOR:
+ case ByteCode.IREM:
+ case ByteCode.IRETURN:
+ case ByteCode.ISHL:
+ case ByteCode.ISHR:
+ case ByteCode.ISTORE:
+ case ByteCode.ISTORE_0:
+ case ByteCode.ISTORE_1:
+ case ByteCode.ISTORE_2:
+ case ByteCode.ISTORE_3:
+ case ByteCode.ISUB:
+ case ByteCode.IUSHR:
+ case ByteCode.IXOR:
+ case ByteCode.L2F:
+ case ByteCode.L2I:
+ case ByteCode.LOOKUPSWITCH:
+ case ByteCode.LSHL:
+ case ByteCode.LSHR:
+ case ByteCode.LUSHR:
+ case ByteCode.MONITORENTER:
+ case ByteCode.MONITOREXIT:
+ case ByteCode.POP:
+ case ByteCode.PUTFIELD:
+ case ByteCode.SALOAD:
+ case ByteCode.TABLESWITCH:
+ return -1;
+
+ case ByteCode.ANEWARRAY:
+ case ByteCode.ARRAYLENGTH:
+ case ByteCode.BREAKPOINT:
+ case ByteCode.CHECKCAST:
+ case ByteCode.D2L:
+ case ByteCode.DALOAD:
+ case ByteCode.DNEG:
+ case ByteCode.F2I:
+ case ByteCode.FNEG:
+ case ByteCode.GETSTATIC:
+ case ByteCode.GOTO:
+ case ByteCode.GOTO_W:
+ case ByteCode.I2B:
+ case ByteCode.I2C:
+ case ByteCode.I2F:
+ case ByteCode.I2S:
+ case ByteCode.IINC:
+ case ByteCode.IMPDEP1:
+ case ByteCode.IMPDEP2:
+ case ByteCode.INEG:
+ case ByteCode.INSTANCEOF:
+ case ByteCode.INVOKESTATIC:
+ case ByteCode.L2D:
+ case ByteCode.LALOAD:
+ case ByteCode.LNEG:
+ case ByteCode.NEWARRAY:
+ case ByteCode.NOP:
+ case ByteCode.PUTSTATIC:
+ case ByteCode.RET:
+ case ByteCode.RETURN:
+ case ByteCode.SWAP:
+ case ByteCode.WIDE:
+ return 0;
+
+ case ByteCode.ACONST_NULL:
+ case ByteCode.ALOAD:
+ case ByteCode.ALOAD_0:
+ case ByteCode.ALOAD_1:
+ case ByteCode.ALOAD_2:
+ case ByteCode.ALOAD_3:
+ case ByteCode.BIPUSH:
+ case ByteCode.DUP:
+ case ByteCode.DUP_X1:
+ case ByteCode.DUP_X2:
+ case ByteCode.F2D:
+ case ByteCode.F2L:
+ case ByteCode.FCONST_0:
+ case ByteCode.FCONST_1:
+ case ByteCode.FCONST_2:
+ case ByteCode.FLOAD:
+ case ByteCode.FLOAD_0:
+ case ByteCode.FLOAD_1:
+ case ByteCode.FLOAD_2:
+ case ByteCode.FLOAD_3:
+ case ByteCode.I2D:
+ case ByteCode.I2L:
+ case ByteCode.ICONST_0:
+ case ByteCode.ICONST_1:
+ case ByteCode.ICONST_2:
+ case ByteCode.ICONST_3:
+ case ByteCode.ICONST_4:
+ case ByteCode.ICONST_5:
+ case ByteCode.ICONST_M1:
+ case ByteCode.ILOAD:
+ case ByteCode.ILOAD_0:
+ case ByteCode.ILOAD_1:
+ case ByteCode.ILOAD_2:
+ case ByteCode.ILOAD_3:
+ case ByteCode.JSR:
+ case ByteCode.JSR_W:
+ case ByteCode.LDC:
+ case ByteCode.LDC_W:
+ case ByteCode.MULTIANEWARRAY:
+ case ByteCode.NEW:
+ case ByteCode.SIPUSH:
+ return 1;
+
+ case ByteCode.DCONST_0:
+ case ByteCode.DCONST_1:
+ case ByteCode.DLOAD:
+ case ByteCode.DLOAD_0:
+ case ByteCode.DLOAD_1:
+ case ByteCode.DLOAD_2:
+ case ByteCode.DLOAD_3:
+ case ByteCode.DUP2:
+ case ByteCode.DUP2_X1:
+ case ByteCode.DUP2_X2:
+ case ByteCode.LCONST_0:
+ case ByteCode.LCONST_1:
+ case ByteCode.LDC2_W:
+ case ByteCode.LLOAD:
+ case ByteCode.LLOAD_0:
+ case ByteCode.LLOAD_1:
+ case ByteCode.LLOAD_2:
+ case ByteCode.LLOAD_3:
+ return 2;
+ }
+ throw new IllegalArgumentException("Bad opcode: "+opcode);
+ }
+
+ /*
+ * Number of bytes of operands generated after the opcode.
+ * Not in use currently.
+ */
+/*
+ int extra(int opcode)
+ {
+ switch (opcode) {
+ case ByteCode.AALOAD:
+ case ByteCode.AASTORE:
+ case ByteCode.ACONST_NULL:
+ case ByteCode.ALOAD_0:
+ case ByteCode.ALOAD_1:
+ case ByteCode.ALOAD_2:
+ case ByteCode.ALOAD_3:
+ case ByteCode.ARETURN:
+ case ByteCode.ARRAYLENGTH:
+ case ByteCode.ASTORE_0:
+ case ByteCode.ASTORE_1:
+ case ByteCode.ASTORE_2:
+ case ByteCode.ASTORE_3:
+ case ByteCode.ATHROW:
+ case ByteCode.BALOAD:
+ case ByteCode.BASTORE:
+ case ByteCode.BREAKPOINT:
+ case ByteCode.CALOAD:
+ case ByteCode.CASTORE:
+ case ByteCode.D2F:
+ case ByteCode.D2I:
+ case ByteCode.D2L:
+ case ByteCode.DADD:
+ case ByteCode.DALOAD:
+ case ByteCode.DASTORE:
+ case ByteCode.DCMPG:
+ case ByteCode.DCMPL:
+ case ByteCode.DCONST_0:
+ case ByteCode.DCONST_1:
+ case ByteCode.DDIV:
+ case ByteCode.DLOAD_0:
+ case ByteCode.DLOAD_1:
+ case ByteCode.DLOAD_2:
+ case ByteCode.DLOAD_3:
+ case ByteCode.DMUL:
+ case ByteCode.DNEG:
+ case ByteCode.DREM:
+ case ByteCode.DRETURN:
+ case ByteCode.DSTORE_0:
+ case ByteCode.DSTORE_1:
+ case ByteCode.DSTORE_2:
+ case ByteCode.DSTORE_3:
+ case ByteCode.DSUB:
+ case ByteCode.DUP2:
+ case ByteCode.DUP2_X1:
+ case ByteCode.DUP2_X2:
+ case ByteCode.DUP:
+ case ByteCode.DUP_X1:
+ case ByteCode.DUP_X2:
+ case ByteCode.F2D:
+ case ByteCode.F2I:
+ case ByteCode.F2L:
+ case ByteCode.FADD:
+ case ByteCode.FALOAD:
+ case ByteCode.FASTORE:
+ case ByteCode.FCMPG:
+ case ByteCode.FCMPL:
+ case ByteCode.FCONST_0:
+ case ByteCode.FCONST_1:
+ case ByteCode.FCONST_2:
+ case ByteCode.FDIV:
+ case ByteCode.FLOAD_0:
+ case ByteCode.FLOAD_1:
+ case ByteCode.FLOAD_2:
+ case ByteCode.FLOAD_3:
+ case ByteCode.FMUL:
+ case ByteCode.FNEG:
+ case ByteCode.FREM:
+ case ByteCode.FRETURN:
+ case ByteCode.FSTORE_0:
+ case ByteCode.FSTORE_1:
+ case ByteCode.FSTORE_2:
+ case ByteCode.FSTORE_3:
+ case ByteCode.FSUB:
+ case ByteCode.I2B:
+ case ByteCode.I2C:
+ case ByteCode.I2D:
+ case ByteCode.I2F:
+ case ByteCode.I2L:
+ case ByteCode.I2S:
+ case ByteCode.IADD:
+ case ByteCode.IALOAD:
+ case ByteCode.IAND:
+ case ByteCode.IASTORE:
+ case ByteCode.ICONST_0:
+ case ByteCode.ICONST_1:
+ case ByteCode.ICONST_2:
+ case ByteCode.ICONST_3:
+ case ByteCode.ICONST_4:
+ case ByteCode.ICONST_5:
+ case ByteCode.ICONST_M1:
+ case ByteCode.IDIV:
+ case ByteCode.ILOAD_0:
+ case ByteCode.ILOAD_1:
+ case ByteCode.ILOAD_2:
+ case ByteCode.ILOAD_3:
+ case ByteCode.IMPDEP1:
+ case ByteCode.IMPDEP2:
+ case ByteCode.IMUL:
+ case ByteCode.INEG:
+ case ByteCode.IOR:
+ case ByteCode.IREM:
+ case ByteCode.IRETURN:
+ case ByteCode.ISHL:
+ case ByteCode.ISHR:
+ case ByteCode.ISTORE_0:
+ case ByteCode.ISTORE_1:
+ case ByteCode.ISTORE_2:
+ case ByteCode.ISTORE_3:
+ case ByteCode.ISUB:
+ case ByteCode.IUSHR:
+ case ByteCode.IXOR:
+ case ByteCode.L2D:
+ case ByteCode.L2F:
+ case ByteCode.L2I:
+ case ByteCode.LADD:
+ case ByteCode.LALOAD:
+ case ByteCode.LAND:
+ case ByteCode.LASTORE:
+ case ByteCode.LCMP:
+ case ByteCode.LCONST_0:
+ case ByteCode.LCONST_1:
+ case ByteCode.LDIV:
+ case ByteCode.LLOAD_0:
+ case ByteCode.LLOAD_1:
+ case ByteCode.LLOAD_2:
+ case ByteCode.LLOAD_3:
+ case ByteCode.LMUL:
+ case ByteCode.LNEG:
+ case ByteCode.LOR:
+ case ByteCode.LREM:
+ case ByteCode.LRETURN:
+ case ByteCode.LSHL:
+ case ByteCode.LSHR:
+ case ByteCode.LSTORE_0:
+ case ByteCode.LSTORE_1:
+ case ByteCode.LSTORE_2:
+ case ByteCode.LSTORE_3:
+ case ByteCode.LSUB:
+ case ByteCode.LUSHR:
+ case ByteCode.LXOR:
+ case ByteCode.MONITORENTER:
+ case ByteCode.MONITOREXIT:
+ case ByteCode.NOP:
+ case ByteCode.POP2:
+ case ByteCode.POP:
+ case ByteCode.RETURN:
+ case ByteCode.SALOAD:
+ case ByteCode.SASTORE:
+ case ByteCode.SWAP:
+ case ByteCode.WIDE:
+ return 0;
+
+ case ByteCode.ALOAD:
+ case ByteCode.ASTORE:
+ case ByteCode.BIPUSH:
+ case ByteCode.DLOAD:
+ case ByteCode.DSTORE:
+ case ByteCode.FLOAD:
+ case ByteCode.FSTORE:
+ case ByteCode.ILOAD:
+ case ByteCode.ISTORE:
+ case ByteCode.LDC:
+ case ByteCode.LLOAD:
+ case ByteCode.LSTORE:
+ case ByteCode.NEWARRAY:
+ case ByteCode.RET:
+ return 1;
+
+ case ByteCode.ANEWARRAY:
+ case ByteCode.CHECKCAST:
+ case ByteCode.GETFIELD:
+ case ByteCode.GETSTATIC:
+ case ByteCode.GOTO:
+ case ByteCode.IFEQ:
+ case ByteCode.IFGE:
+ case ByteCode.IFGT:
+ case ByteCode.IFLE:
+ case ByteCode.IFLT:
+ case ByteCode.IFNE:
+ case ByteCode.IFNONNULL:
+ case ByteCode.IFNULL:
+ case ByteCode.IF_ACMPEQ:
+ case ByteCode.IF_ACMPNE:
+ case ByteCode.IF_ICMPEQ:
+ case ByteCode.IF_ICMPGE:
+ case ByteCode.IF_ICMPGT:
+ case ByteCode.IF_ICMPLE:
+ case ByteCode.IF_ICMPLT:
+ case ByteCode.IF_ICMPNE:
+ case ByteCode.IINC:
+ case ByteCode.INSTANCEOF:
+ case ByteCode.INVOKEINTERFACE:
+ case ByteCode.INVOKESPECIAL:
+ case ByteCode.INVOKESTATIC:
+ case ByteCode.INVOKEVIRTUAL:
+ case ByteCode.JSR:
+ case ByteCode.LDC2_W:
+ case ByteCode.LDC_W:
+ case ByteCode.NEW:
+ case ByteCode.PUTFIELD:
+ case ByteCode.PUTSTATIC:
+ case ByteCode.SIPUSH:
+ return 2;
+
+ case ByteCode.MULTIANEWARRAY:
+ return 3;
+
+ case ByteCode.GOTO_W:
+ case ByteCode.JSR_W:
+ return 4;
+
+ case ByteCode.LOOKUPSWITCH: // depends on alignment
+ case ByteCode.TABLESWITCH: // depends on alignment
+ return -1;
+ }
+ throw new IllegalArgumentException("Bad opcode: "+opcode);
+ }
+*/
+ @SuppressWarnings("unused")
+ private static String bytecodeStr(int code)
+ {
+ if (DEBUGSTACK || DEBUGCODE) {
+ switch (code) {
+ case ByteCode.NOP: return "nop";
+ case ByteCode.ACONST_NULL: return "aconst_null";
+ case ByteCode.ICONST_M1: return "iconst_m1";
+ case ByteCode.ICONST_0: return "iconst_0";
+ case ByteCode.ICONST_1: return "iconst_1";
+ case ByteCode.ICONST_2: return "iconst_2";
+ case ByteCode.ICONST_3: return "iconst_3";
+ case ByteCode.ICONST_4: return "iconst_4";
+ case ByteCode.ICONST_5: return "iconst_5";
+ case ByteCode.LCONST_0: return "lconst_0";
+ case ByteCode.LCONST_1: return "lconst_1";
+ case ByteCode.FCONST_0: return "fconst_0";
+ case ByteCode.FCONST_1: return "fconst_1";
+ case ByteCode.FCONST_2: return "fconst_2";
+ case ByteCode.DCONST_0: return "dconst_0";
+ case ByteCode.DCONST_1: return "dconst_1";
+ case ByteCode.BIPUSH: return "bipush";
+ case ByteCode.SIPUSH: return "sipush";
+ case ByteCode.LDC: return "ldc";
+ case ByteCode.LDC_W: return "ldc_w";
+ case ByteCode.LDC2_W: return "ldc2_w";
+ case ByteCode.ILOAD: return "iload";
+ case ByteCode.LLOAD: return "lload";
+ case ByteCode.FLOAD: return "fload";
+ case ByteCode.DLOAD: return "dload";
+ case ByteCode.ALOAD: return "aload";
+ case ByteCode.ILOAD_0: return "iload_0";
+ case ByteCode.ILOAD_1: return "iload_1";
+ case ByteCode.ILOAD_2: return "iload_2";
+ case ByteCode.ILOAD_3: return "iload_3";
+ case ByteCode.LLOAD_0: return "lload_0";
+ case ByteCode.LLOAD_1: return "lload_1";
+ case ByteCode.LLOAD_2: return "lload_2";
+ case ByteCode.LLOAD_3: return "lload_3";
+ case ByteCode.FLOAD_0: return "fload_0";
+ case ByteCode.FLOAD_1: return "fload_1";
+ case ByteCode.FLOAD_2: return "fload_2";
+ case ByteCode.FLOAD_3: return "fload_3";
+ case ByteCode.DLOAD_0: return "dload_0";
+ case ByteCode.DLOAD_1: return "dload_1";
+ case ByteCode.DLOAD_2: return "dload_2";
+ case ByteCode.DLOAD_3: return "dload_3";
+ case ByteCode.ALOAD_0: return "aload_0";
+ case ByteCode.ALOAD_1: return "aload_1";
+ case ByteCode.ALOAD_2: return "aload_2";
+ case ByteCode.ALOAD_3: return "aload_3";
+ case ByteCode.IALOAD: return "iaload";
+ case ByteCode.LALOAD: return "laload";
+ case ByteCode.FALOAD: return "faload";
+ case ByteCode.DALOAD: return "daload";
+ case ByteCode.AALOAD: return "aaload";
+ case ByteCode.BALOAD: return "baload";
+ case ByteCode.CALOAD: return "caload";
+ case ByteCode.SALOAD: return "saload";
+ case ByteCode.ISTORE: return "istore";
+ case ByteCode.LSTORE: return "lstore";
+ case ByteCode.FSTORE: return "fstore";
+ case ByteCode.DSTORE: return "dstore";
+ case ByteCode.ASTORE: return "astore";
+ case ByteCode.ISTORE_0: return "istore_0";
+ case ByteCode.ISTORE_1: return "istore_1";
+ case ByteCode.ISTORE_2: return "istore_2";
+ case ByteCode.ISTORE_3: return "istore_3";
+ case ByteCode.LSTORE_0: return "lstore_0";
+ case ByteCode.LSTORE_1: return "lstore_1";
+ case ByteCode.LSTORE_2: return "lstore_2";
+ case ByteCode.LSTORE_3: return "lstore_3";
+ case ByteCode.FSTORE_0: return "fstore_0";
+ case ByteCode.FSTORE_1: return "fstore_1";
+ case ByteCode.FSTORE_2: return "fstore_2";
+ case ByteCode.FSTORE_3: return "fstore_3";
+ case ByteCode.DSTORE_0: return "dstore_0";
+ case ByteCode.DSTORE_1: return "dstore_1";
+ case ByteCode.DSTORE_2: return "dstore_2";
+ case ByteCode.DSTORE_3: return "dstore_3";
+ case ByteCode.ASTORE_0: return "astore_0";
+ case ByteCode.ASTORE_1: return "astore_1";
+ case ByteCode.ASTORE_2: return "astore_2";
+ case ByteCode.ASTORE_3: return "astore_3";
+ case ByteCode.IASTORE: return "iastore";
+ case ByteCode.LASTORE: return "lastore";
+ case ByteCode.FASTORE: return "fastore";
+ case ByteCode.DASTORE: return "dastore";
+ case ByteCode.AASTORE: return "aastore";
+ case ByteCode.BASTORE: return "bastore";
+ case ByteCode.CASTORE: return "castore";
+ case ByteCode.SASTORE: return "sastore";
+ case ByteCode.POP: return "pop";
+ case ByteCode.POP2: return "pop2";
+ case ByteCode.DUP: return "dup";
+ case ByteCode.DUP_X1: return "dup_x1";
+ case ByteCode.DUP_X2: return "dup_x2";
+ case ByteCode.DUP2: return "dup2";
+ case ByteCode.DUP2_X1: return "dup2_x1";
+ case ByteCode.DUP2_X2: return "dup2_x2";
+ case ByteCode.SWAP: return "swap";
+ case ByteCode.IADD: return "iadd";
+ case ByteCode.LADD: return "ladd";
+ case ByteCode.FADD: return "fadd";
+ case ByteCode.DADD: return "dadd";
+ case ByteCode.ISUB: return "isub";
+ case ByteCode.LSUB: return "lsub";
+ case ByteCode.FSUB: return "fsub";
+ case ByteCode.DSUB: return "dsub";
+ case ByteCode.IMUL: return "imul";
+ case ByteCode.LMUL: return "lmul";
+ case ByteCode.FMUL: return "fmul";
+ case ByteCode.DMUL: return "dmul";
+ case ByteCode.IDIV: return "idiv";
+ case ByteCode.LDIV: return "ldiv";
+ case ByteCode.FDIV: return "fdiv";
+ case ByteCode.DDIV: return "ddiv";
+ case ByteCode.IREM: return "irem";
+ case ByteCode.LREM: return "lrem";
+ case ByteCode.FREM: return "frem";
+ case ByteCode.DREM: return "drem";
+ case ByteCode.INEG: return "ineg";
+ case ByteCode.LNEG: return "lneg";
+ case ByteCode.FNEG: return "fneg";
+ case ByteCode.DNEG: return "dneg";
+ case ByteCode.ISHL: return "ishl";
+ case ByteCode.LSHL: return "lshl";
+ case ByteCode.ISHR: return "ishr";
+ case ByteCode.LSHR: return "lshr";
+ case ByteCode.IUSHR: return "iushr";
+ case ByteCode.LUSHR: return "lushr";
+ case ByteCode.IAND: return "iand";
+ case ByteCode.LAND: return "land";
+ case ByteCode.IOR: return "ior";
+ case ByteCode.LOR: return "lor";
+ case ByteCode.IXOR: return "ixor";
+ case ByteCode.LXOR: return "lxor";
+ case ByteCode.IINC: return "iinc";
+ case ByteCode.I2L: return "i2l";
+ case ByteCode.I2F: return "i2f";
+ case ByteCode.I2D: return "i2d";
+ case ByteCode.L2I: return "l2i";
+ case ByteCode.L2F: return "l2f";
+ case ByteCode.L2D: return "l2d";
+ case ByteCode.F2I: return "f2i";
+ case ByteCode.F2L: return "f2l";
+ case ByteCode.F2D: return "f2d";
+ case ByteCode.D2I: return "d2i";
+ case ByteCode.D2L: return "d2l";
+ case ByteCode.D2F: return "d2f";
+ case ByteCode.I2B: return "i2b";
+ case ByteCode.I2C: return "i2c";
+ case ByteCode.I2S: return "i2s";
+ case ByteCode.LCMP: return "lcmp";
+ case ByteCode.FCMPL: return "fcmpl";
+ case ByteCode.FCMPG: return "fcmpg";
+ case ByteCode.DCMPL: return "dcmpl";
+ case ByteCode.DCMPG: return "dcmpg";
+ case ByteCode.IFEQ: return "ifeq";
+ case ByteCode.IFNE: return "ifne";
+ case ByteCode.IFLT: return "iflt";
+ case ByteCode.IFGE: return "ifge";
+ case ByteCode.IFGT: return "ifgt";
+ case ByteCode.IFLE: return "ifle";
+ case ByteCode.IF_ICMPEQ: return "if_icmpeq";
+ case ByteCode.IF_ICMPNE: return "if_icmpne";
+ case ByteCode.IF_ICMPLT: return "if_icmplt";
+ case ByteCode.IF_ICMPGE: return "if_icmpge";
+ case ByteCode.IF_ICMPGT: return "if_icmpgt";
+ case ByteCode.IF_ICMPLE: return "if_icmple";
+ case ByteCode.IF_ACMPEQ: return "if_acmpeq";
+ case ByteCode.IF_ACMPNE: return "if_acmpne";
+ case ByteCode.GOTO: return "goto";
+ case ByteCode.JSR: return "jsr";
+ case ByteCode.RET: return "ret";
+ case ByteCode.TABLESWITCH: return "tableswitch";
+ case ByteCode.LOOKUPSWITCH: return "lookupswitch";
+ case ByteCode.IRETURN: return "ireturn";
+ case ByteCode.LRETURN: return "lreturn";
+ case ByteCode.FRETURN: return "freturn";
+ case ByteCode.DRETURN: return "dreturn";
+ case ByteCode.ARETURN: return "areturn";
+ case ByteCode.RETURN: return "return";
+ case ByteCode.GETSTATIC: return "getstatic";
+ case ByteCode.PUTSTATIC: return "putstatic";
+ case ByteCode.GETFIELD: return "getfield";
+ case ByteCode.PUTFIELD: return "putfield";
+ case ByteCode.INVOKEVIRTUAL: return "invokevirtual";
+ case ByteCode.INVOKESPECIAL: return "invokespecial";
+ case ByteCode.INVOKESTATIC: return "invokestatic";
+ case ByteCode.INVOKEINTERFACE: return "invokeinterface";
+ case ByteCode.NEW: return "new";
+ case ByteCode.NEWARRAY: return "newarray";
+ case ByteCode.ANEWARRAY: return "anewarray";
+ case ByteCode.ARRAYLENGTH: return "arraylength";
+ case ByteCode.ATHROW: return "athrow";
+ case ByteCode.CHECKCAST: return "checkcast";
+ case ByteCode.INSTANCEOF: return "instanceof";
+ case ByteCode.MONITORENTER: return "monitorenter";
+ case ByteCode.MONITOREXIT: return "monitorexit";
+ case ByteCode.WIDE: return "wide";
+ case ByteCode.MULTIANEWARRAY: return "multianewarray";
+ case ByteCode.IFNULL: return "ifnull";
+ case ByteCode.IFNONNULL: return "ifnonnull";
+ case ByteCode.GOTO_W: return "goto_w";
+ case ByteCode.JSR_W: return "jsr_w";
+ case ByteCode.BREAKPOINT: return "breakpoint";
+
+ case ByteCode.IMPDEP1: return "impdep1";
+ case ByteCode.IMPDEP2: return "impdep2";
+ }
+ }
+ return "";
+ }
+
+ final char[] getCharBuffer(int minimalSize)
+ {
+ if (minimalSize > tmpCharBuffer.length) {
+ int newSize = tmpCharBuffer.length * 2;
+ if (minimalSize > newSize) { newSize = minimalSize; }
+ tmpCharBuffer = new char[newSize];
+ }
+ return tmpCharBuffer;
+ }
+
+ /**
+ * Add a pc as the start of super block.
+ *
+ * A pc is the beginning of a super block if:
+ * - pc == 0
+ * - it is the target of a branch instruction
+ * - it is the beginning of an exception handler
+ * - it is directly after an unconditional jump
+ */
+ private void addSuperBlockStart(int pc) {
+ if (GenerateStackMap) {
+ if (itsSuperBlockStarts == null) {
+ itsSuperBlockStarts = new int[SuperBlockStartsSize];
+ } else if (itsSuperBlockStarts.length == itsSuperBlockStartsTop) {
+ int[] tmp = new int[itsSuperBlockStartsTop * 2];
+ System.arraycopy(itsSuperBlockStarts, 0, tmp, 0,
+ itsSuperBlockStartsTop);
+ itsSuperBlockStarts = tmp;
+ }
+ itsSuperBlockStarts[itsSuperBlockStartsTop++] = pc;
+ }
+ }
+
+ /**
+ * Sort the list of recorded super block starts and remove duplicates.
+ *
+ * Also adds exception handling blocks as block starts, since there is no
+ * explicit control flow to these. Used for stack map table generation.
+ */
+ private void finalizeSuperBlockStarts() {
+ if (GenerateStackMap) {
+ for (int i = 0; i < itsExceptionTableTop; i++) {
+ ExceptionTableEntry ete = itsExceptionTable[i];
+ short handlerPC = (short) getLabelPC(ete.itsHandlerLabel);
+ addSuperBlockStart(handlerPC);
+ }
+ Arrays.sort(itsSuperBlockStarts, 0, itsSuperBlockStartsTop);
+ int prev = itsSuperBlockStarts[0];
+ int copyTo = 1;
+ for (int i = 1; i < itsSuperBlockStartsTop; i++) {
+ int curr = itsSuperBlockStarts[i];
+ if (prev != curr) {
+ if (copyTo != i) {
+ itsSuperBlockStarts[copyTo] = curr;
+ }
+ copyTo++;
+ prev = curr;
+ }
+ }
+ itsSuperBlockStartsTop = copyTo;
+ if (itsSuperBlockStarts[copyTo - 1] == itsCodeBufferTop) {
+ itsSuperBlockStartsTop--;
+ }
+ }
+ }
+
+ private int[] itsSuperBlockStarts = null;
+ private int itsSuperBlockStartsTop = 0;
+ private static final int SuperBlockStartsSize = 4;
+
+ // Used to find blocks of code with no dependencies (aka dead code).
+ // Necessary for generating type information for dead code, which is
+ // expected by the Sun verifier. It is only necessary to store a single
+ // jump source to determine if a block is reachable or not.
+ private UintMap itsJumpFroms = null;
+
+ private static final int LineNumberTableSize = 16;
+ private static final int ExceptionTableSize = 4;
+
+ private static final int MajorVersion;
+ private static final int MinorVersion;
+ private static final boolean GenerateStackMap;
+
+ static {
+ // Figure out which classfile version should be generated. This assumes
+ // that the runtime used to compile the JavaScript files is the same as
+ // the one used to run them. This is important because there are cases
+ // when bytecode is generated at runtime, where it is not easy to pass
+ // along what version is necessary. Instead, we grab the version numbers
+ // from the bytecode of this class and use that.
+ //
+ // Based on the version numbers we scrape, we can also determine what
+ // bytecode features we need. For example, Java 6 bytecode (classfile
+ // version 50) should have stack maps generated.
+ InputStream is = null;
+ int major = 48, minor = 0;
+ try {
+ is = ClassFileWriter.class.getResourceAsStream("ClassFileWriter.class");
+ if (is == null) {
+ is = ClassLoader.getSystemResourceAsStream(
+ "org/mozilla/classfile/ClassFileWriter.class");
+ }
+ byte[] header = new byte[8];
+ // read loop is required since JDK7 will only provide 2 bytes
+ // on the first read() - see bug #630111
+ int read = 0;
+ while (read < 8) {
+ int c = is.read(header, read, 8 - read);
+ if (c < 0) throw new IOException();
+ read += c;
+ }
+ minor = (header[4] << 8) | (header[5] & 0xff);
+ major = (header[6] << 8) | (header[7] & 0xff);
+ } catch (Exception e) {
+ // Unable to get class file, use default bytecode version
+ } finally {
+ MinorVersion = minor;
+ MajorVersion = major;
+ GenerateStackMap = major >= 50;
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private final static int FileHeaderConstant = 0xCAFEBABE;
+ // Set DEBUG flags to true to get better checking and progress info.
+ private static final boolean DEBUGSTACK = false;
+ private static final boolean DEBUGLABELS = false;
+ private static final boolean DEBUGCODE = false;
+
+ private String generatedClassName;
+
+ private ExceptionTableEntry itsExceptionTable[];
+ private int itsExceptionTableTop;
+
+ private int itsLineNumberTable[]; // pack start_pc & line_number together
+ private int itsLineNumberTableTop;
+
+ private byte[] itsCodeBuffer = new byte[256];
+ private int itsCodeBufferTop;
+
+ private ConstantPool itsConstantPool;
+
+ private ClassFileMethod itsCurrentMethod;
+ private short itsStackTop;
+
+ private short itsMaxStack;
+ private short itsMaxLocals;
+
+ private ObjArray itsMethods = new ObjArray();
+ private ObjArray itsFields = new ObjArray();
+ private ObjArray itsInterfaces = new ObjArray();
+
+ private short itsFlags;
+ private short itsThisClassIndex;
+ private short itsSuperClassIndex;
+ private short itsSourceFileNameIndex;
+
+ private static final int MIN_LABEL_TABLE_SIZE = 32;
+ private int[] itsLabelTable;
+ private int itsLabelTableTop;
+
+// itsFixupTable[i] = (label_index << 32) | fixup_site
+ private static final int MIN_FIXUP_TABLE_SIZE = 40;
+ private long[] itsFixupTable;
+ private int itsFixupTableTop;
+ private ObjArray itsVarDescriptors;
+
+ private char[] tmpCharBuffer = new char[64];
+}
+
+final class ExceptionTableEntry
+{
+
+ ExceptionTableEntry(int startLabel, int endLabel,
+ int handlerLabel, short catchType)
+ {
+ itsStartLabel = startLabel;
+ itsEndLabel = endLabel;
+ itsHandlerLabel = handlerLabel;
+ itsCatchType = catchType;
+ }
+
+ int itsStartLabel;
+ int itsEndLabel;
+ int itsHandlerLabel;
+ short itsCatchType;
+}
+
+final class ClassFileField
+{
+
+ ClassFileField(short nameIndex, short typeIndex, short flags)
+ {
+ itsNameIndex = nameIndex;
+ itsTypeIndex = typeIndex;
+ itsFlags = flags;
+ itsHasAttributes = false;
+ }
+
+ void setAttributes(short attr1, short attr2, short attr3, int index)
+ {
+ itsHasAttributes = true;
+ itsAttr1 = attr1;
+ itsAttr2 = attr2;
+ itsAttr3 = attr3;
+ itsIndex = index;
+ }
+
+ int write(byte[] data, int offset)
+ {
+ offset = ClassFileWriter.putInt16(itsFlags, data, offset);
+ offset = ClassFileWriter.putInt16(itsNameIndex, data, offset);
+ offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset);
+ if (!itsHasAttributes) {
+ // write 0 attributes
+ offset = ClassFileWriter.putInt16(0, data, offset);
+ } else {
+ offset = ClassFileWriter.putInt16(1, data, offset);
+ offset = ClassFileWriter.putInt16(itsAttr1, data, offset);
+ offset = ClassFileWriter.putInt16(itsAttr2, data, offset);
+ offset = ClassFileWriter.putInt16(itsAttr3, data, offset);
+ offset = ClassFileWriter.putInt16(itsIndex, data, offset);
+ }
+ return offset;
+ }
+
+ int getWriteSize()
+ {
+ int size = 2 * 3;
+ if (!itsHasAttributes) {
+ size += 2;
+ } else {
+ size += 2 + 2 * 4;
+ }
+ return size;
+ }
+
+ private short itsNameIndex;
+ private short itsTypeIndex;
+ private short itsFlags;
+ private boolean itsHasAttributes;
+ private short itsAttr1, itsAttr2, itsAttr3;
+ private int itsIndex;
+}
+
+final class ClassFileMethod
+{
+
+ ClassFileMethod(String name, short nameIndex, String type, short typeIndex,
+ short flags)
+ {
+ itsName = name;
+ itsNameIndex = nameIndex;
+ itsType = type;
+ itsTypeIndex = typeIndex;
+ itsFlags = flags;
+ }
+
+ void setCodeAttribute(byte codeAttribute[])
+ {
+ itsCodeAttribute = codeAttribute;
+ }
+
+ int write(byte[] data, int offset)
+ {
+ offset = ClassFileWriter.putInt16(itsFlags, data, offset);
+ offset = ClassFileWriter.putInt16(itsNameIndex, data, offset);
+ offset = ClassFileWriter.putInt16(itsTypeIndex, data, offset);
+ // Code attribute only
+ offset = ClassFileWriter.putInt16(1, data, offset);
+ System.arraycopy(itsCodeAttribute, 0, data, offset,
+ itsCodeAttribute.length);
+ offset += itsCodeAttribute.length;
+ return offset;
+ }
+
+ int getWriteSize()
+ {
+ return 2 * 4 + itsCodeAttribute.length;
+ }
+
+ String getName()
+ {
+ return itsName;
+ }
+
+ String getType()
+ {
+ return itsType;
+ }
+
+ short getFlags()
+ {
+ return itsFlags;
+ }
+
+ private String itsName;
+ private String itsType;
+ private short itsNameIndex;
+ private short itsTypeIndex;
+ private short itsFlags;
+ private byte[] itsCodeAttribute;
+
+}
+
+final class ConstantPool
+{
+
+ ConstantPool(ClassFileWriter cfw)
+ {
+ this.cfw = cfw;
+ itsTopIndex = 1; // the zero'th entry is reserved
+ itsPool = new byte[ConstantPoolSize];
+ itsTop = 0;
+ }
+
+ private static final int ConstantPoolSize = 256;
+ static final byte
+ CONSTANT_Class = 7,
+ CONSTANT_Fieldref = 9,
+ CONSTANT_Methodref = 10,
+ CONSTANT_InterfaceMethodref = 11,
+ CONSTANT_String = 8,
+ CONSTANT_Integer = 3,
+ CONSTANT_Float = 4,
+ CONSTANT_Long = 5,
+ CONSTANT_Double = 6,
+ CONSTANT_NameAndType = 12,
+ CONSTANT_Utf8 = 1;
+
+ int write(byte[] data, int offset)
+ {
+ offset = ClassFileWriter.putInt16((short)itsTopIndex, data, offset);
+ System.arraycopy(itsPool, 0, data, offset, itsTop);
+ offset += itsTop;
+ return offset;
+ }
+
+ int getWriteSize()
+ {
+ return 2 + itsTop;
+ }
+
+ int addConstant(int k)
+ {
+ ensure(5);
+ itsPool[itsTop++] = CONSTANT_Integer;
+ itsTop = ClassFileWriter.putInt32(k, itsPool, itsTop);
+ itsPoolTypes.put(itsTopIndex, CONSTANT_Integer);
+ return (short)(itsTopIndex++);
+ }
+
+ int addConstant(long k)
+ {
+ ensure(9);
+ itsPool[itsTop++] = CONSTANT_Long;
+ itsTop = ClassFileWriter.putInt64(k, itsPool, itsTop);
+ int index = itsTopIndex;
+ itsTopIndex += 2;
+ itsPoolTypes.put(index, CONSTANT_Long);
+ return index;
+ }
+
+ int addConstant(float k)
+ {
+ ensure(5);
+ itsPool[itsTop++] = CONSTANT_Float;
+ int bits = Float.floatToIntBits(k);
+ itsTop = ClassFileWriter.putInt32(bits, itsPool, itsTop);
+ itsPoolTypes.put(itsTopIndex, CONSTANT_Float);
+ return itsTopIndex++;
+ }
+
+ int addConstant(double k)
+ {
+ ensure(9);
+ itsPool[itsTop++] = CONSTANT_Double;
+ long bits = Double.doubleToLongBits(k);
+ itsTop = ClassFileWriter.putInt64(bits, itsPool, itsTop);
+ int index = itsTopIndex;
+ itsTopIndex += 2;
+ itsPoolTypes.put(index, CONSTANT_Double);
+ return index;
+ }
+
+ int addConstant(String k)
+ {
+ int utf8Index = 0xFFFF & addUtf8(k);
+ int theIndex = itsStringConstHash.getInt(utf8Index, -1);
+ if (theIndex == -1) {
+ theIndex = itsTopIndex++;
+ ensure(3);
+ itsPool[itsTop++] = CONSTANT_String;
+ itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop);
+ itsStringConstHash.put(utf8Index, theIndex);
+ }
+ itsPoolTypes.put(theIndex, CONSTANT_String);
+ return theIndex;
+ }
+
+ boolean isUnderUtfEncodingLimit(String s)
+ {
+ int strLen = s.length();
+ if (strLen * 3 <= MAX_UTF_ENCODING_SIZE) {
+ return true;
+ } else if (strLen > MAX_UTF_ENCODING_SIZE) {
+ return false;
+ }
+ return strLen == getUtfEncodingLimit(s, 0, strLen);
+ }
+
+ /**
+ * Get maximum i such that start <= i <= end and
+ * s.substring(start, i) fits JVM UTF string encoding limit.
+ */
+ int getUtfEncodingLimit(String s, int start, int end)
+ {
+ if ((end - start) * 3 <= MAX_UTF_ENCODING_SIZE) {
+ return end;
+ }
+ int limit = MAX_UTF_ENCODING_SIZE;
+ for (int i = start; i != end; i++) {
+ int c = s.charAt(i);
+ if (0 != c && c <= 0x7F) {
+ --limit;
+ } else if (c < 0x7FF) {
+ limit -= 2;
+ } else {
+ limit -= 3;
+ }
+ if (limit < 0) {
+ return i;
+ }
+ }
+ return end;
+ }
+
+ short addUtf8(String k)
+ {
+ int theIndex = itsUtf8Hash.get(k, -1);
+ if (theIndex == -1) {
+ int strLen = k.length();
+ boolean tooBigString;
+ if (strLen > MAX_UTF_ENCODING_SIZE) {
+ tooBigString = true;
+ } else {
+ tooBigString = false;
+ // Ask for worst case scenario buffer when each char takes 3
+ // bytes
+ ensure(1 + 2 + strLen * 3);
+ int top = itsTop;
+
+ itsPool[top++] = CONSTANT_Utf8;
+ top += 2; // skip length
+
+ char[] chars = cfw.getCharBuffer(strLen);
+ k.getChars(0, strLen, chars, 0);
+
+ for (int i = 0; i != strLen; i++) {
+ int c = chars[i];
+ if (c != 0 && c <= 0x7F) {
+ itsPool[top++] = (byte)c;
+ } else if (c > 0x7FF) {
+ itsPool[top++] = (byte)(0xE0 | (c >> 12));
+ itsPool[top++] = (byte)(0x80 | ((c >> 6) & 0x3F));
+ itsPool[top++] = (byte)(0x80 | (c & 0x3F));
+ } else {
+ itsPool[top++] = (byte)(0xC0 | (c >> 6));
+ itsPool[top++] = (byte)(0x80 | (c & 0x3F));
+ }
+ }
+
+ int utfLen = top - (itsTop + 1 + 2);
+ if (utfLen > MAX_UTF_ENCODING_SIZE) {
+ tooBigString = true;
+ } else {
+ // Write back length
+ itsPool[itsTop + 1] = (byte)(utfLen >>> 8);
+ itsPool[itsTop + 2] = (byte)utfLen;
+
+ itsTop = top;
+ theIndex = itsTopIndex++;
+ itsUtf8Hash.put(k, theIndex);
+ }
+ }
+ if (tooBigString) {
+ throw new IllegalArgumentException("Too big string");
+ }
+ }
+ setConstantData(theIndex, k);
+ itsPoolTypes.put(theIndex, CONSTANT_Utf8);
+ return (short)theIndex;
+ }
+
+ private short addNameAndType(String name, String type)
+ {
+ short nameIndex = addUtf8(name);
+ short typeIndex = addUtf8(type);
+ ensure(5);
+ itsPool[itsTop++] = CONSTANT_NameAndType;
+ itsTop = ClassFileWriter.putInt16(nameIndex, itsPool, itsTop);
+ itsTop = ClassFileWriter.putInt16(typeIndex, itsPool, itsTop);
+ itsPoolTypes.put(itsTopIndex, CONSTANT_NameAndType);
+ return (short)(itsTopIndex++);
+ }
+
+ short addClass(String className)
+ {
+ int theIndex = itsClassHash.get(className, -1);
+ if (theIndex == -1) {
+ String slashed = className;
+ if (className.indexOf('.') > 0) {
+ slashed = ClassFileWriter.getSlashedForm(className);
+ theIndex = itsClassHash.get(slashed, -1);
+ if (theIndex != -1) {
+ itsClassHash.put(className, theIndex);
+ }
+ }
+ if (theIndex == -1) {
+ int utf8Index = addUtf8(slashed);
+ ensure(3);
+ itsPool[itsTop++] = CONSTANT_Class;
+ itsTop = ClassFileWriter.putInt16(utf8Index, itsPool, itsTop);
+ theIndex = itsTopIndex++;
+ itsClassHash.put(slashed, theIndex);
+ if (className != slashed) {
+ itsClassHash.put(className, theIndex);
+ }
+ }
+ }
+ setConstantData(theIndex, className);
+ itsPoolTypes.put(theIndex, CONSTANT_Class);
+ return (short)theIndex;
+ }
+
+ short addFieldRef(String className, String fieldName, String fieldType)
+ {
+ FieldOrMethodRef ref = new FieldOrMethodRef(className, fieldName,
+ fieldType);
+
+ int theIndex = itsFieldRefHash.get(ref, -1);
+ if (theIndex == -1) {
+ short ntIndex = addNameAndType(fieldName, fieldType);
+ short classIndex = addClass(className);
+ ensure(5);
+ itsPool[itsTop++] = CONSTANT_Fieldref;
+ itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
+ itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
+ theIndex = itsTopIndex++;
+ itsFieldRefHash.put(ref, theIndex);
+ }
+ setConstantData(theIndex, ref);
+ itsPoolTypes.put(theIndex, CONSTANT_Fieldref);
+ return (short)theIndex;
+ }
+
+ short addMethodRef(String className, String methodName,
+ String methodType)
+ {
+ FieldOrMethodRef ref = new FieldOrMethodRef(className, methodName,
+ methodType);
+
+ int theIndex = itsMethodRefHash.get(ref, -1);
+ if (theIndex == -1) {
+ short ntIndex = addNameAndType(methodName, methodType);
+ short classIndex = addClass(className);
+ ensure(5);
+ itsPool[itsTop++] = CONSTANT_Methodref;
+ itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
+ itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
+ theIndex = itsTopIndex++;
+ itsMethodRefHash.put(ref, theIndex);
+ }
+ setConstantData(theIndex, ref);
+ itsPoolTypes.put(theIndex, CONSTANT_Methodref);
+ return (short)theIndex;
+ }
+
+ short addInterfaceMethodRef(String className,
+ String methodName, String methodType)
+ {
+ short ntIndex = addNameAndType(methodName, methodType);
+ short classIndex = addClass(className);
+ ensure(5);
+ itsPool[itsTop++] = CONSTANT_InterfaceMethodref;
+ itsTop = ClassFileWriter.putInt16(classIndex, itsPool, itsTop);
+ itsTop = ClassFileWriter.putInt16(ntIndex, itsPool, itsTop);
+ FieldOrMethodRef r = new FieldOrMethodRef(className, methodName,
+ methodType);
+ setConstantData(itsTopIndex, r);
+ itsPoolTypes.put(itsTopIndex, CONSTANT_InterfaceMethodref);
+ return (short)(itsTopIndex++);
+ }
+
+ Object getConstantData(int index)
+ {
+ return itsConstantData.getObject(index);
+ }
+
+ void setConstantData(int index, Object data)
+ {
+ itsConstantData.put(index, data);
+ }
+
+ byte getConstantType(int index)
+ {
+ return (byte) itsPoolTypes.getInt(index, 0);
+ }
+
+ void ensure(int howMuch)
+ {
+ if (itsTop + howMuch > itsPool.length) {
+ int newCapacity = itsPool.length * 2;
+ if (itsTop + howMuch > newCapacity) {
+ newCapacity = itsTop + howMuch;
+ }
+ byte[] tmp = new byte[newCapacity];
+ System.arraycopy(itsPool, 0, tmp, 0, itsTop);
+ itsPool = tmp;
+ }
+ }
+
+ private ClassFileWriter cfw;
+
+ private static final int MAX_UTF_ENCODING_SIZE = 65535;
+
+ private UintMap itsStringConstHash = new UintMap();
+ private ObjToIntMap itsUtf8Hash = new ObjToIntMap();
+ private ObjToIntMap itsFieldRefHash = new ObjToIntMap();
+ private ObjToIntMap itsMethodRefHash = new ObjToIntMap();
+ private ObjToIntMap itsClassHash = new ObjToIntMap();
+
+ private int itsTop;
+ private int itsTopIndex;
+ private UintMap itsConstantData = new UintMap();
+ private UintMap itsPoolTypes = new UintMap();
+ private byte itsPool[];
+}
+
+final class FieldOrMethodRef
+{
+ FieldOrMethodRef(String className, String name, String type)
+ {
+ this.className = className;
+ this.name = name;
+ this.type = type;
+ }
+
+ public String getClassName()
+ {
+ return className;
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public String getType()
+ {
+ return type;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (!(obj instanceof FieldOrMethodRef)) { return false; }
+ FieldOrMethodRef x = (FieldOrMethodRef)obj;
+ return className.equals(x.className)
+ && name.equals(x.name)
+ && type.equals(x.type);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ if (hashCode == -1) {
+ int h1 = className.hashCode();
+ int h2 = name.hashCode();
+ int h3 = type.hashCode();
+ hashCode = h1 ^ h2 ^ h3;
+ }
+ return hashCode;
+ }
+
+ private String className;
+ private String name;
+ private String type;
+ private int hashCode = -1;
+}
+
+/**
+ * A super block is defined as a contiguous chunk of code with a single entry
+ * point and multiple exit points (therefore ending in an unconditional jump
+ * or the end of the method). This is used to emulate OpenJDK's compiler, which
+ * outputs stack map frames at the start of every super block except the method
+ * start.
+ */
+final class SuperBlock {
+ SuperBlock(int index, int start, int end, int[] initialLocals) {
+ this.index = index;
+ this.start = start;
+ this.end = end;
+ locals = new int[initialLocals.length];
+ System.arraycopy(initialLocals, 0, locals, 0, initialLocals.length);
+ stack = new int[0];
+ isInitialized = false;
+ isInQueue = false;
+ }
+
+ int getIndex() {
+ return index;
+ }
+
+ int[] getLocals() {
+ int[] copy = new int[locals.length];
+ System.arraycopy(locals, 0, copy, 0, locals.length);
+ return copy;
+ }
+
+ /**
+ * Get a copy of the super block's locals without any trailing TOP types.
+ *
+ * This is useful for actual writing stack maps; during the computation of
+ * stack map types, all local arrays have the same size; the max locals for
+ * the method. In addition, DOUBLE and LONG types have trailing TOP types
+ * because they occupy two words. For writing purposes, these are not
+ * useful.
+ */
+ int[] getTrimmedLocals() {
+ int last = locals.length - 1;
+ // Exclude all of the trailing TOPs not bound to a DOUBLE/LONG
+ while (last >= 0 && locals[last] == TypeInfo.TOP &&
+ !TypeInfo.isTwoWords(locals[last - 1])) {
+ last--;
+ }
+ last++;
+ // Exclude trailing TOPs following a DOUBLE/LONG
+ int size = last;
+ for (int i = 0; i < last; i++) {
+ if (TypeInfo.isTwoWords(locals[i])) {
+ size--;
+ }
+ }
+ int[] copy = new int[size];
+ for (int i = 0, j = 0; i < size; i++, j++) {
+ copy[i] = locals[j];
+ if (TypeInfo.isTwoWords(locals[j])) {
+ j++;
+ }
+ }
+ return copy;
+ }
+
+ int[] getStack() {
+ int[] copy = new int[stack.length];
+ System.arraycopy(stack, 0, copy, 0, stack.length);
+ return copy;
+ }
+
+ boolean merge(int[] locals, int localsTop, int[] stack, int stackTop,
+ ConstantPool pool) {
+ if (!isInitialized) {
+ System.arraycopy(locals, 0, this.locals, 0, localsTop);
+ this.stack = new int[stackTop];
+ System.arraycopy(stack, 0, this.stack, 0, stackTop);
+ isInitialized = true;
+ return true;
+ } else if (this.locals.length == localsTop &&
+ this.stack.length == stackTop) {
+ boolean localsChanged = mergeState(this.locals, locals, localsTop,
+ pool);
+ boolean stackChanged = mergeState(this.stack, stack, stackTop,
+ pool);
+ return localsChanged || stackChanged;
+ } else {
+ if (ClassFileWriter.StackMapTable.DEBUGSTACKMAP) {
+ System.out.println("bad merge");
+ System.out.println("current type state:");
+ TypeInfo.print(this.locals, this.stack, pool);
+ System.out.println("incoming type state:");
+ TypeInfo.print(locals, localsTop, stack, stackTop, pool);
+ }
+ throw new IllegalArgumentException("bad merge attempt");
+ }
+ }
+
+ /**
+ * Merge an operand stack or local variable array with incoming state.
+ *
+ * They are treated the same way; by this point, it should already be
+ * ensured that the array sizes are the same, which is the only additional
+ * constraint that is imposed on merging operand stacks (the local variable
+ * array is always the same size).
+ */
+ private boolean mergeState(int[] current, int[] incoming, int size,
+ ConstantPool pool) {
+ boolean changed = false;
+ for (int i = 0; i < size; i++) {
+ int currentType = current[i];
+
+ current[i] = TypeInfo.merge(current[i], incoming[i], pool);
+ if (currentType != current[i]) {
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ int getStart() {
+ return start;
+ }
+
+ int getEnd() {
+ return end;
+ }
+
+ @Override
+ public String toString() {
+ return "sb " + index;
+ }
+
+ boolean isInitialized() {
+ return isInitialized;
+ }
+
+ void setInitialized(boolean b) {
+ isInitialized = b;
+ }
+
+ boolean isInQueue() {
+ return isInQueue;
+ }
+
+ void setInQueue(boolean b) {
+ isInQueue = b;
+ }
+
+ private int index;
+ private int start;
+ private int end;
+ private int[] locals;
+ private int[] stack;
+ private boolean isInitialized;
+ private boolean isInQueue;
+}
+
+/**
+ * Helper class for internal representations of type information. In most
+ * cases, type information can be represented by a constant, but in some
+ * cases, a payload is included. Despite the payload coming after the type
+ * tag in the output, we store it in bits 8-23 for uniformity; the tag is
+ * always in bits 0-7.
+ */
+final class TypeInfo {
+ private TypeInfo() { }
+
+ static final int TOP = 0;
+ static final int INTEGER = 1;
+ static final int FLOAT = 2;
+ static final int DOUBLE = 3;
+ static final int LONG = 4;
+ static final int NULL = 5;
+ static final int UNINITIALIZED_THIS = 6;
+ static final int OBJECT_TAG = 7;
+ static final int UNINITIALIZED_VAR_TAG = 8;
+
+ static final int OBJECT(int constantPoolIndex) {
+ return ((constantPoolIndex & 0xFFFF) << 8) | OBJECT_TAG;
+ }
+
+ static final int OBJECT(String type, ConstantPool pool) {
+ return OBJECT(pool.addClass(type));
+ }
+
+ static final int UNINITIALIZED_VARIABLE(int bytecodeOffset) {
+ return ((bytecodeOffset & 0xFFFF) << 8) | UNINITIALIZED_VAR_TAG;
+ }
+
+ static final int getTag(int typeInfo) {
+ return typeInfo & 0xFF;
+ }
+
+ static final int getPayload(int typeInfo) {
+ return typeInfo >>> 8;
+ }
+
+ /**
+ * Treat the result of getPayload as a constant pool index and fetch the
+ * corresponding String mapped to it.
+ *
+ * Only works on OBJECT types.
+ */
+ static final String getPayloadAsType(int typeInfo, ConstantPool pool) {
+ if (getTag(typeInfo) == OBJECT_TAG) {
+ return (String) pool.getConstantData(getPayload(typeInfo));
+ }
+ throw new IllegalArgumentException("expecting object type");
+ }
+
+ /**
+ * Create type information from an internal type.
+ */
+ static final int fromType(String type, ConstantPool pool) {
+ if (type.length() == 1) {
+ switch (type.charAt(0)) {
+ case 'B': // sbyte
+ case 'C': // unicode char
+ case 'S': // short
+ case 'Z': // boolean
+ case 'I': // all of the above are verified as integers
+ return INTEGER;
+ case 'D':
+ return DOUBLE;
+ case 'F':
+ return FLOAT;
+ case 'J':
+ return LONG;
+ default:
+ throw new IllegalArgumentException("bad type");
+ }
+ }
+ return TypeInfo.OBJECT(type, pool);
+ }
+
+ static boolean isTwoWords(int type) {
+ return type == DOUBLE || type == LONG;
+ }
+
+ /**
+ * Merge two verification types.
+ *
+ * In most cases, the verification types must be the same. For example,
+ * INTEGER and DOUBLE cannot be merged and an exception will be thrown.
+ * The basic rules are:
+ *
+ * - If the types are equal, simply return one.
+ * - If either type is TOP, return TOP.
+ * - If either type is NULL, return the other type.
+ * - If both types are objects, find the lowest common ancestor in the
+ * class hierarchy.
+ *
+ * This method uses reflection to traverse the class hierarchy. Therefore,
+ * it is assumed that the current class being generated is never the target
+ * of a full object-object merge, which would need to load the current
+ * class reflectively.
+ */
+ static int merge(int current, int incoming, ConstantPool pool) {
+ int currentTag = getTag(current);
+ int incomingTag = getTag(incoming);
+ boolean currentIsObject = currentTag == TypeInfo.OBJECT_TAG;
+ boolean incomingIsObject = incomingTag == TypeInfo.OBJECT_TAG;
+
+ if (current == incoming || (currentIsObject && incoming == NULL)) {
+ return current;
+ } else if (currentTag == TypeInfo.TOP ||
+ incomingTag == TypeInfo.TOP) {
+ return TypeInfo.TOP;
+ } else if (current == NULL && incomingIsObject) {
+ return incoming;
+ } else if (currentIsObject && incomingIsObject) {
+ String currentName = getPayloadAsType(current, pool);
+ String incomingName = getPayloadAsType(incoming, pool);
+ // The class file always has the class and super names in the same
+ // spot. The constant order is: class_data, class_name, super_data,
+ // super_name.
+ String currentlyGeneratedName = (String) pool.getConstantData(2);
+ String currentlyGeneratedSuperName =
+ (String) pool.getConstantData(4);
+
+ // If any of the merged types are the class that's currently being
+ // generated, automatically start at the super class instead. At
+ // this point, we already know the classes are different, so we
+ // don't need to handle that case.
+ if (currentName.equals(currentlyGeneratedName)) {
+ currentName = currentlyGeneratedSuperName;
+ }
+ if (incomingName.equals(currentlyGeneratedName)) {
+ incomingName = currentlyGeneratedSuperName;
+ }
+
+ Class> currentClass = getClassFromInternalName(currentName);
+ Class> incomingClass = getClassFromInternalName(incomingName);
+
+ if (currentClass.isAssignableFrom(incomingClass)) {
+ return current;
+ } else if (incomingClass.isAssignableFrom(currentClass)) {
+ return incoming;
+ } else if (incomingClass.isInterface() ||
+ currentClass.isInterface()) {
+ // For verification purposes, Sun specifies that interfaces are
+ // subtypes of Object. Therefore, we know that the merge result
+ // involving interfaces where one is not assignable to the
+ // other results in Object.
+ return OBJECT("java/lang/Object", pool);
+ } else {
+ Class> commonClass = incomingClass.getSuperclass();
+ while (commonClass != null) {
+ if (commonClass.isAssignableFrom(currentClass)) {
+ String name = commonClass.getName();
+ name = ClassFileWriter.getSlashedForm(name);
+ return OBJECT(name, pool);
+ }
+ commonClass = commonClass.getSuperclass();
+ }
+ }
+ }
+ throw new IllegalArgumentException("bad merge attempt between " +
+ toString(current, pool) + " and " +
+ toString(incoming, pool));
+ }
+
+ static String toString(int type, ConstantPool pool) {
+ int tag = getTag(type);
+ switch (tag) {
+ case TypeInfo.TOP:
+ return "top";
+ case TypeInfo.INTEGER:
+ return "int";
+ case TypeInfo.FLOAT:
+ return "float";
+ case TypeInfo.DOUBLE:
+ return "double";
+ case TypeInfo.LONG:
+ return "long";
+ case TypeInfo.NULL:
+ return "null";
+ case TypeInfo.UNINITIALIZED_THIS:
+ return "uninitialized_this";
+ default:
+ if (tag == TypeInfo.OBJECT_TAG) {
+ return getPayloadAsType(type, pool);
+ } else if (tag == TypeInfo.UNINITIALIZED_VAR_TAG) {
+ return "uninitialized";
+ } else {
+ throw new IllegalArgumentException("bad type");
+ }
+ }
+ }
+
+ /**
+ * Take an internal name and return a java.lang.Class instance that
+ * represents it.
+ *
+ * For example, given "java/lang/Object", returns the equivalent of
+ * Class.forName("java.lang.Object"), but also handles exceptions.
+ */
+ @SuppressWarnings("rawtypes")
+ static Class getClassFromInternalName(String internalName) {
+ try {
+ return Class.forName(internalName.replace('/', '.'));
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ static String toString(int[] types, ConstantPool pool) {
+ return toString(types, types.length, pool);
+ }
+
+ static String toString(int[] types, int typesTop, ConstantPool pool) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ for (int i = 0; i < typesTop; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(toString(types[i], pool));
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ static void print(int[] locals, int[] stack, ConstantPool pool) {
+ print(locals, locals.length, stack, stack.length, pool);
+ }
+
+ static void print(int[] locals, int localsTop, int[] stack, int stackTop,
+ ConstantPool pool) {
+ System.out.print("locals: ");
+ System.out.println(toString(locals, localsTop, pool));
+ System.out.print("stack: ");
+ System.out.println(toString(stack, stackTop, pool));
+ System.out.println();
+ }
+}
diff --git a/src/main/java/org/mozilla/javascript/Arguments.java b/src/main/java/org/mozilla/javascript/Arguments.java
new file mode 100644
index 0000000..f7027bc
--- /dev/null
+++ b/src/main/java/org/mozilla/javascript/Arguments.java
@@ -0,0 +1,375 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+/**
+ * This class implements the "arguments" object.
+ *
+ * See ECMA 10.1.8
+ *
+ * @see org.mozilla.javascript.NativeCall
+ * @author Norris Boyd
+ */
+final class Arguments extends IdScriptableObject
+{
+ static final long serialVersionUID = 4275508002492040609L;
+
+ private static final String FTAG = "Arguments";
+
+ public Arguments(NativeCall activation)
+ {
+ this.activation = activation;
+
+ Scriptable parent = activation.getParentScope();
+ setParentScope(parent);
+ setPrototype(ScriptableObject.getObjectPrototype(parent));
+
+ args = activation.originalArgs;
+ lengthObj = Integer.valueOf(args.length);
+
+ NativeFunction f = activation.function;
+ calleeObj = f;
+
+ Scriptable topLevel = getTopLevelScope(parent);
+ constructor = getProperty(topLevel, "Object");
+
+ int version = f.getLanguageVersion();
+ if (version <= Context.VERSION_1_3
+ && version != Context.VERSION_DEFAULT)
+ {
+ callerObj = null;
+ } else {
+ callerObj = NOT_FOUND;
+ }
+ }
+
+ @Override
+ public String getClassName()
+ {
+ return FTAG;
+ }
+
+ private Object arg(int index) {
+ if (index < 0 || args.length <= index) return NOT_FOUND;
+ return args[index];
+ }
+
+ // the following helper methods assume that 0 < index < args.length
+
+ private void putIntoActivation(int index, Object value) {
+ String argName = activation.function.getParamOrVarName(index);
+ activation.put(argName, activation, value);
+ }
+
+ private Object getFromActivation(int index) {
+ String argName = activation.function.getParamOrVarName(index);
+ return activation.get(argName, activation);
+ }
+
+ private void replaceArg(int index, Object value) {
+ if (sharedWithActivation(index)) {
+ putIntoActivation(index, value);
+ }
+ synchronized (this) {
+ if (args == activation.originalArgs) {
+ args = args.clone();
+ }
+ args[index] = value;
+ }
+ }
+
+ private void removeArg(int index) {
+ synchronized (this) {
+ if (args[index] != NOT_FOUND) {
+ if (args == activation.originalArgs) {
+ args = args.clone();
+ }
+ args[index] = NOT_FOUND;
+ }
+ }
+ }
+
+ // end helpers
+
+ @Override
+ public boolean has(int index, Scriptable start)
+ {
+ if (arg(index) != NOT_FOUND) {
+ return true;
+ }
+ return super.has(index, start);
+ }
+
+ @Override
+ public Object get(int index, Scriptable start)
+ {
+ final Object value = arg(index);
+ if (value == NOT_FOUND) {
+ return super.get(index, start);
+ } else {
+ if (sharedWithActivation(index)) {
+ return getFromActivation(index);
+ } else {
+ return value;
+ }
+ }
+ }
+
+ private boolean sharedWithActivation(int index)
+ {
+ NativeFunction f = activation.function;
+ int definedCount = f.getParamCount();
+ if (index < definedCount) {
+ // Check if argument is not hidden by later argument with the same
+ // name as hidden arguments are not shared with activation
+ if (index < definedCount - 1) {
+ String argName = f.getParamOrVarName(index);
+ for (int i = index + 1; i < definedCount; i++) {
+ if (argName.equals(f.getParamOrVarName(i))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void put(int index, Scriptable start, Object value)
+ {
+ if (arg(index) == NOT_FOUND) {
+ super.put(index, start, value);
+ } else {
+ replaceArg(index, value);
+ }
+ }
+
+ @Override
+ public void delete(int index)
+ {
+ if (0 <= index && index < args.length) {
+ removeArg(index);
+ }
+ super.delete(index);
+ }
+
+// #string_id_map#
+
+ private static final int
+ Id_callee = 1,
+ Id_length = 2,
+ Id_caller = 3,
+ Id_constructor = 4,
+
+ MAX_INSTANCE_ID = Id_constructor;
+
+ @Override
+ protected int getMaxInstanceId()
+ {
+ return MAX_INSTANCE_ID;
+ }
+
+ @Override
+ protected int findInstanceIdInfo(String s)
+ {
+ int id;
+// #generated# Last update: 2010-01-06 05:48:21 ARST
+ L0: { id = 0; String X = null; int c;
+ int s_length = s.length();
+ if (s_length==6) {
+ c=s.charAt(5);
+ if (c=='e') { X="callee";id=Id_callee; }
+ else if (c=='h') { X="length";id=Id_length; }
+ else if (c=='r') { X="caller";id=Id_caller; }
+ }
+ else if (s_length==11) { X="constructor";id=Id_constructor; }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+
+ if (id == 0) return super.findInstanceIdInfo(s);
+
+ int attr;
+ switch (id) {
+ case Id_callee:
+ case Id_caller:
+ case Id_length:
+ case Id_constructor:
+ attr = DONTENUM;
+ break;
+ default: throw new IllegalStateException();
+ }
+ return instanceIdInfo(attr, id);
+ }
+
+// #/string_id_map#
+
+ @Override
+ protected String getInstanceIdName(int id)
+ {
+ switch (id) {
+ case Id_callee: return "callee";
+ case Id_length: return "length";
+ case Id_caller: return "caller";
+ case Id_constructor: return "constructor";
+ }
+ return null;
+ }
+
+ @Override
+ protected Object getInstanceIdValue(int id)
+ {
+ switch (id) {
+ case Id_callee: return calleeObj;
+ case Id_length: return lengthObj;
+ case Id_caller: {
+ Object value = callerObj;
+ if (value == UniqueTag.NULL_VALUE) { value = null; }
+ else if (value == null) {
+ NativeCall caller = activation.parentActivationCall;
+ if (caller != null) {
+ value = caller.get("arguments", caller);
+ }
+ }
+ return value;
+ }
+ case Id_constructor:
+ return constructor;
+ }
+ return super.getInstanceIdValue(id);
+ }
+
+ @Override
+ protected void setInstanceIdValue(int id, Object value)
+ {
+ switch (id) {
+ case Id_callee: calleeObj = value; return;
+ case Id_length: lengthObj = value; return;
+ case Id_caller:
+ callerObj = (value != null) ? value : UniqueTag.NULL_VALUE;
+ return;
+ case Id_constructor: constructor = value; return;
+ }
+ super.setInstanceIdValue(id, value);
+ }
+
+ @Override
+ Object[] getIds(boolean getAll)
+ {
+ Object[] ids = super.getIds(getAll);
+ if (args.length != 0) {
+ boolean[] present = new boolean[args.length];
+ int extraCount = args.length;
+ for (int i = 0; i != ids.length; ++i) {
+ Object id = ids[i];
+ if (id instanceof Integer) {
+ int index = ((Integer)id).intValue();
+ if (0 <= index && index < args.length) {
+ if (!present[index]) {
+ present[index] = true;
+ extraCount--;
+ }
+ }
+ }
+ }
+ if (!getAll) { // avoid adding args which were redefined to non-enumerable
+ for (int i = 0; i < present.length; i++) {
+ if (!present[i] && super.has(i, this)) {
+ present[i] = true;
+ extraCount--;
+ }
+ }
+ }
+ if (extraCount != 0) {
+ Object[] tmp = new Object[extraCount + ids.length];
+ System.arraycopy(ids, 0, tmp, extraCount, ids.length);
+ ids = tmp;
+ int offset = 0;
+ for (int i = 0; i != args.length; ++i) {
+ if (present == null || !present[i]) {
+ ids[offset] = Integer.valueOf(i);
+ ++offset;
+ }
+ }
+ if (offset != extraCount) Kit.codeBug();
+ }
+ }
+ return ids;
+ }
+
+ @Override
+ protected ScriptableObject getOwnPropertyDescriptor(Context cx, Object id) {
+ double d = ScriptRuntime.toNumber(id);
+ int index = (int) d;
+ if (d != index) {
+ return super.getOwnPropertyDescriptor(cx, id);
+ }
+ Object value = arg(index);
+ if (value == NOT_FOUND) {
+ return super.getOwnPropertyDescriptor(cx, id);
+ }
+ if (sharedWithActivation(index)) {
+ value = getFromActivation(index);
+ }
+ if (super.has(index, this)) { // the descriptor has been redefined
+ ScriptableObject desc = super.getOwnPropertyDescriptor(cx, id);
+ desc.put("value", desc, value);
+ return desc;
+ } else {
+ Scriptable scope = getParentScope();
+ if (scope == null) scope = this;
+ return buildDataDescriptor(scope, value, EMPTY);
+ }
+ }
+
+ @Override
+ protected void defineOwnProperty(Context cx, Object id,
+ ScriptableObject desc,
+ boolean checkValid) {
+ super.defineOwnProperty(cx, id, desc, checkValid);
+
+ double d = ScriptRuntime.toNumber(id);
+ int index = (int) d;
+ if (d != index) return;
+
+ Object value = arg(index);
+ if (value == NOT_FOUND) return;
+
+ if (isAccessorDescriptor(desc)) {
+ removeArg(index);
+ return;
+ }
+
+ Object newValue = getProperty(desc, "value");
+ if (newValue == NOT_FOUND) return;
+
+ replaceArg(index, newValue);
+
+ if (isFalse(getProperty(desc, "writable"))) {
+ removeArg(index);
+ }
+ }
+
+// Fields to hold caller, callee and length properties,
+// where NOT_FOUND value tags deleted properties.
+// In addition if callerObj == NULL_VALUE, it tags null for scripts, as
+// initial callerObj == null means access to caller arguments available
+// only in JS <= 1.3 scripts
+ private Object callerObj;
+ private Object calleeObj;
+ private Object lengthObj;
+ private Object constructor;
+
+ private NativeCall activation;
+
+// Initially args holds activation.getOriginalArgs(), but any modification
+// of its elements triggers creation of a copy. If its element holds NOT_FOUND,
+// it indicates deleted index, in which case super class is queried.
+ private Object[] args;
+}
diff --git a/src/main/java/org/mozilla/javascript/BaseFunction.java b/src/main/java/org/mozilla/javascript/BaseFunction.java
new file mode 100644
index 0000000..d0ed162
--- /dev/null
+++ b/src/main/java/org/mozilla/javascript/BaseFunction.java
@@ -0,0 +1,587 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+/**
+ * The base class for Function objects
+ * See ECMA 15.3.
+ * @author Norris Boyd
+ */
+public class BaseFunction extends IdScriptableObject implements Function
+{
+
+ static final long serialVersionUID = 5311394446546053859L;
+
+ private static final Object FUNCTION_TAG = "Function";
+
+ static void init(Scriptable scope, boolean sealed)
+ {
+ BaseFunction obj = new BaseFunction();
+ // Function.prototype attributes: see ECMA 15.3.3.1
+ obj.prototypePropertyAttributes = DONTENUM | READONLY | PERMANENT;
+ obj.exportAsJSClass(MAX_PROTOTYPE_ID, scope, sealed);
+ }
+
+ public BaseFunction()
+ {
+ }
+
+ public BaseFunction(Scriptable scope, Scriptable prototype)
+ {
+ super(scope, prototype);
+ }
+
+ @Override
+ public String getClassName() {
+ return "Function";
+ }
+
+ /**
+ * Gets the value returned by calling the typeof operator on this object.
+ * @see org.mozilla.javascript.ScriptableObject#getTypeOf()
+ * @return "function" or "undefined" if {@link #avoidObjectDetection()} returns true
+ */
+ @Override
+ public String getTypeOf()
+ {
+ return avoidObjectDetection() ? "undefined" : "function";
+ }
+
+ /**
+ * Implements the instanceof operator for JavaScript Function objects.
+ *
+ *
+ * foo = new Foo();
+ * foo instanceof Foo; // true
+ *
+ *
+ * @param instance The value that appeared on the LHS of the instanceof
+ * operator
+ * @return true if the "prototype" property of "this" appears in
+ * value's prototype chain
+ *
+ */
+ @Override
+ public boolean hasInstance(Scriptable instance)
+ {
+ Object protoProp = ScriptableObject.getProperty(this, "prototype");
+ if (protoProp instanceof Scriptable) {
+ return ScriptRuntime.jsDelegatesTo(instance, (Scriptable)protoProp);
+ }
+ throw ScriptRuntime.typeError1("msg.instanceof.bad.prototype",
+ getFunctionName());
+ }
+
+// #string_id_map#
+
+ private static final int
+ Id_length = 1,
+ Id_arity = 2,
+ Id_name = 3,
+ Id_prototype = 4,
+ Id_arguments = 5,
+
+ MAX_INSTANCE_ID = 5;
+
+ @Override
+ protected int getMaxInstanceId()
+ {
+ return MAX_INSTANCE_ID;
+ }
+
+ @Override
+ protected int findInstanceIdInfo(String s)
+ {
+ int id;
+// #generated# Last update: 2007-05-09 08:15:15 EDT
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 4: X="name";id=Id_name; break L;
+ case 5: X="arity";id=Id_arity; break L;
+ case 6: X="length";id=Id_length; break L;
+ case 9: c=s.charAt(0);
+ if (c=='a') { X="arguments";id=Id_arguments; }
+ else if (c=='p') { X="prototype";id=Id_prototype; }
+ break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+// #/string_id_map#
+
+ if (id == 0) return super.findInstanceIdInfo(s);
+
+ int attr;
+ switch (id) {
+ case Id_length:
+ case Id_arity:
+ case Id_name:
+ attr = DONTENUM | READONLY | PERMANENT;
+ break;
+ case Id_prototype:
+ // some functions such as built-ins don't have a prototype property
+ if (!hasPrototypeProperty()) {
+ return 0;
+ }
+ attr = prototypePropertyAttributes;
+ break;
+ case Id_arguments:
+ attr = DONTENUM | PERMANENT;
+ break;
+ default: throw new IllegalStateException();
+ }
+ return instanceIdInfo(attr, id);
+ }
+
+ @Override
+ protected String getInstanceIdName(int id)
+ {
+ switch (id) {
+ case Id_length: return "length";
+ case Id_arity: return "arity";
+ case Id_name: return "name";
+ case Id_prototype: return "prototype";
+ case Id_arguments: return "arguments";
+ }
+ return super.getInstanceIdName(id);
+ }
+
+ @Override
+ protected Object getInstanceIdValue(int id)
+ {
+ switch (id) {
+ case Id_length: return ScriptRuntime.wrapInt(getLength());
+ case Id_arity: return ScriptRuntime.wrapInt(getArity());
+ case Id_name: return getFunctionName();
+ case Id_prototype: return getPrototypeProperty();
+ case Id_arguments: return getArguments();
+ }
+ return super.getInstanceIdValue(id);
+ }
+
+ @Override
+ protected void setInstanceIdValue(int id, Object value)
+ {
+ switch (id) {
+ case Id_prototype:
+ if ((prototypePropertyAttributes & READONLY) == 0) {
+ prototypeProperty = (value != null)
+ ? value : UniqueTag.NULL_VALUE;
+ }
+ return;
+ case Id_arguments:
+ if (value == NOT_FOUND) {
+ // This should not be called since "arguments" is PERMANENT
+ Kit.codeBug();
+ }
+ defaultPut("arguments", value);
+ return;
+ case Id_name:
+ case Id_arity:
+ case Id_length:
+ return;
+ }
+ super.setInstanceIdValue(id, value);
+ }
+
+ @Override
+ protected void fillConstructorProperties(IdFunctionObject ctor)
+ {
+ // Fix up bootstrapping problem: getPrototype of the IdFunctionObject
+ // can not return Function.prototype because Function object is not
+ // yet defined.
+ ctor.setPrototype(this);
+ super.fillConstructorProperties(ctor);
+ }
+
+ @Override
+ protected void initPrototypeId(int id)
+ {
+ String s;
+ int arity;
+ switch (id) {
+ case Id_constructor: arity=1; s="constructor"; break;
+ case Id_toString: arity=1; s="toString"; break;
+ case Id_toSource: arity=1; s="toSource"; break;
+ case Id_apply: arity=2; s="apply"; break;
+ case Id_call: arity=1; s="call"; break;
+ case Id_bind: arity=1; s="bind"; break;
+ default: throw new IllegalArgumentException(String.valueOf(id));
+ }
+ initPrototypeMethod(FUNCTION_TAG, id, s, arity);
+ }
+
+ static boolean isApply(IdFunctionObject f) {
+ return f.hasTag(FUNCTION_TAG) && f.methodId() == Id_apply;
+ }
+
+ static boolean isApplyOrCall(IdFunctionObject f) {
+ if(f.hasTag(FUNCTION_TAG)) {
+ switch(f.methodId()) {
+ case Id_apply:
+ case Id_call:
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Object execIdCall(IdFunctionObject f, Context cx, Scriptable scope,
+ Scriptable thisObj, Object[] args)
+ {
+ if (!f.hasTag(FUNCTION_TAG)) {
+ return super.execIdCall(f, cx, scope, thisObj, args);
+ }
+ int id = f.methodId();
+ switch (id) {
+ case Id_constructor:
+ return jsConstructor(cx, scope, args);
+
+ case Id_toString: {
+ BaseFunction realf = realFunction(thisObj, f);
+ int indent = ScriptRuntime.toInt32(args, 0);
+ return realf.decompile(indent, 0);
+ }
+
+ case Id_toSource: {
+ BaseFunction realf = realFunction(thisObj, f);
+ int indent = 0;
+ int flags = Decompiler.TO_SOURCE_FLAG;
+ if (args.length != 0) {
+ indent = ScriptRuntime.toInt32(args[0]);
+ if (indent >= 0) {
+ flags = 0;
+ } else {
+ indent = 0;
+ }
+ }
+ return realf.decompile(indent, flags);
+ }
+
+ case Id_apply:
+ case Id_call:
+ return ScriptRuntime.applyOrCall(id == Id_apply,
+ cx, scope, thisObj, args);
+
+ case Id_bind:
+ if ( !(thisObj instanceof Callable) ) {
+ throw ScriptRuntime.notFunctionError(thisObj);
+ }
+ Callable targetFunction = (Callable) thisObj;
+ int argc = args.length;
+ final Scriptable boundThis;
+ final Object[] boundArgs;
+ if (argc > 0) {
+ boundThis = ScriptRuntime.toObjectOrNull(cx, args[0], scope);
+ boundArgs = new Object[argc-1];
+ System.arraycopy(args, 1, boundArgs, 0, argc-1);
+ } else {
+ boundThis = null;
+ boundArgs = ScriptRuntime.emptyArgs;
+ }
+ return new BoundFunction(cx, scope, targetFunction, boundThis, boundArgs);
+ }
+ throw new IllegalArgumentException(String.valueOf(id));
+ }
+
+ private BaseFunction realFunction(Scriptable thisObj, IdFunctionObject f)
+ {
+ Object x = thisObj.getDefaultValue(ScriptRuntime.FunctionClass);
+ if (x instanceof BaseFunction) {
+ return (BaseFunction)x;
+ }
+ throw ScriptRuntime.typeError1("msg.incompat.call",
+ f.getFunctionName());
+ }
+
+ /**
+ * Make value as DontEnum, DontDelete, ReadOnly
+ * prototype property of this Function object
+ */
+ public void setImmunePrototypeProperty(Object value)
+ {
+ if ((prototypePropertyAttributes & READONLY) != 0) {
+ throw new IllegalStateException();
+ }
+ prototypeProperty = (value != null) ? value : UniqueTag.NULL_VALUE;
+ prototypePropertyAttributes = DONTENUM | PERMANENT | READONLY;
+ }
+
+ protected Scriptable getClassPrototype()
+ {
+ Object protoVal = getPrototypeProperty();
+ if (protoVal instanceof Scriptable) {
+ return (Scriptable) protoVal;
+ }
+ return ScriptableObject.getObjectPrototype(this);
+ }
+
+ /**
+ * Should be overridden.
+ */
+ @Override
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args)
+ {
+ return Undefined.instance;
+ }
+
+ @Override
+ public Scriptable construct(Context cx, Scriptable scope, Object[] args)
+ {
+ Scriptable result = createObject(cx, scope);
+ if (result != null) {
+ Object val = call(cx, scope, result, args);
+ if (val instanceof Scriptable) {
+ result = (Scriptable)val;
+ }
+ } else {
+ Object val = call(cx, scope, null, args);
+ if (!(val instanceof Scriptable)) {
+ // It is program error not to return Scriptable from
+ // the call method if createObject returns null.
+ throw new IllegalStateException(
+ "Bad implementaion of call as constructor, name="
+ +getFunctionName()+" in "+getClass().getName());
+ }
+ result = (Scriptable)val;
+ if (result.getPrototype() == null) {
+ Scriptable proto = getClassPrototype();
+ if (result != proto) {
+ result.setPrototype(proto);
+ }
+ }
+ if (result.getParentScope() == null) {
+ Scriptable parent = getParentScope();
+ if (result != parent) {
+ result.setParentScope(parent);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates new script object.
+ * The default implementation of {@link #construct} uses the method to
+ * to get the value for thisObj argument when invoking
+ * {@link #call}.
+ * The methos is allowed to return null to indicate that
+ * {@link #call} will create a new object itself. In this case
+ * {@link #construct} will set scope and prototype on the result
+ * {@link #call} unless they are already set.
+ */
+ public Scriptable createObject(Context cx, Scriptable scope)
+ {
+ Scriptable newInstance = new NativeObject();
+ newInstance.setPrototype(getClassPrototype());
+ newInstance.setParentScope(getParentScope());
+ return newInstance;
+ }
+
+ /**
+ * Decompile the source information associated with this js
+ * function/script back into a string.
+ *
+ * @param indent How much to indent the decompiled result.
+ *
+ * @param flags Flags specifying format of decompilation output.
+ */
+ String decompile(int indent, int flags)
+ {
+ StringBuffer sb = new StringBuffer();
+ boolean justbody = (0 != (flags & Decompiler.ONLY_BODY_FLAG));
+ if (!justbody) {
+ sb.append("function ");
+ sb.append(getFunctionName());
+ sb.append("() {\n\t");
+ }
+ sb.append("[native code, arity=");
+ sb.append(getArity());
+ sb.append("]\n");
+ if (!justbody) {
+ sb.append("}\n");
+ }
+ return sb.toString();
+ }
+
+ public int getArity() { return 0; }
+
+ public int getLength() { return 0; }
+
+ public String getFunctionName() {
+ return "";
+ }
+
+ protected boolean hasPrototypeProperty() {
+ return prototypeProperty != null || this instanceof NativeFunction;
+ }
+
+ protected Object getPrototypeProperty() {
+ Object result = prototypeProperty;
+ if (result == null) {
+ // only create default prototype on native JavaScript functions,
+ // not on built-in functions, java methods, host objects etc.
+ if (this instanceof NativeFunction) {
+ result = setupDefaultPrototype();
+ } else {
+ result = Undefined.instance;
+ }
+ } else if (result == UniqueTag.NULL_VALUE) {
+ result = null;
+ }
+ return result;
+ }
+
+ private synchronized Object setupDefaultPrototype() {
+ if (prototypeProperty != null) {
+ return prototypeProperty;
+ }
+ NativeObject obj = new NativeObject();
+ final int attr = ScriptableObject.DONTENUM;
+ obj.defineProperty("constructor", this, attr);
+ // put the prototype property into the object now, then in the
+ // wacky case of a user defining a function Object(), we don't
+ // get an infinite loop trying to find the prototype.
+ prototypeProperty = obj;
+ Scriptable proto = getObjectPrototype(this);
+ if (proto != obj) {
+ // not the one we just made, it must remain grounded
+ obj.setPrototype(proto);
+ }
+ return obj;
+ }
+
+ private Object getArguments()
+ {
+ // .arguments is deprecated, so we use a slow
+ // way of getting it that doesn't add to the invocation cost.
+ // TODO: add warning, error based on version
+ Object value = defaultGet("arguments");
+ if (value != NOT_FOUND) {
+ // Should after changing .arguments its
+ // activation still be available during Function call?
+ // This code assumes it should not:
+ // defaultGet("arguments") != NOT_FOUND
+ // means assigned arguments
+ return value;
+ }
+ Context cx = Context.getContext();
+ NativeCall activation = ScriptRuntime.findFunctionActivation(cx, this);
+ return (activation == null)
+ ? null
+ : activation.get("arguments", activation);
+ }
+
+ private static Object jsConstructor(Context cx, Scriptable scope,
+ Object[] args)
+ {
+ int arglen = args.length;
+ StringBuffer sourceBuf = new StringBuffer();
+
+ sourceBuf.append("function ");
+ /* version != 1.2 Function constructor behavior -
+ * print 'anonymous' as the function name if the
+ * version (under which the function was compiled) is
+ * less than 1.2... or if it's greater than 1.2, because
+ * we need to be closer to ECMA.
+ */
+ if (cx.getLanguageVersion() != Context.VERSION_1_2) {
+ sourceBuf.append("anonymous");
+ }
+ sourceBuf.append('(');
+
+ // Append arguments as coma separated strings
+ for (int i = 0; i < arglen - 1; i++) {
+ if (i > 0) {
+ sourceBuf.append(',');
+ }
+ sourceBuf.append(ScriptRuntime.toString(args[i]));
+ }
+ sourceBuf.append(") {");
+ if (arglen != 0) {
+ // append function body
+ String funBody = ScriptRuntime.toString(args[arglen - 1]);
+ sourceBuf.append(funBody);
+ }
+ sourceBuf.append("\n}");
+ String source = sourceBuf.toString();
+
+ int[] linep = new int[1];
+ String filename = Context.getSourcePositionFromStack(linep);
+ if (filename == null) {
+ filename = "";
+ linep[0] = 1;
+ }
+
+ String sourceURI = ScriptRuntime.
+ makeUrlForGeneratedScript(false, filename, linep[0]);
+
+ Scriptable global = ScriptableObject.getTopLevelScope(scope);
+
+ ErrorReporter reporter;
+ reporter = DefaultErrorReporter.forEval(cx.getErrorReporter());
+
+ Evaluator evaluator = Context.createInterpreter();
+ if (evaluator == null) {
+ throw new JavaScriptException("Interpreter not present",
+ filename, linep[0]);
+ }
+
+ // Compile with explicit interpreter instance to force interpreter
+ // mode.
+ return cx.compileFunction(global, source, evaluator, reporter,
+ sourceURI, 1, null);
+ }
+
+ @Override
+ protected int findPrototypeId(String s)
+ {
+ int id;
+// #string_id_map#
+// #generated# Last update: 2009-07-24 16:00:52 EST
+ L0: { id = 0; String X = null; int c;
+ L: switch (s.length()) {
+ case 4: c=s.charAt(0);
+ if (c=='b') { X="bind";id=Id_bind; }
+ else if (c=='c') { X="call";id=Id_call; }
+ break L;
+ case 5: X="apply";id=Id_apply; break L;
+ case 8: c=s.charAt(3);
+ if (c=='o') { X="toSource";id=Id_toSource; }
+ else if (c=='t') { X="toString";id=Id_toString; }
+ break L;
+ case 11: X="constructor";id=Id_constructor; break L;
+ }
+ if (X!=null && X!=s && !X.equals(s)) id = 0;
+ break L0;
+ }
+// #/generated#
+ return id;
+ }
+
+ private static final int
+ Id_constructor = 1,
+ Id_toString = 2,
+ Id_toSource = 3,
+ Id_apply = 4,
+ Id_call = 5,
+ Id_bind = 6,
+
+ MAX_PROTOTYPE_ID = Id_bind;
+
+// #/string_id_map#
+
+ private Object prototypeProperty;
+ // For function object instances, attributes are
+ // {configurable:false, enumerable:false};
+ // see ECMA 15.3.5.2
+ private int prototypePropertyAttributes = PERMANENT|DONTENUM;
+}
+
diff --git a/src/main/java/org/mozilla/javascript/BoundFunction.java b/src/main/java/org/mozilla/javascript/BoundFunction.java
new file mode 100644
index 0000000..692db72
--- /dev/null
+++ b/src/main/java/org/mozilla/javascript/BoundFunction.java
@@ -0,0 +1,83 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+/**
+ * The class for results of the Function.bind operation
+ * EcmaScript 5 spec, 15.3.4.5
+ * @author Raphael Speyer
+ */
+public class BoundFunction extends BaseFunction {
+
+ static final long serialVersionUID = 2118137342826470729L;
+
+ private final Callable targetFunction;
+ private final Scriptable boundThis;
+ private final Object[] boundArgs;
+ private final int length;
+
+ public BoundFunction(Context cx, Scriptable scope, Callable targetFunction, Scriptable boundThis,
+ Object[] boundArgs)
+ {
+ this.targetFunction = targetFunction;
+ this.boundThis = boundThis;
+ this.boundArgs = boundArgs;
+ if (targetFunction instanceof BaseFunction) {
+ length = Math.max(0, ((BaseFunction) targetFunction).getLength() - boundArgs.length);
+ } else {
+ length = 0;
+ }
+
+ ScriptRuntime.setFunctionProtoAndParent(this, scope);
+
+ Function thrower = ScriptRuntime.typeErrorThrower();
+ NativeObject throwing = new NativeObject();
+ throwing.put("get", throwing, thrower);
+ throwing.put("set", throwing, thrower);
+ throwing.put("enumerable", throwing, false);
+ throwing.put("configurable", throwing, false);
+ throwing.preventExtensions();
+
+ this.defineOwnProperty(cx, "caller", throwing, false);
+ this.defineOwnProperty(cx, "arguments", throwing, false);
+ }
+
+ @Override
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] extraArgs)
+ {
+ Scriptable callThis = boundThis != null ? boundThis : ScriptRuntime.getTopCallScope(cx);
+ return targetFunction.call(cx, scope, callThis, concat(boundArgs, extraArgs));
+ }
+
+ @Override
+ public Scriptable construct(Context cx, Scriptable scope, Object[] extraArgs) {
+ if (targetFunction instanceof Function) {
+ return ((Function) targetFunction).construct(cx, scope, concat(boundArgs, extraArgs));
+ }
+ throw ScriptRuntime.typeError0("msg.not.ctor");
+ }
+
+ @Override
+ public boolean hasInstance(Scriptable instance) {
+ if (targetFunction instanceof Function) {
+ return ((Function) targetFunction).hasInstance(instance);
+ }
+ throw ScriptRuntime.typeError0("msg.not.ctor");
+ }
+
+ @Override
+ public int getLength() {
+ return length;
+ }
+
+ private Object[] concat(Object[] first, Object[] second) {
+ Object[] args = new Object[first.length + second.length];
+ System.arraycopy(first, 0, args, 0, first.length);
+ System.arraycopy(second, 0, args, first.length, second.length);
+ return args;
+ }
+}
diff --git a/src/main/java/org/mozilla/javascript/Callable.java b/src/main/java/org/mozilla/javascript/Callable.java
new file mode 100644
index 0000000..ca1127b
--- /dev/null
+++ b/src/main/java/org/mozilla/javascript/Callable.java
@@ -0,0 +1,27 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+/**
+ * Generic notion of callable object that can execute some script-related code
+ * upon request with specified values for script scope and this objects.
+ */
+public interface Callable
+{
+ /**
+ * Perform the call.
+ *
+ * @param cx the current Context for this thread
+ * @param scope the scope to use to resolve properties.
+ * @param thisObj the JavaScript this object
+ * @param args the array of arguments
+ * @return the result of the call
+ */
+ public Object call(Context cx, Scriptable scope, Scriptable thisObj,
+ Object[] args);
+}
+
diff --git a/src/main/java/org/mozilla/javascript/ClassCache.java b/src/main/java/org/mozilla/javascript/ClassCache.java
new file mode 100644
index 0000000..190961a
--- /dev/null
+++ b/src/main/java/org/mozilla/javascript/ClassCache.java
@@ -0,0 +1,197 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.io.Serializable;
+
+/**
+ * Cache of generated classes and data structures to access Java runtime
+ * from JavaScript.
+ *
+ * @author Igor Bukanov
+ *
+ * @since Rhino 1.5 Release 5
+ */
+@SuppressWarnings("dep-ann")
+public class ClassCache implements Serializable
+{
+ private static final long serialVersionUID = -8866246036237312215L;
+ private static final Object AKEY = "ClassCache";
+ private volatile boolean cachingIsEnabled = true;
+ private transient HashMap,JavaMembers> classTable;
+ private transient HashMap> classAdapterCache;
+ private transient HashMap,Object> interfaceAdapterCache;
+ private int generatedClassSerial;
+ private Scriptable associatedScope;
+
+ /**
+ * Search for ClassCache object in the given scope.
+ * The method first calls
+ * {@link ScriptableObject#getTopLevelScope(Scriptable scope)}
+ * to get the top most scope and then tries to locate associated
+ * ClassCache object in the prototype chain of the top scope.
+ *
+ * @param scope scope to search for ClassCache object.
+ * @return previously associated ClassCache object or a new instance of
+ * ClassCache if no ClassCache object was found.
+ *
+ * @see #associate(ScriptableObject topScope)
+ */
+ public static ClassCache get(Scriptable scope)
+ {
+ ClassCache cache = (ClassCache)
+ ScriptableObject.getTopScopeValue(scope, AKEY);
+ if (cache == null) {
+ throw new RuntimeException("Can't find top level scope for " +
+ "ClassCache.get");
+ }
+ return cache;
+ }
+
+ /**
+ * Associate ClassCache object with the given top-level scope.
+ * The ClassCache object can only be associated with the given scope once.
+ *
+ * @param topScope scope to associate this ClassCache object with.
+ * @return true if no previous ClassCache objects were embedded into
+ * the scope and this ClassCache were successfully associated
+ * or false otherwise.
+ *
+ * @see #get(Scriptable scope)
+ */
+ public boolean associate(ScriptableObject topScope)
+ {
+ if (topScope.getParentScope() != null) {
+ // Can only associate cache with top level scope
+ throw new IllegalArgumentException();
+ }
+ if (this == topScope.associateValue(AKEY, this)) {
+ associatedScope = topScope;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Empty caches of generated Java classes and Java reflection information.
+ */
+ public synchronized void clearCaches()
+ {
+ classTable = null;
+ classAdapterCache = null;
+ interfaceAdapterCache = null;
+ }
+
+ /**
+ * Check if generated Java classes and Java reflection information
+ * is cached.
+ */
+ public final boolean isCachingEnabled()
+ {
+ return cachingIsEnabled;
+ }
+
+ /**
+ * Set whether to cache some values.
+ *
+ * By default, the engine will cache the results of
+ * Class.getMethods() and similar calls.
+ * This can speed execution dramatically, but increases the memory
+ * footprint. Also, with caching enabled, references may be held to
+ * objects past the lifetime of any real usage.
+ *
+ * If caching is enabled and this method is called with a
+ * false argument, the caches will be emptied.
+ *
+ * Caching is enabled by default.
+ *
+ * @param enabled if true, caching is enabled
+ *
+ * @see #clearCaches()
+ */
+ public synchronized void setCachingEnabled(boolean enabled)
+ {
+ if (enabled == cachingIsEnabled)
+ return;
+ if (!enabled)
+ clearCaches();
+ cachingIsEnabled = enabled;
+ }
+
+ /**
+ * @return a map from classes to associated JavaMembers objects
+ */
+ Map,JavaMembers> getClassCacheMap() {
+ if (classTable == null) {
+ classTable = new HashMap,JavaMembers>();
+ }
+ return classTable;
+ }
+
+ Map> getInterfaceAdapterCacheMap()
+ {
+ if (classAdapterCache == null) {
+ classAdapterCache = new HashMap>();
+ }
+ return classAdapterCache;
+ }
+
+ /**
+ * @deprecated
+ * The method always returns false.
+ * @see #setInvokerOptimizationEnabled(boolean enabled)
+ */
+ public boolean isInvokerOptimizationEnabled()
+ {
+ return false;
+ }
+
+ /**
+ * @deprecated
+ * The method does nothing.
+ * Invoker optimization is no longer used by Rhino.
+ * On modern JDK like 1.4 or 1.5 the disadvantages of the optimization
+ * like increased memory usage or longer initialization time overweight
+ * small speed increase that can be gained using generated proxy class
+ * to replace reflection.
+ */
+ public synchronized void setInvokerOptimizationEnabled(boolean enabled)
+ {
+ }
+
+ /**
+ * Internal engine method to return serial number for generated classes
+ * to ensure name uniqueness.
+ */
+ public final synchronized int newClassSerialNumber()
+ {
+ return ++generatedClassSerial;
+ }
+
+ Object getInterfaceAdapter(Class> cl)
+ {
+ return interfaceAdapterCache == null
+ ? null
+ : interfaceAdapterCache.get(cl);
+ }
+
+ synchronized void cacheInterfaceAdapter(Class> cl, Object iadapter)
+ {
+ if (cachingIsEnabled) {
+ if (interfaceAdapterCache == null) {
+ interfaceAdapterCache = new HashMap,Object>();
+ }
+ interfaceAdapterCache.put(cl, iadapter);
+ }
+ }
+
+ Scriptable getAssociatedScope() {
+ return associatedScope;
+ }
+}
diff --git a/src/main/java/org/mozilla/javascript/ClassShutter.java b/src/main/java/org/mozilla/javascript/ClassShutter.java
new file mode 100644
index 0000000..f425e08
--- /dev/null
+++ b/src/main/java/org/mozilla/javascript/ClassShutter.java
@@ -0,0 +1,56 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// API class
+
+package org.mozilla.javascript;
+
+/**
+Embeddings that wish to filter Java classes that are visible to scripts
+through the LiveConnect, should implement this interface.
+
+@see Context#setClassShutter(ClassShutter)
+@since 1.5 Release 4
+@author Norris Boyd
+*/
+
+ public interface ClassShutter {
+
+ /**
+ * Return true iff the Java class with the given name should be exposed
+ * to scripts.
+ *
+ * An embedding may filter which Java classes are exposed through
+ * LiveConnect to JavaScript scripts.
+ *
+ * Due to the fact that there is no package reflection in Java,
+ * this method will also be called with package names. There
+ * is no way for Rhino to tell if "Packages.a.b" is a package name
+ * or a class that doesn't exist. What Rhino does is attempt
+ * to load each segment of "Packages.a.b.c": It first attempts to
+ * load class "a", then attempts to load class "a.b", then
+ * finally attempts to load class "a.b.c". On a Rhino installation
+ * without any ClassShutter set, and without any of the
+ * above classes, the expression "Packages.a.b.c" will result in
+ * a [JavaPackage a.b.c] and not an error.
+ *
+ * With ClassShutter supplied, Rhino will first call
+ * visibleToScripts before attempting to look up the class name. If
+ * visibleToScripts returns false, the class name lookup is not
+ * performed and subsequent Rhino execution assumes the class is
+ * not present. So for "java.lang.System.out.println" the lookup
+ * of "java.lang.System" is skipped and thus Rhino assumes that
+ * "java.lang.System" doesn't exist. So then for "java.lang.System.out",
+ * Rhino attempts to load the class "java.lang.System.out" because
+ * it assumes that "java.lang.System" is a package name.
+ *
+ * @param fullClassName the full name of the class (including the package
+ * name, with '.' as a delimiter). For example the
+ * standard string class is "java.lang.String"
+ * @return whether or not to reveal this class to scripts
+ */
+ public boolean visibleToScripts(String fullClassName);
+}
diff --git a/src/main/java/org/mozilla/javascript/CodeGenerator.java b/src/main/java/org/mozilla/javascript/CodeGenerator.java
new file mode 100644
index 0000000..8e10a14
--- /dev/null
+++ b/src/main/java/org/mozilla/javascript/CodeGenerator.java
@@ -0,0 +1,1470 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import org.mozilla.javascript.ast.AstRoot;
+import org.mozilla.javascript.ast.ScriptNode;
+import org.mozilla.javascript.ast.Jump;
+import org.mozilla.javascript.ast.FunctionNode;
+
+/**
+ * Generates bytecode for the Interpreter.
+ */
+class CodeGenerator extends Icode {
+
+ private static final int MIN_LABEL_TABLE_SIZE = 32;
+ private static final int MIN_FIXUP_TABLE_SIZE = 40;
+
+ private CompilerEnvirons compilerEnv;
+
+ private boolean itsInFunctionFlag;
+ private boolean itsInTryFlag;
+
+ private InterpreterData itsData;
+
+ private ScriptNode scriptOrFn;
+ private int iCodeTop;
+ private int stackDepth;
+ private int lineNumber;
+ private int doubleTableTop;
+
+ private ObjToIntMap strings = new ObjToIntMap(20);
+ private int localTop;
+ private int[] labelTable;
+ private int labelTableTop;
+
+ // fixupTable[i] = (label_index << 32) | fixup_site
+ private long[] fixupTable;
+ private int fixupTableTop;
+ private ObjArray literalIds = new ObjArray();
+
+ private int exceptionTableTop;
+
+ // ECF_ or Expression Context Flags constants: for now only TAIL
+ private static final int ECF_TAIL = 1 << 0;
+
+ public InterpreterData compile(CompilerEnvirons compilerEnv,
+ ScriptNode tree,
+ String encodedSource,
+ boolean returnFunction)
+ {
+ this.compilerEnv = compilerEnv;
+
+ if (Token.printTrees) {
+ System.out.println("before transform:");
+ System.out.println(tree.toStringTree(tree));
+ }
+
+ new NodeTransformer().transform(tree);
+
+ if (Token.printTrees) {
+ System.out.println("after transform:");
+ System.out.println(tree.toStringTree(tree));
+ }
+
+ if (returnFunction) {
+ scriptOrFn = tree.getFunctionNode(0);
+ } else {
+ scriptOrFn = tree;
+ }
+ itsData = new InterpreterData(compilerEnv.getLanguageVersion(),
+ scriptOrFn.getSourceName(),
+ encodedSource,
+ ((AstRoot)tree).isInStrictMode());
+ itsData.topLevel = true;
+
+ if (returnFunction) {
+ generateFunctionICode();
+ } else {
+ generateICodeFromTree(scriptOrFn);
+ }
+ return itsData;
+ }
+
+ private void generateFunctionICode()
+ {
+ itsInFunctionFlag = true;
+
+ FunctionNode theFunction = (FunctionNode)scriptOrFn;
+
+ itsData.itsFunctionType = theFunction.getFunctionType();
+ itsData.itsNeedsActivation = theFunction.requiresActivation();
+ if (theFunction.getFunctionName() != null) {
+ itsData.itsName = theFunction.getName();
+ }
+ if (theFunction.isGenerator()) {
+ addIcode(Icode_GENERATOR);
+ addUint16(theFunction.getBaseLineno() & 0xFFFF);
+ }
+
+ generateICodeFromTree(theFunction.getLastChild());
+ }
+
+ private void generateICodeFromTree(Node tree)
+ {
+ generateNestedFunctions();
+
+ generateRegExpLiterals();
+
+ visitStatement(tree, 0);
+ fixLabelGotos();
+ // add RETURN_RESULT only to scripts as function always ends with RETURN
+ if (itsData.itsFunctionType == 0) {
+ addToken(Token.RETURN_RESULT);
+ }
+
+ if (itsData.itsICode.length != iCodeTop) {
+ // Make itsData.itsICode length exactly iCodeTop to save memory
+ // and catch bugs with jumps beyond icode as early as possible
+ byte[] tmp = new byte[iCodeTop];
+ System.arraycopy(itsData.itsICode, 0, tmp, 0, iCodeTop);
+ itsData.itsICode = tmp;
+ }
+ if (strings.size() == 0) {
+ itsData.itsStringTable = null;
+ } else {
+ itsData.itsStringTable = new String[strings.size()];
+ ObjToIntMap.Iterator iter = strings.newIterator();
+ for (iter.start(); !iter.done(); iter.next()) {
+ String str = (String)iter.getKey();
+ int index = iter.getValue();
+ if (itsData.itsStringTable[index] != null) Kit.codeBug();
+ itsData.itsStringTable[index] = str;
+ }
+ }
+ if (doubleTableTop == 0) {
+ itsData.itsDoubleTable = null;
+ } else if (itsData.itsDoubleTable.length != doubleTableTop) {
+ double[] tmp = new double[doubleTableTop];
+ System.arraycopy(itsData.itsDoubleTable, 0, tmp, 0,
+ doubleTableTop);
+ itsData.itsDoubleTable = tmp;
+ }
+ if (exceptionTableTop != 0
+ && itsData.itsExceptionTable.length != exceptionTableTop)
+ {
+ int[] tmp = new int[exceptionTableTop];
+ System.arraycopy(itsData.itsExceptionTable, 0, tmp, 0,
+ exceptionTableTop);
+ itsData.itsExceptionTable = tmp;
+ }
+
+ itsData.itsMaxVars = scriptOrFn.getParamAndVarCount();
+ // itsMaxFrameArray: interpret method needs this amount for its
+ // stack and sDbl arrays
+ itsData.itsMaxFrameArray = itsData.itsMaxVars
+ + itsData.itsMaxLocals
+ + itsData.itsMaxStack;
+
+ itsData.argNames = scriptOrFn.getParamAndVarNames();
+ itsData.argIsConst = scriptOrFn.getParamAndVarConst();
+ itsData.argCount = scriptOrFn.getParamCount();
+
+ itsData.encodedSourceStart = scriptOrFn.getEncodedSourceStart();
+ itsData.encodedSourceEnd = scriptOrFn.getEncodedSourceEnd();
+
+ if (literalIds.size() != 0) {
+ itsData.literalIds = literalIds.toArray();
+ }
+
+ if (Token.printICode) Interpreter.dumpICode(itsData);
+ }
+
+ private void generateNestedFunctions()
+ {
+ int functionCount = scriptOrFn.getFunctionCount();
+ if (functionCount == 0) return;
+
+ InterpreterData[] array = new InterpreterData[functionCount];
+ for (int i = 0; i != functionCount; i++) {
+ FunctionNode fn = scriptOrFn.getFunctionNode(i);
+ CodeGenerator gen = new CodeGenerator();
+ gen.compilerEnv = compilerEnv;
+ gen.scriptOrFn = fn;
+ gen.itsData = new InterpreterData(itsData);
+ gen.generateFunctionICode();
+ array[i] = gen.itsData;
+ }
+ itsData.itsNestedFunctions = array;
+ }
+
+ private void generateRegExpLiterals()
+ {
+ int N = scriptOrFn.getRegexpCount();
+ if (N == 0) return;
+
+ Context cx = Context.getContext();
+ RegExpProxy rep = ScriptRuntime.checkRegExpProxy(cx);
+ Object[] array = new Object[N];
+ for (int i = 0; i != N; i++) {
+ String string = scriptOrFn.getRegexpString(i);
+ String flags = scriptOrFn.getRegexpFlags(i);
+ array[i] = rep.compileRegExp(cx, string, flags);
+ }
+ itsData.itsRegExpLiterals = array;
+ }
+
+ private void updateLineNumber(Node node)
+ {
+ int lineno = node.getLineno();
+ if (lineno != lineNumber && lineno >= 0) {
+ if (itsData.firstLinePC < 0) {
+ itsData.firstLinePC = lineno;
+ }
+ lineNumber = lineno;
+ addIcode(Icode_LINE);
+ addUint16(lineno & 0xFFFF);
+ }
+ }
+
+ private RuntimeException badTree(Node node)
+ {
+ throw new RuntimeException(node.toString());
+ }
+
+ private void visitStatement(Node node, int initialStackDepth)
+ {
+ int type = node.getType();
+ Node child = node.getFirstChild();
+ switch (type) {
+
+ case Token.FUNCTION:
+ {
+ int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
+ int fnType = scriptOrFn.getFunctionNode(fnIndex).
+ getFunctionType();
+ // Only function expressions or function expression
+ // statements need closure code creating new function
+ // object on stack as function statements are initialized
+ // at script/function start.
+ // In addition, function expressions can not be present here
+ // at statement level, they must only be present as expressions.
+ if (fnType == FunctionNode.FUNCTION_EXPRESSION_STATEMENT) {
+ addIndexOp(Icode_CLOSURE_STMT, fnIndex);
+ } else {
+ if (fnType != FunctionNode.FUNCTION_STATEMENT) {
+ throw Kit.codeBug();
+ }
+ }
+ // For function statements or function expression statements
+ // in scripts, we need to ensure that the result of the script
+ // is the function if it is the last statement in the script.
+ // For example, eval("function () {}") should return a
+ // function, not undefined.
+ if (!itsInFunctionFlag) {
+ addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
+ stackChange(1);
+ addIcode(Icode_POP_RESULT);
+ stackChange(-1);
+ }
+ }
+ break;
+
+ case Token.LABEL:
+ case Token.LOOP:
+ case Token.BLOCK:
+ case Token.EMPTY:
+ case Token.WITH:
+ updateLineNumber(node);
+ case Token.SCRIPT:
+ // fall through
+ while (child != null) {
+ visitStatement(child, initialStackDepth);
+ child = child.getNext();
+ }
+ break;
+
+ case Token.ENTERWITH:
+ visitExpression(child, 0);
+ addToken(Token.ENTERWITH);
+ stackChange(-1);
+ break;
+
+ case Token.LEAVEWITH:
+ addToken(Token.LEAVEWITH);
+ break;
+
+ case Token.LOCAL_BLOCK:
+ {
+ int local = allocLocal();
+ node.putIntProp(Node.LOCAL_PROP, local);
+ updateLineNumber(node);
+ while (child != null) {
+ visitStatement(child, initialStackDepth);
+ child = child.getNext();
+ }
+ addIndexOp(Icode_LOCAL_CLEAR, local);
+ releaseLocal(local);
+ }
+ break;
+
+ case Token.DEBUGGER:
+ addIcode(Icode_DEBUGGER);
+ break;
+
+ case Token.SWITCH:
+ updateLineNumber(node);
+ // See comments in IRFactory.createSwitch() for description
+ // of SWITCH node
+ {
+ visitExpression(child, 0);
+ for (Jump caseNode = (Jump)child.getNext();
+ caseNode != null;
+ caseNode = (Jump)caseNode.getNext())
+ {
+ if (caseNode.getType() != Token.CASE)
+ throw badTree(caseNode);
+ Node test = caseNode.getFirstChild();
+ addIcode(Icode_DUP);
+ stackChange(1);
+ visitExpression(test, 0);
+ addToken(Token.SHEQ);
+ stackChange(-1);
+ // If true, Icode_IFEQ_POP will jump and remove case
+ // value from stack
+ addGoto(caseNode.target, Icode_IFEQ_POP);
+ stackChange(-1);
+ }
+ addIcode(Icode_POP);
+ stackChange(-1);
+ }
+ break;
+
+ case Token.TARGET:
+ markTargetLabel(node);
+ break;
+
+ case Token.IFEQ :
+ case Token.IFNE :
+ {
+ Node target = ((Jump)node).target;
+ visitExpression(child, 0);
+ addGoto(target, type);
+ stackChange(-1);
+ }
+ break;
+
+ case Token.GOTO:
+ {
+ Node target = ((Jump)node).target;
+ addGoto(target, type);
+ }
+ break;
+
+ case Token.JSR:
+ {
+ Node target = ((Jump)node).target;
+ addGoto(target, Icode_GOSUB);
+ }
+ break;
+
+ case Token.FINALLY:
+ {
+ // Account for incomming GOTOSUB address
+ stackChange(1);
+ int finallyRegister = getLocalBlockRef(node);
+ addIndexOp(Icode_STARTSUB, finallyRegister);
+ stackChange(-1);
+ while (child != null) {
+ visitStatement(child, initialStackDepth);
+ child = child.getNext();
+ }
+ addIndexOp(Icode_RETSUB, finallyRegister);
+ }
+ break;
+
+ case Token.EXPR_VOID:
+ case Token.EXPR_RESULT:
+ updateLineNumber(node);
+ visitExpression(child, 0);
+ addIcode((type == Token.EXPR_VOID) ? Icode_POP : Icode_POP_RESULT);
+ stackChange(-1);
+ break;
+
+ case Token.TRY:
+ {
+ Jump tryNode = (Jump)node;
+ int exceptionObjectLocal = getLocalBlockRef(tryNode);
+ int scopeLocal = allocLocal();
+
+ addIndexOp(Icode_SCOPE_SAVE, scopeLocal);
+
+ int tryStart = iCodeTop;
+ boolean savedFlag = itsInTryFlag;
+ itsInTryFlag = true;
+ while (child != null) {
+ visitStatement(child, initialStackDepth);
+ child = child.getNext();
+ }
+ itsInTryFlag = savedFlag;
+
+ Node catchTarget = tryNode.target;
+ if (catchTarget != null) {
+ int catchStartPC
+ = labelTable[getTargetLabel(catchTarget)];
+ addExceptionHandler(
+ tryStart, catchStartPC, catchStartPC,
+ false, exceptionObjectLocal, scopeLocal);
+ }
+ Node finallyTarget = tryNode.getFinally();
+ if (finallyTarget != null) {
+ int finallyStartPC
+ = labelTable[getTargetLabel(finallyTarget)];
+ addExceptionHandler(
+ tryStart, finallyStartPC, finallyStartPC,
+ true, exceptionObjectLocal, scopeLocal);
+ }
+
+ addIndexOp(Icode_LOCAL_CLEAR, scopeLocal);
+ releaseLocal(scopeLocal);
+ }
+ break;
+
+ case Token.CATCH_SCOPE:
+ {
+ int localIndex = getLocalBlockRef(node);
+ int scopeIndex = node.getExistingIntProp(Node.CATCH_SCOPE_PROP);
+ String name = child.getString();
+ child = child.getNext();
+ visitExpression(child, 0); // load expression object
+ addStringPrefix(name);
+ addIndexPrefix(localIndex);
+ addToken(Token.CATCH_SCOPE);
+ addUint8(scopeIndex != 0 ? 1 : 0);
+ stackChange(-1);
+ }
+ break;
+
+ case Token.THROW:
+ updateLineNumber(node);
+ visitExpression(child, 0);
+ addToken(Token.THROW);
+ addUint16(lineNumber & 0xFFFF);
+ stackChange(-1);
+ break;
+
+ case Token.RETHROW:
+ updateLineNumber(node);
+ addIndexOp(Token.RETHROW, getLocalBlockRef(node));
+ break;
+
+ case Token.RETURN:
+ updateLineNumber(node);
+ if (node.getIntProp(Node.GENERATOR_END_PROP, 0) != 0) {
+ // We're in a generator, so change RETURN to GENERATOR_END
+ addIcode(Icode_GENERATOR_END);
+ addUint16(lineNumber & 0xFFFF);
+ } else if (child != null) {
+ visitExpression(child, ECF_TAIL);
+ addToken(Token.RETURN);
+ stackChange(-1);
+ } else {
+ addIcode(Icode_RETUNDEF);
+ }
+ break;
+
+ case Token.RETURN_RESULT:
+ updateLineNumber(node);
+ addToken(Token.RETURN_RESULT);
+ break;
+
+ case Token.ENUM_INIT_KEYS:
+ case Token.ENUM_INIT_VALUES:
+ case Token.ENUM_INIT_ARRAY:
+ visitExpression(child, 0);
+ addIndexOp(type, getLocalBlockRef(node));
+ stackChange(-1);
+ break;
+
+ case Icode_GENERATOR:
+ break;
+
+ default:
+ throw badTree(node);
+ }
+
+ if (stackDepth != initialStackDepth) {
+ throw Kit.codeBug();
+ }
+ }
+
+ private void visitExpression(Node node, int contextFlags)
+ {
+ int type = node.getType();
+ Node child = node.getFirstChild();
+ int savedStackDepth = stackDepth;
+ switch (type) {
+
+ case Token.FUNCTION:
+ {
+ int fnIndex = node.getExistingIntProp(Node.FUNCTION_PROP);
+ FunctionNode fn = scriptOrFn.getFunctionNode(fnIndex);
+ // See comments in visitStatement for Token.FUNCTION case
+ if (fn.getFunctionType() != FunctionNode.FUNCTION_EXPRESSION) {
+ throw Kit.codeBug();
+ }
+ addIndexOp(Icode_CLOSURE_EXPR, fnIndex);
+ stackChange(1);
+ }
+ break;
+
+ case Token.LOCAL_LOAD:
+ {
+ int localIndex = getLocalBlockRef(node);
+ addIndexOp(Token.LOCAL_LOAD, localIndex);
+ stackChange(1);
+ }
+ break;
+
+ case Token.COMMA:
+ {
+ Node lastChild = node.getLastChild();
+ while (child != lastChild) {
+ visitExpression(child, 0);
+ addIcode(Icode_POP);
+ stackChange(-1);
+ child = child.getNext();
+ }
+ // Preserve tail context flag if any
+ visitExpression(child, contextFlags & ECF_TAIL);
+ }
+ break;
+
+ case Token.USE_STACK:
+ // Indicates that stack was modified externally,
+ // like placed catch object
+ stackChange(1);
+ break;
+
+ case Token.REF_CALL:
+ case Token.CALL:
+ case Token.NEW:
+ {
+ if (type == Token.NEW) {
+ visitExpression(child, 0);
+ } else {
+ generateCallFunAndThis(child);
+ }
+ int argCount = 0;
+ while ((child = child.getNext()) != null) {
+ visitExpression(child, 0);
+ ++argCount;
+ }
+ int callType = node.getIntProp(Node.SPECIALCALL_PROP,
+ Node.NON_SPECIALCALL);
+ if (callType != Node.NON_SPECIALCALL) {
+ // embed line number and source filename
+ addIndexOp(Icode_CALLSPECIAL, argCount);
+ addUint8(callType);
+ addUint8(type == Token.NEW ? 1 : 0);
+ addUint16(lineNumber & 0xFFFF);
+ } else {
+ // Only use the tail call optimization if we're not in a try
+ // or we're not generating debug info (since the
+ // optimization will confuse the debugger)
+ if (type == Token.CALL && (contextFlags & ECF_TAIL) != 0 &&
+ !compilerEnv.isGenerateDebugInfo() && !itsInTryFlag)
+ {
+ type = Icode_TAIL_CALL;
+ }
+ addIndexOp(type, argCount);
+ }
+ // adjust stack
+ if (type == Token.NEW) {
+ // new: f, args -> result
+ stackChange(-argCount);
+ } else {
+ // call: f, thisObj, args -> result
+ // ref_call: f, thisObj, args -> ref
+ stackChange(-1 - argCount);
+ }
+ if (argCount > itsData.itsMaxCalleeArgs) {
+ itsData.itsMaxCalleeArgs = argCount;
+ }
+ }
+ break;
+
+ case Token.AND:
+ case Token.OR:
+ {
+ visitExpression(child, 0);
+ addIcode(Icode_DUP);
+ stackChange(1);
+ int afterSecondJumpStart = iCodeTop;
+ int jump = (type == Token.AND) ? Token.IFNE : Token.IFEQ;
+ addGotoOp(jump);
+ stackChange(-1);
+ addIcode(Icode_POP);
+ stackChange(-1);
+ child = child.getNext();
+ // Preserve tail context flag if any
+ visitExpression(child, contextFlags & ECF_TAIL);
+ resolveForwardGoto(afterSecondJumpStart);
+ }
+ break;
+
+ case Token.HOOK:
+ {
+ Node ifThen = child.getNext();
+ Node ifElse = ifThen.getNext();
+ visitExpression(child, 0);
+ int elseJumpStart = iCodeTop;
+ addGotoOp(Token.IFNE);
+ stackChange(-1);
+ // Preserve tail context flag if any
+ visitExpression(ifThen, contextFlags & ECF_TAIL);
+ int afterElseJumpStart = iCodeTop;
+ addGotoOp(Token.GOTO);
+ resolveForwardGoto(elseJumpStart);
+ stackDepth = savedStackDepth;
+ // Preserve tail context flag if any
+ visitExpression(ifElse, contextFlags & ECF_TAIL);
+ resolveForwardGoto(afterElseJumpStart);
+ }
+ break;
+
+ case Token.GETPROP:
+ case Token.GETPROPNOWARN:
+ visitExpression(child, 0);
+ child = child.getNext();
+ addStringOp(type, child.getString());
+ break;
+
+ case Token.DELPROP:
+ boolean isName = child.getType() == Token.BINDNAME;
+ visitExpression(child, 0);
+ child = child.getNext();
+ visitExpression(child, 0);
+ if (isName) {
+ // special handling for delete name
+ addIcode(Icode_DELNAME);
+ } else {
+ addToken(Token.DELPROP);
+ }
+ stackChange(-1);
+ break;
+
+ case Token.GETELEM:
+ case Token.BITAND:
+ case Token.BITOR:
+ case Token.BITXOR:
+ case Token.LSH:
+ case Token.RSH:
+ case Token.URSH:
+ case Token.ADD:
+ case Token.SUB:
+ case Token.MOD:
+ case Token.DIV:
+ case Token.MUL:
+ case Token.EQ:
+ case Token.NE:
+ case Token.SHEQ:
+ case Token.SHNE:
+ case Token.IN:
+ case Token.INSTANCEOF:
+ case Token.LE:
+ case Token.LT:
+ case Token.GE:
+ case Token.GT:
+ visitExpression(child, 0);
+ child = child.getNext();
+ visitExpression(child, 0);
+ addToken(type);
+ stackChange(-1);
+ break;
+
+ case Token.POS:
+ case Token.NEG:
+ case Token.NOT:
+ case Token.BITNOT:
+ case Token.TYPEOF:
+ case Token.VOID:
+ visitExpression(child, 0);
+ if (type == Token.VOID) {
+ addIcode(Icode_POP);
+ addIcode(Icode_UNDEF);
+ } else {
+ addToken(type);
+ }
+ break;
+
+ case Token.GET_REF:
+ case Token.DEL_REF:
+ visitExpression(child, 0);
+ addToken(type);
+ break;
+
+ case Token.SETPROP:
+ case Token.SETPROP_OP:
+ {
+ visitExpression(child, 0);
+ child = child.getNext();
+ String property = child.getString();
+ child = child.getNext();
+ if (type == Token.SETPROP_OP) {
+ addIcode(Icode_DUP);
+ stackChange(1);
+ addStringOp(Token.GETPROP, property);
+ // Compensate for the following USE_STACK
+ stackChange(-1);
+ }
+ visitExpression(child, 0);
+ addStringOp(Token.SETPROP, property);
+ stackChange(-1);
+ }
+ break;
+
+ case Token.SETELEM:
+ case Token.SETELEM_OP:
+ visitExpression(child, 0);
+ child = child.getNext();
+ visitExpression(child, 0);
+ child = child.getNext();
+ if (type == Token.SETELEM_OP) {
+ addIcode(Icode_DUP2);
+ stackChange(2);
+ addToken(Token.GETELEM);
+ stackChange(-1);
+ // Compensate for the following USE_STACK
+ stackChange(-1);
+ }
+ visitExpression(child, 0);
+ addToken(Token.SETELEM);
+ stackChange(-2);
+ break;
+
+ case Token.SET_REF:
+ case Token.SET_REF_OP:
+ visitExpression(child, 0);
+ child = child.getNext();
+ if (type == Token.SET_REF_OP) {
+ addIcode(Icode_DUP);
+ stackChange(1);
+ addToken(Token.GET_REF);
+ // Compensate for the following USE_STACK
+ stackChange(-1);
+ }
+ visitExpression(child, 0);
+ addToken(Token.SET_REF);
+ stackChange(-1);
+ break;
+
+ case Token.STRICT_SETNAME:
+ case Token.SETNAME:
+ {
+ String name = child.getString();
+ visitExpression(child, 0);
+ child = child.getNext();
+ visitExpression(child, 0);
+ addStringOp(type, name);
+ stackChange(-1);
+ }
+ break;
+
+ case Token.SETCONST:
+ {
+ String name = child.getString();
+ visitExpression(child, 0);
+ child = child.getNext();
+ visitExpression(child, 0);
+ addStringOp(Icode_SETCONST, name);
+ stackChange(-1);
+ }
+ break;
+
+ case Token.TYPEOFNAME:
+ {
+ int index = -1;
+ // use typeofname if an activation frame exists
+ // since the vars all exist there instead of in jregs
+ if (itsInFunctionFlag && !itsData.itsNeedsActivation)
+ index = scriptOrFn.getIndexForNameNode(node);
+ if (index == -1) {
+ addStringOp(Icode_TYPEOFNAME, node.getString());
+ stackChange(1);
+ } else {
+ addVarOp(Token.GETVAR, index);
+ stackChange(1);
+ addToken(Token.TYPEOF);
+ }
+ }
+ break;
+
+ case Token.BINDNAME:
+ case Token.NAME:
+ case Token.STRING:
+ addStringOp(type, node.getString());
+ stackChange(1);
+ break;
+
+ case Token.INC:
+ case Token.DEC:
+ visitIncDec(node, child);
+ break;
+
+ case Token.NUMBER:
+ {
+ double num = node.getDouble();
+ int inum = (int)num;
+ if (inum == num) {
+ if (inum == 0) {
+ addIcode(Icode_ZERO);
+ // Check for negative zero
+ if (1.0 / num < 0.0) {
+ addToken(Token.NEG);
+ }
+ } else if (inum == 1) {
+ addIcode(Icode_ONE);
+ } else if ((short)inum == inum) {
+ addIcode(Icode_SHORTNUMBER);
+ // write short as uin16 bit pattern
+ addUint16(inum & 0xFFFF);
+ } else {
+ addIcode(Icode_INTNUMBER);
+ addInt(inum);
+ }
+ } else {
+ int index = getDoubleIndex(num);
+ addIndexOp(Token.NUMBER, index);
+ }
+ stackChange(1);
+ }
+ break;
+
+ case Token.GETVAR:
+ {
+ if (itsData.itsNeedsActivation) Kit.codeBug();
+ int index = scriptOrFn.getIndexForNameNode(node);
+ addVarOp(Token.GETVAR, index);
+ stackChange(1);
+ }
+ break;
+
+ case Token.SETVAR:
+ {
+ if (itsData.itsNeedsActivation) Kit.codeBug();
+ int index = scriptOrFn.getIndexForNameNode(child);
+ child = child.getNext();
+ visitExpression(child, 0);
+ addVarOp(Token.SETVAR, index);
+ }
+ break;
+
+ case Token.SETCONSTVAR:
+ {
+ if (itsData.itsNeedsActivation) Kit.codeBug();
+ int index = scriptOrFn.getIndexForNameNode(child);
+ child = child.getNext();
+ visitExpression(child, 0);
+ addVarOp(Token.SETCONSTVAR, index);
+ }
+ break;
+
+ case Token.NULL:
+ case Token.THIS:
+ case Token.THISFN:
+ case Token.FALSE:
+ case Token.TRUE:
+ addToken(type);
+ stackChange(1);
+ break;
+
+ case Token.ENUM_NEXT:
+ case Token.ENUM_ID:
+ addIndexOp(type, getLocalBlockRef(node));
+ stackChange(1);
+ break;
+
+ case Token.REGEXP:
+ {
+ int index = node.getExistingIntProp(Node.REGEXP_PROP);
+ addIndexOp(Token.REGEXP, index);
+ stackChange(1);
+ }
+ break;
+
+ case Token.ARRAYLIT:
+ case Token.OBJECTLIT:
+ visitLiteral(node, child);
+ break;
+
+ case Token.ARRAYCOMP:
+ visitArrayComprehension(node, child, child.getNext());
+ break;
+
+ case Token.REF_SPECIAL:
+ visitExpression(child, 0);
+ addStringOp(type, (String)node.getProp(Node.NAME_PROP));
+ break;
+
+ case Token.REF_MEMBER:
+ case Token.REF_NS_MEMBER:
+ case Token.REF_NAME:
+ case Token.REF_NS_NAME:
+ {
+ int memberTypeFlags = node.getIntProp(Node.MEMBER_TYPE_PROP, 0);
+ // generate possible target, possible namespace and member
+ int childCount = 0;
+ do {
+ visitExpression(child, 0);
+ ++childCount;
+ child = child.getNext();
+ } while (child != null);
+ addIndexOp(type, memberTypeFlags);
+ stackChange(1 - childCount);
+ }
+ break;
+
+ case Token.DOTQUERY:
+ {
+ int queryPC;
+ updateLineNumber(node);
+ visitExpression(child, 0);
+ addIcode(Icode_ENTERDQ);
+ stackChange(-1);
+ queryPC = iCodeTop;
+ visitExpression(child.getNext(), 0);
+ addBackwardGoto(Icode_LEAVEDQ, queryPC);
+ }
+ break;
+
+ case Token.DEFAULTNAMESPACE :
+ case Token.ESCXMLATTR :
+ case Token.ESCXMLTEXT :
+ visitExpression(child, 0);
+ addToken(type);
+ break;
+
+ case Token.YIELD:
+ if (child != null) {
+ visitExpression(child, 0);
+ } else {
+ addIcode(Icode_UNDEF);
+ stackChange(1);
+ }
+ addToken(Token.YIELD);
+ addUint16(node.getLineno() & 0xFFFF);
+ break;
+
+ case Token.WITHEXPR: {
+ Node enterWith = node.getFirstChild();
+ Node with = enterWith.getNext();
+ visitExpression(enterWith.getFirstChild(), 0);
+ addToken(Token.ENTERWITH);
+ stackChange(-1);
+ visitExpression(with.getFirstChild(), 0);
+ addToken(Token.LEAVEWITH);
+ break;
+ }
+
+ default:
+ throw badTree(node);
+ }
+ if (savedStackDepth + 1 != stackDepth) {
+ Kit.codeBug();
+ }
+ }
+
+ private void generateCallFunAndThis(Node left)
+ {
+ // Generate code to place on stack function and thisObj
+ int type = left.getType();
+ switch (type) {
+ case Token.NAME: {
+ String name = left.getString();
+ // stack: ... -> ... function thisObj
+ addStringOp(Icode_NAME_AND_THIS, name);
+ stackChange(2);
+ break;
+ }
+ case Token.GETPROP:
+ case Token.GETELEM: {
+ Node target = left.getFirstChild();
+ visitExpression(target, 0);
+ Node id = target.getNext();
+ if (type == Token.GETPROP) {
+ String property = id.getString();
+ // stack: ... target -> ... function thisObj
+ addStringOp(Icode_PROP_AND_THIS, property);
+ stackChange(1);
+ } else {
+ visitExpression(id, 0);
+ // stack: ... target id -> ... function thisObj
+ addIcode(Icode_ELEM_AND_THIS);
+ }
+ break;
+ }
+ default:
+ // Including Token.GETVAR
+ visitExpression(left, 0);
+ // stack: ... value -> ... function thisObj
+ addIcode(Icode_VALUE_AND_THIS);
+ stackChange(1);
+ break;
+ }
+ }
+
+
+ private void visitIncDec(Node node, Node child)
+ {
+ int incrDecrMask = node.getExistingIntProp(Node.INCRDECR_PROP);
+ int childType = child.getType();
+ switch (childType) {
+ case Token.GETVAR : {
+ if (itsData.itsNeedsActivation) Kit.codeBug();
+ int i = scriptOrFn.getIndexForNameNode(child);
+ addVarOp(Icode_VAR_INC_DEC, i);
+ addUint8(incrDecrMask);
+ stackChange(1);
+ break;
+ }
+ case Token.NAME : {
+ String name = child.getString();
+ addStringOp(Icode_NAME_INC_DEC, name);
+ addUint8(incrDecrMask);
+ stackChange(1);
+ break;
+ }
+ case Token.GETPROP : {
+ Node object = child.getFirstChild();
+ visitExpression(object, 0);
+ String property = object.getNext().getString();
+ addStringOp(Icode_PROP_INC_DEC, property);
+ addUint8(incrDecrMask);
+ break;
+ }
+ case Token.GETELEM : {
+ Node object = child.getFirstChild();
+ visitExpression(object, 0);
+ Node index = object.getNext();
+ visitExpression(index, 0);
+ addIcode(Icode_ELEM_INC_DEC);
+ addUint8(incrDecrMask);
+ stackChange(-1);
+ break;
+ }
+ case Token.GET_REF : {
+ Node ref = child.getFirstChild();
+ visitExpression(ref, 0);
+ addIcode(Icode_REF_INC_DEC);
+ addUint8(incrDecrMask);
+ break;
+ }
+ default : {
+ throw badTree(node);
+ }
+ }
+ }
+
+ private void visitLiteral(Node node, Node child)
+ {
+ int type = node.getType();
+ int count;
+ Object[] propertyIds = null;
+ if (type == Token.ARRAYLIT) {
+ count = 0;
+ for (Node n = child; n != null; n = n.getNext()) {
+ ++count;
+ }
+ } else if (type == Token.OBJECTLIT) {
+ propertyIds = (Object[])node.getProp(Node.OBJECT_IDS_PROP);
+ count = propertyIds.length;
+ } else {
+ throw badTree(node);
+ }
+ addIndexOp(Icode_LITERAL_NEW, count);
+ stackChange(2);
+ while (child != null) {
+ int childType = child.getType();
+ if (childType == Token.GET) {
+ visitExpression(child.getFirstChild(), 0);
+ addIcode(Icode_LITERAL_GETTER);
+ } else if (childType == Token.SET) {
+ visitExpression(child.getFirstChild(), 0);
+ addIcode(Icode_LITERAL_SETTER);
+ } else {
+ visitExpression(child, 0);
+ addIcode(Icode_LITERAL_SET);
+ }
+ stackChange(-1);
+ child = child.getNext();
+ }
+ if (type == Token.ARRAYLIT) {
+ int[] skipIndexes = (int[])node.getProp(Node.SKIP_INDEXES_PROP);
+ if (skipIndexes == null) {
+ addToken(Token.ARRAYLIT);
+ } else {
+ int index = literalIds.size();
+ literalIds.add(skipIndexes);
+ addIndexOp(Icode_SPARE_ARRAYLIT, index);
+ }
+ } else {
+ int index = literalIds.size();
+ literalIds.add(propertyIds);
+ addIndexOp(Token.OBJECTLIT, index);
+ }
+ stackChange(-1);
+ }
+
+ private void visitArrayComprehension(Node node, Node initStmt, Node expr)
+ {
+ // A bit of a hack: array comprehensions are implemented using
+ // statement nodes for the iteration, yet they appear in an
+ // expression context. So we pass the current stack depth to
+ // visitStatement so it can check that the depth is not altered
+ // by statements.
+ visitStatement(initStmt, stackDepth);
+ visitExpression(expr, 0);
+ }
+
+ private int getLocalBlockRef(Node node)
+ {
+ Node localBlock = (Node)node.getProp(Node.LOCAL_BLOCK_PROP);
+ return localBlock.getExistingIntProp(Node.LOCAL_PROP);
+ }
+
+ private int getTargetLabel(Node target)
+ {
+ int label = target.labelId();
+ if (label != -1) {
+ return label;
+ }
+ label = labelTableTop;
+ if (labelTable == null || label == labelTable.length) {
+ if (labelTable == null) {
+ labelTable = new int[MIN_LABEL_TABLE_SIZE];
+ }else {
+ int[] tmp = new int[labelTable.length * 2];
+ System.arraycopy(labelTable, 0, tmp, 0, label);
+ labelTable = tmp;
+ }
+ }
+ labelTableTop = label + 1;
+ labelTable[label] = -1;
+
+ target.labelId(label);
+ return label;
+ }
+
+ private void markTargetLabel(Node target)
+ {
+ int label = getTargetLabel(target);
+ if (labelTable[label] != -1) {
+ // Can mark label only once
+ Kit.codeBug();
+ }
+ labelTable[label] = iCodeTop;
+ }
+
+ private void addGoto(Node target, int gotoOp)
+ {
+ int label = getTargetLabel(target);
+ if (!(label < labelTableTop)) Kit.codeBug();
+ int targetPC = labelTable[label];
+
+ if (targetPC != -1) {
+ addBackwardGoto(gotoOp, targetPC);
+ } else {
+ int gotoPC = iCodeTop;
+ addGotoOp(gotoOp);
+ int top = fixupTableTop;
+ if (fixupTable == null || top == fixupTable.length) {
+ if (fixupTable == null) {
+ fixupTable = new long[MIN_FIXUP_TABLE_SIZE];
+ } else {
+ long[] tmp = new long[fixupTable.length * 2];
+ System.arraycopy(fixupTable, 0, tmp, 0, top);
+ fixupTable = tmp;
+ }
+ }
+ fixupTableTop = top + 1;
+ fixupTable[top] = ((long)label << 32) | gotoPC;
+ }
+ }
+
+ private void fixLabelGotos()
+ {
+ for (int i = 0; i < fixupTableTop; i++) {
+ long fixup = fixupTable[i];
+ int label = (int)(fixup >> 32);
+ int jumpSource = (int)fixup;
+ int pc = labelTable[label];
+ if (pc == -1) {
+ // Unlocated label
+ throw Kit.codeBug();
+ }
+ resolveGoto(jumpSource, pc);
+ }
+ fixupTableTop = 0;
+ }
+
+ private void addBackwardGoto(int gotoOp, int jumpPC)
+ {
+ int fromPC = iCodeTop;
+ // Ensure that this is a jump backward
+ if (fromPC <= jumpPC) throw Kit.codeBug();
+ addGotoOp(gotoOp);
+ resolveGoto(fromPC, jumpPC);
+ }
+
+ private void resolveForwardGoto(int fromPC)
+ {
+ // Ensure that forward jump skips at least self bytecode
+ if (iCodeTop < fromPC + 3) throw Kit.codeBug();
+ resolveGoto(fromPC, iCodeTop);
+ }
+
+ private void resolveGoto(int fromPC, int jumpPC)
+ {
+ int offset = jumpPC - fromPC;
+ // Ensure that jumps do not overlap
+ if (0 <= offset && offset <= 2) throw Kit.codeBug();
+ int offsetSite = fromPC + 1;
+ if (offset != (short)offset) {
+ if (itsData.longJumps == null) {
+ itsData.longJumps = new UintMap();
+ }
+ itsData.longJumps.put(offsetSite, jumpPC);
+ offset = 0;
+ }
+ byte[] array = itsData.itsICode;
+ array[offsetSite] = (byte)(offset >> 8);
+ array[offsetSite + 1] = (byte)offset;
+ }
+
+ private void addToken(int token)
+ {
+ if (!Icode.validTokenCode(token)) throw Kit.codeBug();
+ addUint8(token);
+ }
+
+ private void addIcode(int icode)
+ {
+ if (!Icode.validIcode(icode)) throw Kit.codeBug();
+ // Write negative icode as uint8 bits
+ addUint8(icode & 0xFF);
+ }
+
+ private void addUint8(int value)
+ {
+ if ((value & ~0xFF) != 0) throw Kit.codeBug();
+ byte[] array = itsData.itsICode;
+ int top = iCodeTop;
+ if (top == array.length) {
+ array = increaseICodeCapacity(1);
+ }
+ array[top] = (byte)value;
+ iCodeTop = top + 1;
+ }
+
+ private void addUint16(int value)
+ {
+ if ((value & ~0xFFFF) != 0) throw Kit.codeBug();
+ byte[] array = itsData.itsICode;
+ int top = iCodeTop;
+ if (top + 2 > array.length) {
+ array = increaseICodeCapacity(2);
+ }
+ array[top] = (byte)(value >>> 8);
+ array[top + 1] = (byte)value;
+ iCodeTop = top + 2;
+ }
+
+ private void addInt(int i)
+ {
+ byte[] array = itsData.itsICode;
+ int top = iCodeTop;
+ if (top + 4 > array.length) {
+ array = increaseICodeCapacity(4);
+ }
+ array[top] = (byte)(i >>> 24);
+ array[top + 1] = (byte)(i >>> 16);
+ array[top + 2] = (byte)(i >>> 8);
+ array[top + 3] = (byte)i;
+ iCodeTop = top + 4;
+ }
+
+ private int getDoubleIndex(double num)
+ {
+ int index = doubleTableTop;
+ if (index == 0) {
+ itsData.itsDoubleTable = new double[64];
+ } else if (itsData.itsDoubleTable.length == index) {
+ double[] na = new double[index * 2];
+ System.arraycopy(itsData.itsDoubleTable, 0, na, 0, index);
+ itsData.itsDoubleTable = na;
+ }
+ itsData.itsDoubleTable[index] = num;
+ doubleTableTop = index + 1;
+ return index;
+ }
+
+ private void addGotoOp(int gotoOp)
+ {
+ byte[] array = itsData.itsICode;
+ int top = iCodeTop;
+ if (top + 3 > array.length) {
+ array = increaseICodeCapacity(3);
+ }
+ array[top] = (byte)gotoOp;
+ // Offset would written later
+ iCodeTop = top + 1 + 2;
+ }
+
+ private void addVarOp(int op, int varIndex)
+ {
+ switch (op) {
+ case Token.SETCONSTVAR:
+ if (varIndex < 128) {
+ addIcode(Icode_SETCONSTVAR1);
+ addUint8(varIndex);
+ return;
+ }
+ addIndexOp(Icode_SETCONSTVAR, varIndex);
+ return;
+ case Token.GETVAR:
+ case Token.SETVAR:
+ if (varIndex < 128) {
+ addIcode(op == Token.GETVAR ? Icode_GETVAR1 : Icode_SETVAR1);
+ addUint8(varIndex);
+ return;
+ }
+ // fallthrough
+ case Icode_VAR_INC_DEC:
+ addIndexOp(op, varIndex);
+ return;
+ }
+ throw Kit.codeBug();
+ }
+
+ private void addStringOp(int op, String str)
+ {
+ addStringPrefix(str);
+ if (Icode.validIcode(op)) {
+ addIcode(op);
+ } else {
+ addToken(op);
+ }
+ }
+
+ private void addIndexOp(int op, int index)
+ {
+ addIndexPrefix(index);
+ if (Icode.validIcode(op)) {
+ addIcode(op);
+ } else {
+ addToken(op);
+ }
+ }
+
+ private void addStringPrefix(String str)
+ {
+ int index = strings.get(str, -1);
+ if (index == -1) {
+ index = strings.size();
+ strings.put(str, index);
+ }
+ if (index < 4) {
+ addIcode(Icode_REG_STR_C0 - index);
+ } else if (index <= 0xFF) {
+ addIcode(Icode_REG_STR1);
+ addUint8(index);
+ } else if (index <= 0xFFFF) {
+ addIcode(Icode_REG_STR2);
+ addUint16(index);
+ } else {
+ addIcode(Icode_REG_STR4);
+ addInt(index);
+ }
+ }
+
+ private void addIndexPrefix(int index)
+ {
+ if (index < 0) Kit.codeBug();
+ if (index < 6) {
+ addIcode(Icode_REG_IND_C0 - index);
+ } else if (index <= 0xFF) {
+ addIcode(Icode_REG_IND1);
+ addUint8(index);
+ } else if (index <= 0xFFFF) {
+ addIcode(Icode_REG_IND2);
+ addUint16(index);
+ } else {
+ addIcode(Icode_REG_IND4);
+ addInt(index);
+ }
+ }
+
+ private void addExceptionHandler(int icodeStart, int icodeEnd,
+ int handlerStart, boolean isFinally,
+ int exceptionObjectLocal, int scopeLocal)
+ {
+ int top = exceptionTableTop;
+ int[] table = itsData.itsExceptionTable;
+ if (table == null) {
+ if (top != 0) Kit.codeBug();
+ table = new int[Interpreter.EXCEPTION_SLOT_SIZE * 2];
+ itsData.itsExceptionTable = table;
+ } else if (table.length == top) {
+ table = new int[table.length * 2];
+ System.arraycopy(itsData.itsExceptionTable, 0, table, 0, top);
+ itsData.itsExceptionTable = table;
+ }
+ table[top + Interpreter.EXCEPTION_TRY_START_SLOT] = icodeStart;
+ table[top + Interpreter.EXCEPTION_TRY_END_SLOT] = icodeEnd;
+ table[top + Interpreter.EXCEPTION_HANDLER_SLOT] = handlerStart;
+ table[top + Interpreter.EXCEPTION_TYPE_SLOT] = isFinally ? 1 : 0;
+ table[top + Interpreter.EXCEPTION_LOCAL_SLOT] = exceptionObjectLocal;
+ table[top + Interpreter.EXCEPTION_SCOPE_SLOT] = scopeLocal;
+
+ exceptionTableTop = top + Interpreter.EXCEPTION_SLOT_SIZE;
+ }
+
+ private byte[] increaseICodeCapacity(int extraSize)
+ {
+ int capacity = itsData.itsICode.length;
+ int top = iCodeTop;
+ if (top + extraSize <= capacity) throw Kit.codeBug();
+ capacity *= 2;
+ if (top + extraSize > capacity) {
+ capacity = top + extraSize;
+ }
+ byte[] array = new byte[capacity];
+ System.arraycopy(itsData.itsICode, 0, array, 0, top);
+ itsData.itsICode = array;
+ return array;
+ }
+
+ private void stackChange(int change)
+ {
+ if (change <= 0) {
+ stackDepth += change;
+ } else {
+ int newDepth = stackDepth + change;
+ if (newDepth > itsData.itsMaxStack) {
+ itsData.itsMaxStack = newDepth;
+ }
+ stackDepth = newDepth;
+ }
+ }
+
+ private int allocLocal()
+ {
+ int localSlot = localTop;
+ ++localTop;
+ if (localTop > itsData.itsMaxLocals) {
+ itsData.itsMaxLocals = localTop;
+ }
+ return localSlot;
+ }
+
+ private void releaseLocal(int localSlot)
+ {
+ --localTop;
+ if (localSlot != localTop) Kit.codeBug();
+ }
+}
diff --git a/src/main/java/org/mozilla/javascript/CompilerEnvirons.java b/src/main/java/org/mozilla/javascript/CompilerEnvirons.java
new file mode 100644
index 0000000..9ef81bb
--- /dev/null
+++ b/src/main/java/org/mozilla/javascript/CompilerEnvirons.java
@@ -0,0 +1,295 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.util.Set;
+
+import org.mozilla.javascript.ast.ErrorCollector;
+
+public class CompilerEnvirons
+{
+ public CompilerEnvirons()
+ {
+ errorReporter = DefaultErrorReporter.instance;
+ languageVersion = Context.VERSION_DEFAULT;
+ generateDebugInfo = true;
+ reservedKeywordAsIdentifier = true;
+ allowMemberExprAsFunctionName = false;
+ xmlAvailable = true;
+ optimizationLevel = 0;
+ generatingSource = true;
+ strictMode = false;
+ warningAsError = false;
+ generateObserverCount = false;
+ allowSharpComments = false;
+ }
+
+ public void initFromContext(Context cx)
+ {
+ setErrorReporter(cx.getErrorReporter());
+ languageVersion = cx.getLanguageVersion();
+ generateDebugInfo = (!cx.isGeneratingDebugChanged()
+ || cx.isGeneratingDebug());
+ reservedKeywordAsIdentifier
+ = cx.hasFeature(Context.FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER);
+ allowMemberExprAsFunctionName
+ = cx.hasFeature(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME);
+ strictMode
+ = cx.hasFeature(Context.FEATURE_STRICT_MODE);
+ warningAsError = cx.hasFeature(Context.FEATURE_WARNING_AS_ERROR);
+ xmlAvailable
+ = cx.hasFeature(Context.FEATURE_E4X);
+
+ optimizationLevel = cx.getOptimizationLevel();
+
+ generatingSource = cx.isGeneratingSource();
+ activationNames = cx.activationNames;
+
+ // Observer code generation in compiled code :
+ generateObserverCount = cx.generateObserverCount;
+ }
+
+ public final ErrorReporter getErrorReporter()
+ {
+ return errorReporter;
+ }
+
+ public void setErrorReporter(ErrorReporter errorReporter)
+ {
+ if (errorReporter == null) throw new IllegalArgumentException();
+ this.errorReporter = errorReporter;
+ }
+
+ public final int getLanguageVersion()
+ {
+ return languageVersion;
+ }
+
+ public void setLanguageVersion(int languageVersion)
+ {
+ Context.checkLanguageVersion(languageVersion);
+ this.languageVersion = languageVersion;
+ }
+
+ public final boolean isGenerateDebugInfo()
+ {
+ return generateDebugInfo;
+ }
+
+ public void setGenerateDebugInfo(boolean flag)
+ {
+ this.generateDebugInfo = flag;
+ }
+
+ public final boolean isReservedKeywordAsIdentifier()
+ {
+ return reservedKeywordAsIdentifier;
+ }
+
+ public void setReservedKeywordAsIdentifier(boolean flag)
+ {
+ reservedKeywordAsIdentifier = flag;
+ }
+
+ /**
+ * Extension to ECMA: if 'function <name>' is not followed
+ * by '(', assume <name> starts a {@code memberExpr}
+ */
+ public final boolean isAllowMemberExprAsFunctionName()
+ {
+ return allowMemberExprAsFunctionName;
+ }
+
+ public void setAllowMemberExprAsFunctionName(boolean flag)
+ {
+ allowMemberExprAsFunctionName = flag;
+ }
+
+ public final boolean isXmlAvailable()
+ {
+ return xmlAvailable;
+ }
+
+ public void setXmlAvailable(boolean flag)
+ {
+ xmlAvailable = flag;
+ }
+
+ public final int getOptimizationLevel()
+ {
+ return optimizationLevel;
+ }
+
+ public void setOptimizationLevel(int level)
+ {
+ Context.checkOptimizationLevel(level);
+ this.optimizationLevel = level;
+ }
+
+ public final boolean isGeneratingSource()
+ {
+ return generatingSource;
+ }
+
+ public boolean getWarnTrailingComma() {
+ return warnTrailingComma;
+ }
+
+ public void setWarnTrailingComma(boolean warn) {
+ warnTrailingComma = warn;
+ }
+
+ public final boolean isStrictMode()
+ {
+ return strictMode;
+ }
+
+ public void setStrictMode(boolean strict)
+ {
+ strictMode = strict;
+ }
+
+ public final boolean reportWarningAsError()
+ {
+ return warningAsError;
+ }
+
+ /**
+ * Specify whether or not source information should be generated.
+ *
+ * Without source information, evaluating the "toString" method
+ * on JavaScript functions produces only "[native code]" for
+ * the body of the function.
+ * Note that code generated without source is not fully ECMA
+ * conformant.
+ */
+ public void setGeneratingSource(boolean generatingSource)
+ {
+ this.generatingSource = generatingSource;
+ }
+
+ /**
+ * @return true iff code will be generated with callbacks to enable
+ * instruction thresholds
+ */
+ public boolean isGenerateObserverCount() {
+ return generateObserverCount;
+ }
+
+ /**
+ * Turn on or off generation of code with callbacks to
+ * track the count of executed instructions.
+ * Currently only affects JVM byte code generation: this slows down the
+ * generated code, but code generated without the callbacks will not
+ * be counted toward instruction thresholds. Rhino's interpretive
+ * mode does instruction counting without inserting callbacks, so
+ * there is no requirement to compile code differently.
+ * @param generateObserverCount if true, generated code will contain
+ * calls to accumulate an estimate of the instructions executed.
+ */
+ public void setGenerateObserverCount(boolean generateObserverCount) {
+ this.generateObserverCount = generateObserverCount;
+ }
+
+ public boolean isRecordingComments() {
+ return recordingComments;
+ }
+
+ public void setRecordingComments(boolean record) {
+ recordingComments = record;
+ }
+
+ public boolean isRecordingLocalJsDocComments() {
+ return recordingLocalJsDocComments;
+ }
+
+ public void setRecordingLocalJsDocComments(boolean record) {
+ recordingLocalJsDocComments = record;
+ }
+
+ /**
+ * Turn on or off full error recovery. In this mode, parse errors do not
+ * throw an exception, and the parser attempts to build a full syntax tree
+ * from the input. Useful for IDEs and other frontends.
+ */
+ public void setRecoverFromErrors(boolean recover) {
+ recoverFromErrors = recover;
+ }
+
+ public boolean recoverFromErrors() {
+ return recoverFromErrors;
+ }
+
+ /**
+ * Puts the parser in "IDE" mode. This enables some slightly more expensive
+ * computations, such as figuring out helpful error bounds.
+ */
+ public void setIdeMode(boolean ide) {
+ ideMode = ide;
+ }
+
+ public boolean isIdeMode() {
+ return ideMode;
+ }
+
+ public Set getActivationNames() {
+ return activationNames;
+ }
+
+ public void setActivationNames(Set activationNames) {
+ this.activationNames = activationNames;
+ }
+
+ /**
+ * Mozilla sources use the C preprocessor.
+ */
+ public void setAllowSharpComments(boolean allow) {
+ allowSharpComments = allow;
+ }
+
+ public boolean getAllowSharpComments() {
+ return allowSharpComments;
+ }
+
+ /**
+ * Returns a {@code CompilerEnvirons} suitable for using Rhino
+ * in an IDE environment. Most features are enabled by default.
+ * The {@link ErrorReporter} is set to an {@link ErrorCollector}.
+ */
+ public static CompilerEnvirons ideEnvirons() {
+ CompilerEnvirons env = new CompilerEnvirons();
+ env.setRecoverFromErrors(true);
+ env.setRecordingComments(true);
+ env.setStrictMode(true);
+ env.setWarnTrailingComma(true);
+ env.setLanguageVersion(170);
+ env.setReservedKeywordAsIdentifier(true);
+ env.setIdeMode(true);
+ env.setErrorReporter(new ErrorCollector());
+ return env;
+ }
+
+ private ErrorReporter errorReporter;
+
+ private int languageVersion;
+ private boolean generateDebugInfo;
+ private boolean reservedKeywordAsIdentifier;
+ private boolean allowMemberExprAsFunctionName;
+ private boolean xmlAvailable;
+ private int optimizationLevel;
+ private boolean generatingSource;
+ private boolean strictMode;
+ private boolean warningAsError;
+ private boolean generateObserverCount;
+ private boolean recordingComments;
+ private boolean recordingLocalJsDocComments;
+ private boolean recoverFromErrors;
+ private boolean warnTrailingComma;
+ private boolean ideMode;
+ private boolean allowSharpComments;
+ Set activationNames;
+}
diff --git a/src/main/java/org/mozilla/javascript/ConsString.java b/src/main/java/org/mozilla/javascript/ConsString.java
new file mode 100644
index 0000000..aeafc22
--- /dev/null
+++ b/src/main/java/org/mozilla/javascript/ConsString.java
@@ -0,0 +1,103 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.javascript;
+
+import java.io.Serializable;
+
+/**
+ *
This class represents a string composed of two components, each of which
+ * may be a java.lang.String or another ConsString.
+ *
+ *
This string representation is optimized for concatenation using the "+"
+ * operator. Instead of immediately copying both components to a new character
+ * array, ConsString keeps references to the original components and only
+ * converts them to a String if either toString() is called or a certain depth
+ * level is reached.
+ *
+ *
Note that instances of this class are only immutable if both parts are
+ * immutable, i.e. either Strings or ConsStrings that are ultimately composed
+ * of Strings.
+ *
+ *
Both the name and the concept are borrowed from V8.
+ */
+public class ConsString implements CharSequence, Serializable {
+
+ private static final long serialVersionUID = -8432806714471372570L;
+
+ private CharSequence s1, s2;
+ private final int length;
+ private int depth;
+
+ public ConsString(CharSequence str1, CharSequence str2) {
+ s1 = str1;
+ s2 = str2;
+ length = str1.length() + str2.length();
+ depth = 1;
+ if (str1 instanceof ConsString) {
+ depth += ((ConsString)str1).depth;
+ }
+ if (str2 instanceof ConsString) {
+ depth += ((ConsString)str2).depth;
+ }
+ // Don't let it grow too deep, can cause stack overflows
+ if (depth > 2000) {
+ flatten();
+ }
+ }
+
+ // Replace with string representation when serializing
+ private Object writeReplace() {
+ return this.toString();
+ }
+
+ @Override
+ public String toString() {
+ return depth == 0 ? (String)s1 : flatten();
+ }
+
+ private synchronized String flatten() {
+ if (depth > 0) {
+ StringBuilder b = new StringBuilder(length);
+ appendTo(b);
+ s1 = b.toString();
+ s2 = "";
+ depth = 0;
+ }
+ return (String)s1;
+ }
+
+ private synchronized void appendTo(StringBuilder b) {
+ appendFragment(s1, b);
+ appendFragment(s2, b);
+ }
+
+ private static void appendFragment(CharSequence s, StringBuilder b) {
+ if (s instanceof ConsString) {
+ ((ConsString)s).appendTo(b);
+ } else {
+ b.append(s);
+ }
+ }
+
+ @Override
+ public int length() {
+ return length;
+ }
+
+ @Override
+ public char charAt(int index) {
+ String str = depth == 0 ? (String)s1 : flatten();
+ return str.charAt(index);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ String str = depth == 0 ? (String)s1 : flatten();
+ return str.substring(start, end);
+ }
+
+}
diff --git a/src/main/java/org/mozilla/javascript/ConstProperties.java b/src/main/java/org/mozilla/javascript/ConstProperties.java
new file mode 100644
index 0000000..d8d9ebc
--- /dev/null
+++ b/src/main/java/org/mozilla/javascript/ConstProperties.java
@@ -0,0 +1,77 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// API class
+
+package org.mozilla.javascript;
+
+public interface ConstProperties {
+ /**
+ * Sets a named const property in this object.
+ *
+ * The property is specified by a string name
+ * as defined for Scriptable.get.
+ *
+ * The possible values that may be passed in are as defined for
+ * Scriptable.get. A class that implements this method may choose
+ * to ignore calls to set certain properties, in which case those
+ * properties are effectively read-only.
+ * For properties defined in a prototype chain,
+ * use putProperty in ScriptableObject.
+ * Note that if a property a is defined in the prototype p
+ * of an object o, then evaluating o.a = 23 will cause
+ * set to be called on the prototype p with
+ * o as the start parameter.
+ * To preserve JavaScript semantics, it is the Scriptable
+ * object's responsibility to modify o.
+ * This design allows properties to be defined in prototypes and implemented
+ * in terms of getters and setters of Java values without consuming slots
+ * in each instance.
+ *
+ * The values that may be set are limited to the following:
+ *
+ *
java.lang.Boolean objects
+ *
java.lang.String objects
+ *
java.lang.Number objects
+ *
org.mozilla.javascript.Scriptable objects
+ *
null
+ *
The value returned by Context.getUndefinedValue()
+ *
+ * Arbitrary Java objects may be wrapped in a Scriptable by first calling
+ * Context.toObject. This allows the property of a JavaScript
+ * object to contain an arbitrary Java object as a value.
+ * Note that has will be called by the runtime first before
+ * set is called to determine in which object the
+ * property is defined.
+ * Note that this method is not expected to traverse the prototype chain,
+ * which is different from the ECMA [[Put]] operation.
+ * @param name the name of the property
+ * @param start the object whose property is being set
+ * @param value value to set the property to
+ * @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
+ * @see org.mozilla.javascript.Scriptable#get(String, Scriptable)
+ * @see org.mozilla.javascript.ScriptableObject#putProperty(Scriptable, String, Object)
+ * @see org.mozilla.javascript.Context#toObject(Object, Scriptable)
+ */
+ public void putConst(String name, Scriptable start, Object value);
+
+ /**
+ * Reserves a definition spot for a const. This will set up a definition
+ * of the const property, but set its value to undefined. The semantics of
+ * the start parameter is the same as for putConst.
+ * @param name The name of the property.
+ * @param start The object whose property is being reserved.
+ */
+ public void defineConst(String name, Scriptable start);
+
+ /**
+ * Returns true if the named property is defined as a const on this object.
+ * @param name ?
+ * @return true if the named property is defined as a const, false
+ * otherwise.
+ */
+ public boolean isConst(String name);
+}
diff --git a/src/main/java/org/mozilla/javascript/Context.java b/src/main/java/org/mozilla/javascript/Context.java
new file mode 100644
index 0000000..0716d43
--- /dev/null
+++ b/src/main/java/org/mozilla/javascript/Context.java
@@ -0,0 +1,2628 @@
+/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// API class
+
+package org.mozilla.javascript;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Locale;
+
+import org.mozilla.javascript.ast.AstRoot;
+import org.mozilla.javascript.ast.ScriptNode;
+import org.mozilla.javascript.debug.DebuggableScript;
+import org.mozilla.javascript.debug.Debugger;
+import org.mozilla.javascript.xml.XMLLib;
+
+/**
+ * This class represents the runtime context of an executing script.
+ *
+ * Before executing a script, an instance of Context must be created
+ * and associated with the thread that will be executing the script.
+ * The Context will be used to store information about the executing
+ * of the script such as the call stack. Contexts are associated with
+ * the current thread using the {@link #call(ContextAction)}
+ * or {@link #enter()} methods.
+ *
+ * Different forms of script execution are supported. Scripts may be
+ * evaluated from the source directly, or first compiled and then later
+ * executed. Interactive execution is also supported.
+ *
+ * Some aspects of script execution, such as type conversions and
+ * object creation, may be accessed directly through methods of
+ * Context.
+ *
+ * @see Scriptable
+ * @author Norris Boyd
+ * @author Brendan Eich
+ */
+
+@SuppressWarnings({"javadoc","dep-ann"})
+public class Context
+{
+ /**
+ * Language versions.
+ *
+ * All integral values are reserved for future version numbers.
+ */
+
+ /**
+ * The unknown version.
+ */
+ public static final int VERSION_UNKNOWN = -1;
+
+ /**
+ * The default version.
+ */
+ public static final int VERSION_DEFAULT = 0;
+
+ /**
+ * JavaScript 1.0
+ */
+ public static final int VERSION_1_0 = 100;
+
+ /**
+ * JavaScript 1.1
+ */
+ public static final int VERSION_1_1 = 110;
+
+ /**
+ * JavaScript 1.2
+ */
+ public static final int VERSION_1_2 = 120;
+
+ /**
+ * JavaScript 1.3
+ */
+ public static final int VERSION_1_3 = 130;
+
+ /**
+ * JavaScript 1.4
+ */
+ public static final int VERSION_1_4 = 140;
+
+ /**
+ * JavaScript 1.5
+ */
+ public static final int VERSION_1_5 = 150;
+
+ /**
+ * JavaScript 1.6
+ */
+ public static final int VERSION_1_6 = 160;
+
+ /**
+ * JavaScript 1.7
+ */
+ public static final int VERSION_1_7 = 170;
+
+ /**
+ * JavaScript 1.8
+ */
+ public static final int VERSION_1_8 = 180;
+
+ /**
+ * Controls behaviour of Date.prototype.getYear().
+ * If hasFeature(FEATURE_NON_ECMA_GET_YEAR) returns true,
+ * Date.prototype.getYear subtructs 1900 only if 1900 <= date < 2000.
+ * The default behavior of {@link #hasFeature(int)} is always to subtruct
+ * 1900 as rquired by ECMAScript B.2.4.
+ */
+ public static final int FEATURE_NON_ECMA_GET_YEAR = 1;
+
+ /**
+ * Control if member expression as function name extension is available.
+ * If hasFeature(FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME) returns
+ * true, allow function memberExpression(args) { body } to be
+ * syntax sugar for memberExpression = function(args) { body },
+ * when memberExpression is not a simple identifier.
+ * See ECMAScript-262, section 11.2 for definition of memberExpression.
+ * By default {@link #hasFeature(int)} returns false.
+ */
+ public static final int FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME = 2;
+
+ /**
+ * Control if reserved keywords are treated as identifiers.
+ * If hasFeature(RESERVED_KEYWORD_AS_IDENTIFIER) returns true,
+ * treat future reserved keyword (see Ecma-262, section 7.5.3) as ordinary
+ * identifiers but warn about this usage.
+ *
+ * By default {@link #hasFeature(int)} returns false.
+ */
+ public static final int FEATURE_RESERVED_KEYWORD_AS_IDENTIFIER = 3;
+
+ /**
+ * Control if toString() should returns the same result
+ * as toSource() when applied to objects and arrays.
+ * If hasFeature(FEATURE_TO_STRING_AS_SOURCE) returns true,
+ * calling toString() on JS objects gives the same result as
+ * calling toSource(). That is it returns JS source with code
+ * to create an object with all enumeratable fields of the original object
+ * instead of printing [object result of
+ * {@link Scriptable#getClassName()}].
+ *
+ * By default {@link #hasFeature(int)} returns true only if
+ * the current JS version is set to {@link #VERSION_1_2}.
+ */
+ public static final int FEATURE_TO_STRING_AS_SOURCE = 4;
+
+ /**
+ * Control if properties __proto__ and __parent__
+ * are treated specially.
+ * If hasFeature(FEATURE_PARENT_PROTO_PROPERTIES) returns true,
+ * treat __parent__ and __proto__ as special properties.
+ *
+ * The properties allow to query and set scope and prototype chains for the
+ * objects. The special meaning of the properties is available
+ * only when they are used as the right hand side of the dot operator.
+ * For example, while x.__proto__ = y changes the prototype
+ * chain of the object x to point to y,
+ * x["__proto__"] = y simply assigns a new value to the property
+ * __proto__ in x even when the feature is on.
+ *
+ * By default {@link #hasFeature(int)} returns true.
+ */
+ public static final int FEATURE_PARENT_PROTO_PROPERTIES = 5;
+
+ /**
+ * @deprecated In previous releases, this name was given to
+ * FEATURE_PARENT_PROTO_PROPERTIES.
+ */
+ public static final int FEATURE_PARENT_PROTO_PROPRTIES = 5;
+
+ /**
+ * Control if support for E4X(ECMAScript for XML) extension is available.
+ * If hasFeature(FEATURE_E4X) returns true, the XML syntax is available.
+ *
+ * By default {@link #hasFeature(int)} returns true if
+ * the current JS version is set to {@link #VERSION_DEFAULT}
+ * or is at least {@link #VERSION_1_6}.
+ * @since 1.6 Release 1
+ */
+ public static final int FEATURE_E4X = 6;
+
+ /**
+ * Control if dynamic scope should be used for name access.
+ * If hasFeature(FEATURE_DYNAMIC_SCOPE) returns true, then the name lookup
+ * during name resolution will use the top scope of the script or function
+ * which is at the top of JS execution stack instead of the top scope of the
+ * script or function from the current stack frame if the top scope of
+ * the top stack frame contains the top scope of the current stack frame
+ * on its prototype chain.
+ *
+ * This is useful to define shared scope containing functions that can
+ * be called from scripts and functions using private scopes.
+ *
+ * By default {@link #hasFeature(int)} returns false.
+ * @since 1.6 Release 1
+ */
+ public static final int FEATURE_DYNAMIC_SCOPE = 7;
+
+ /**
+ * Control if strict variable mode is enabled.
+ * When the feature is on Rhino reports runtime errors if assignment
+ * to a global variable that does not exist is executed. When the feature
+ * is off such assignments create a new variable in the global scope as
+ * required by ECMA 262.
+ *
+ * By default {@link #hasFeature(int)} returns false.
+ * @since 1.6 Release 1
+ */
+ public static final int FEATURE_STRICT_VARS = 8;
+
+ /**
+ * Control if strict eval mode is enabled.
+ * When the feature is on Rhino reports runtime errors if non-string
+ * argument is passed to the eval function. When the feature is off
+ * eval simply return non-string argument as is without performing any
+ * evaluation as required by ECMA 262.
+ *
+ * By default {@link #hasFeature(int)} returns false.
+ * @since 1.6 Release 1
+ */
+ public static final int FEATURE_STRICT_EVAL = 9;
+
+ /**
+ * When the feature is on Rhino will add a "fileName" and "lineNumber"
+ * properties to Error objects automatically. When the feature is off, you
+ * have to explicitly pass them as the second and third argument to the
+ * Error constructor. Note that neither behavior is fully ECMA 262
+ * compliant (as 262 doesn't specify a three-arg constructor), but keeping
+ * the feature off results in Error objects that don't have
+ * additional non-ECMA properties when constructed using the ECMA-defined
+ * single-arg constructor and is thus desirable if a stricter ECMA
+ * compliance is desired, specifically adherence to the point 15.11.5. of
+ * the standard.
+ *
+ * By default {@link #hasFeature(int)} returns false.
+ * @since 1.6 Release 6
+ */
+ public static final int FEATURE_LOCATION_INFORMATION_IN_ERROR = 10;
+
+ /**
+ * Controls whether JS 1.5 'strict mode' is enabled.
+ * When the feature is on, Rhino reports more than a dozen different
+ * warnings. When the feature is off, these warnings are not generated.
+ * FEATURE_STRICT_MODE implies FEATURE_STRICT_VARS and FEATURE_STRICT_EVAL.
+ *
+ * By default {@link #hasFeature(int)} returns false.
+ * @since 1.6 Release 6
+ */
+ public static final int FEATURE_STRICT_MODE = 11;
+
+ /**
+ * Controls whether a warning should be treated as an error.
+ * @since 1.6 Release 6
+ */
+ public static final int FEATURE_WARNING_AS_ERROR = 12;
+
+ /**
+ * Enables enhanced access to Java.
+ * Specifically, controls whether private and protected members can be
+ * accessed, and whether scripts can catch all Java exceptions.
+ *
+ * Note that this feature should only be enabled for trusted scripts.
+ *
+ * By default {@link #hasFeature(int)} returns false.
+ * @since 1.7 Release 1
+ */
+ public static final int FEATURE_ENHANCED_JAVA_ACCESS = 13;
+
+ public static final String languageVersionProperty = "language version";
+ public static final String errorReporterProperty = "error reporter";
+
+ /**
+ * Convenient value to use as zero-length array of objects.
+ */
+ public static final Object[] emptyArgs = ScriptRuntime.emptyArgs;
+
+ /**
+ * Creates a new Context. The context will be associated with the {@link
+ * ContextFactory#getGlobal() global context factory}.
+ *
+ * Note that the Context must be associated with a thread before
+ * it can be used to execute a script.
+ * @deprecated this constructor is deprecated because it creates a
+ * dependency on a static singleton context factory. Use
+ * {@link ContextFactory#enter()} or
+ * {@link ContextFactory#call(ContextAction)} instead. If you subclass
+ * this class, consider using {@link #Context(ContextFactory)} constructor
+ * instead in the subclasses' constructors.
+ */
+ public Context()
+ {
+ this(ContextFactory.getGlobal());
+ }
+
+ /**
+ * Creates a new context. Provided as a preferred super constructor for
+ * subclasses in place of the deprecated default public constructor.
+ * @param factory the context factory associated with this context (most
+ * likely, the one that created the context). Can not be null. The context
+ * features are inherited from the factory, and the context will also
+ * otherwise use its factory's services.
+ * @throws IllegalArgumentException if factory parameter is null.
+ */
+ protected Context(ContextFactory factory)
+ {
+ if(factory == null) {
+ throw new IllegalArgumentException("factory == null");
+ }
+ this.factory = factory;
+ version = VERSION_DEFAULT;
+ optimizationLevel = codegenClass != null ? 0 : -1;
+ maximumInterpreterStackDepth = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Get the current Context.
+ *
+ * The current Context is per-thread; this method looks up
+ * the Context associated with the current thread.
+ *
+ * @return the Context associated with the current thread, or
+ * null if no context is associated with the current
+ * thread.
+ * @see ContextFactory#enterContext()
+ * @see ContextFactory#call(ContextAction)
+ */
+ public static Context getCurrentContext()
+ {
+ Object helper = VMBridge.instance.getThreadContextHelper();
+ return VMBridge.instance.getContext(helper);
+ }
+
+ /**
+ * Same as calling {@link ContextFactory#enterContext()} on the global
+ * ContextFactory instance.
+ * @return a Context associated with the current thread
+ * @see #getCurrentContext()
+ * @see #exit()
+ * @see #call(ContextAction)
+ */
+ public static Context enter()
+ {
+ return enter(null);
+ }
+
+ /**
+ * Get a Context associated with the current thread, using
+ * the given Context if need be.
+ *
+ * The same as enter() except that cx
+ * is associated with the current thread and returned if
+ * the current thread has no associated context and cx
+ * is not associated with any other thread.
+ * @param cx a Context to associate with the thread if possible
+ * @return a Context associated with the current thread
+ * @deprecated use {@link ContextFactory#enterContext(Context)} instead as
+ * this method relies on usage of a static singleton "global" ContextFactory.
+ * @see ContextFactory#enterContext(Context)
+ * @see ContextFactory#call(ContextAction)
+ */
+ public static Context enter(Context cx)
+ {
+ return enter(cx, ContextFactory.getGlobal());
+ }
+
+ static final Context enter(Context cx, ContextFactory factory)
+ {
+ Object helper = VMBridge.instance.getThreadContextHelper();
+ Context old = VMBridge.instance.getContext(helper);
+ if (old != null) {
+ cx = old;
+ } else {
+ if (cx == null) {
+ cx = factory.makeContext();
+ if (cx.enterCount != 0) {
+ throw new IllegalStateException("factory.makeContext() returned Context instance already associated with some thread");
+ }
+ factory.onContextCreated(cx);
+ if (factory.isSealed() && !cx.isSealed()) {
+ cx.seal(null);
+ }
+ } else {
+ if (cx.enterCount != 0) {
+ throw new IllegalStateException("can not use Context instance already associated with some thread");
+ }
+ }
+ VMBridge.instance.setContext(helper, cx);
+ }
+ ++cx.enterCount;
+ return cx;
+ }
+
+ /**
+ * Exit a block of code requiring a Context.
+ *
+ * Calling exit() will remove the association between
+ * the current thread and a Context if the prior call to
+ * {@link ContextFactory#enterContext()} on this thread newly associated a
+ * Context with this thread. Once the current thread no longer has an
+ * associated Context, it cannot be used to execute JavaScript until it is
+ * again associated with a Context.
+ * @see ContextFactory#enterContext()
+ */
+ public static void exit()
+ {
+ Object helper = VMBridge.instance.getThreadContextHelper();
+ Context cx = VMBridge.instance.getContext(helper);
+ if (cx == null) {
+ throw new IllegalStateException(
+ "Calling Context.exit without previous Context.enter");
+ }
+ if (cx.enterCount < 1) Kit.codeBug();
+ if (--cx.enterCount == 0) {
+ VMBridge.instance.setContext(helper, null);
+ cx.factory.onContextReleased(cx);
+ }
+ }
+
+ /**
+ * Call {@link ContextAction#run(Context cx)}
+ * using the Context instance associated with the current thread.
+ * If no Context is associated with the thread, then
+ * ContextFactory.getGlobal().makeContext() will be called to
+ * construct new Context instance. The instance will be temporary
+ * associated with the thread during call to
+ * {@link ContextAction#run(Context)}.
+ * @deprecated use {@link ContextFactory#call(ContextAction)} instead as
+ * this method relies on usage of a static singleton "global"
+ * ContextFactory.
+ * @return The result of {@link ContextAction#run(Context)}.
+ */
+ public static Object call(ContextAction action)
+ {
+ return call(ContextFactory.getGlobal(), action);
+ }
+
+ /**
+ * Call {@link
+ * Callable#call(Context cx, Scriptable scope, Scriptable thisObj,
+ * Object[] args)}
+ * using the Context instance associated with the current thread.
+ * If no Context is associated with the thread, then
+ * {@link ContextFactory#makeContext()} will be called to construct
+ * new Context instance. The instance will be temporary associated
+ * with the thread during call to {@link ContextAction#run(Context)}.
+ *
+ * It is allowed but not advisable to use null for factory
+ * argument in which case the global static singleton ContextFactory
+ * instance will be used to create new context instances.
+ * @see ContextFactory#call(ContextAction)
+ */
+ public static Object call(ContextFactory factory, final Callable callable,
+ final Scriptable scope, final Scriptable thisObj,
+ final Object[] args)
+ {
+ if(factory == null) {
+ factory = ContextFactory.getGlobal();
+ }
+ return call(factory, new ContextAction() {
+ @Override
+ public Object run(Context cx) {
+ return callable.call(cx, scope, thisObj, args);
+ }
+ });
+ }
+
+ /**
+ * The method implements {@link ContextFactory#call(ContextAction)} logic.
+ */
+ static Object call(ContextFactory factory, ContextAction action) {
+ Context cx = enter(null, factory);
+ try {
+ return action.run(cx);
+ }
+ finally {
+ exit();
+ }
+ }
+
+ /**
+ * @deprecated
+ * @see ContextFactory#addListener(org.mozilla.javascript.ContextFactory.Listener)
+ * @see ContextFactory#getGlobal()
+ */
+ public static void addContextListener(ContextListener listener)
+ {
+ // Special workaround for the debugger
+ String DBG = "org.mozilla.javascript.tools.debugger.Main";
+ if (DBG.equals(listener.getClass().getName())) {
+ Class> cl = listener.getClass();
+ Class> factoryClass = Kit.classOrNull(
+ "org.mozilla.javascript.ContextFactory");
+ Class>[] sig = { factoryClass };
+ Object[] args = { ContextFactory.getGlobal() };
+ try {
+ Method m = cl.getMethod("attachTo", sig);
+ m.invoke(listener, args);
+ } catch (Exception ex) {
+ RuntimeException rex = new RuntimeException();
+ Kit.initCause(rex, ex);
+ throw rex;
+ }
+ return;
+ }
+
+ ContextFactory.getGlobal().addListener(listener);
+ }
+
+ /**
+ * @deprecated
+ * @see ContextFactory#removeListener(org.mozilla.javascript.ContextFactory.Listener)
+ * @see ContextFactory#getGlobal()
+ */
+ public static void removeContextListener(ContextListener listener)
+ {
+ ContextFactory.getGlobal().addListener(listener);
+ }
+
+ /**
+ * Return {@link ContextFactory} instance used to create this Context.
+ */
+ public final ContextFactory getFactory()
+ {
+ return factory;
+ }
+
+ /**
+ * Checks if this is a sealed Context. A sealed Context instance does not
+ * allow to modify any of its properties and will throw an exception
+ * on any such attempt.
+ * @see #seal(Object sealKey)
+ */
+ public final boolean isSealed()
+ {
+ return sealed;
+ }
+
+ /**
+ * Seal this Context object so any attempt to modify any of its properties
+ * including calling {@link #enter()} and {@link #exit()} methods will
+ * throw an exception.
+ *
+ * If sealKey is not null, calling
+ * {@link #unseal(Object sealKey)} with the same key unseals
+ * the object. If sealKey is null, unsealing is no longer possible.
+ *
+ * @see #isSealed()
+ * @see #unseal(Object)
+ */
+ public final void seal(Object sealKey)
+ {
+ if (sealed) onSealedMutation();
+ sealed = true;
+ this.sealKey = sealKey;
+ }
+
+ /**
+ * Unseal previously sealed Context object.
+ * The sealKey argument should not be null and should match
+ * sealKey suplied with the last call to
+ * {@link #seal(Object)} or an exception will be thrown.
+ *
+ * @see #isSealed()
+ * @see #seal(Object sealKey)
+ */
+ public final void unseal(Object sealKey)
+ {
+ if (sealKey == null) throw new IllegalArgumentException();
+ if (this.sealKey != sealKey) throw new IllegalArgumentException();
+ if (!sealed) throw new IllegalStateException();
+ sealed = false;
+ this.sealKey = null;
+ }
+
+ static void onSealedMutation()
+ {
+ throw new IllegalStateException();
+ }
+
+ /**
+ * Get the current language version.
+ *
+ * The language version number affects JavaScript semantics as detailed
+ * in the overview documentation.
+ *
+ * @return an integer that is one of VERSION_1_0, VERSION_1_1, etc.
+ */
+ public final int getLanguageVersion()
+ {
+ return version;
+ }
+
+ /**
+ * Set the language version.
+ *
+ *
+ * Setting the language version will affect functions and scripts compiled
+ * subsequently. See the overview documentation for version-specific
+ * behavior.
+ *
+ * @param version the version as specified by VERSION_1_0, VERSION_1_1, etc.
+ */
+ public void setLanguageVersion(int version)
+ {
+ if (sealed) onSealedMutation();
+ checkLanguageVersion(version);
+ Object listeners = propertyListeners;
+ if (listeners != null && version != this.version) {
+ firePropertyChangeImpl(listeners, languageVersionProperty,
+ Integer.valueOf(this.version),
+ Integer.valueOf(version));
+ }
+ this.version = version;
+ }
+
+ public static boolean isValidLanguageVersion(int version)
+ {
+ switch (version) {
+ case VERSION_DEFAULT:
+ case VERSION_1_0:
+ case VERSION_1_1:
+ case VERSION_1_2:
+ case VERSION_1_3:
+ case VERSION_1_4:
+ case VERSION_1_5:
+ case VERSION_1_6:
+ case VERSION_1_7:
+ case VERSION_1_8:
+ return true;
+ }
+ return false;
+ }
+
+ public static void checkLanguageVersion(int version)
+ {
+ if (isValidLanguageVersion(version)) {
+ return;
+ }
+ throw new IllegalArgumentException("Bad language version: "+version);
+ }
+
+ /**
+ * Get the implementation version.
+ *
+ *
+ * The implementation version is of the form
+ *
+ * "name langVerreleaserelNum date"
+ *
+ * where name is the name of the product, langVer is
+ * the language version, relNum is the release number, and
+ * date is the release date for that specific
+ * release in the form "yyyy mm dd".
+ *
+ * @return a string that encodes the product, language version, release
+ * number, and date.
+ */
+ public final String getImplementationVersion()
+ {
+ // XXX Probably it would be better to embed this directly into source
+ // with special build preprocessing but that would require some ant
+ // tweaking and then replacing token in resource files was simpler
+ if (implementationVersion == null) {
+ implementationVersion
+ = ScriptRuntime.getMessage0("implementation.version");
+ }
+ return implementationVersion;
+ }
+
+ /**
+ * Get the current error reporter.
+ *
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public final ErrorReporter getErrorReporter()
+ {
+ if (errorReporter == null) {
+ return DefaultErrorReporter.instance;
+ }
+ return errorReporter;
+ }
+
+ /**
+ * Change the current error reporter.
+ *
+ * @return the previous error reporter
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public final ErrorReporter setErrorReporter(ErrorReporter reporter)
+ {
+ if (sealed) onSealedMutation();
+ if (reporter == null) throw new IllegalArgumentException();
+ ErrorReporter old = getErrorReporter();
+ if (reporter == old) {
+ return old;
+ }
+ Object listeners = propertyListeners;
+ if (listeners != null) {
+ firePropertyChangeImpl(listeners, errorReporterProperty,
+ old, reporter);
+ }
+ this.errorReporter = reporter;
+ return old;
+ }
+
+ /**
+ * Get the current locale. Returns the default locale if none has
+ * been set.
+ *
+ * @see java.util.Locale
+ */
+
+ public final Locale getLocale()
+ {
+ if (locale == null)
+ locale = Locale.getDefault();
+ return locale;
+ }
+
+ /**
+ * Set the current locale.
+ *
+ * @see java.util.Locale
+ */
+ public final Locale setLocale(Locale loc)
+ {
+ if (sealed) onSealedMutation();
+ Locale result = locale;
+ locale = loc;
+ return result;
+ }
+
+ /**
+ * Register an object to receive notifications when a bound property
+ * has changed
+ * @see java.beans.PropertyChangeEvent
+ * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
+ * @param l the listener
+ */
+ public final void addPropertyChangeListener(PropertyChangeListener l)
+ {
+ if (sealed) onSealedMutation();
+ propertyListeners = Kit.addListener(propertyListeners, l);
+ }
+
+ /**
+ * Remove an object from the list of objects registered to receive
+ * notification of changes to a bounded property
+ * @see java.beans.PropertyChangeEvent
+ * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
+ * @param l the listener
+ */
+ public final void removePropertyChangeListener(PropertyChangeListener l)
+ {
+ if (sealed) onSealedMutation();
+ propertyListeners = Kit.removeListener(propertyListeners, l);
+ }
+
+ /**
+ * Notify any registered listeners that a bounded property has changed
+ * @see #addPropertyChangeListener(java.beans.PropertyChangeListener)
+ * @see #removePropertyChangeListener(java.beans.PropertyChangeListener)
+ * @see java.beans.PropertyChangeListener
+ * @see java.beans.PropertyChangeEvent
+ * @param property the bound property
+ * @param oldValue the old value
+ * @param newValue the new value
+ */
+ final void firePropertyChange(String property, Object oldValue,
+ Object newValue)
+ {
+ Object listeners = propertyListeners;
+ if (listeners != null) {
+ firePropertyChangeImpl(listeners, property, oldValue, newValue);
+ }
+ }
+
+ private void firePropertyChangeImpl(Object listeners, String property,
+ Object oldValue, Object newValue)
+ {
+ for (int i = 0; ; ++i) {
+ Object l = Kit.getListener(listeners, i);
+ if (l == null)
+ break;
+ if (l instanceof PropertyChangeListener) {
+ PropertyChangeListener pcl = (PropertyChangeListener)l;
+ pcl.propertyChange(new PropertyChangeEvent(
+ this, property, oldValue, newValue));
+ }
+ }
+ }
+
+ /**
+ * Report a warning using the error reporter for the current thread.
+ *
+ * @param message the warning message to report
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number
+ * @param lineSource the text of the line (may be null)
+ * @param lineOffset the offset into lineSource where problem was detected
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public static void reportWarning(String message, String sourceName,
+ int lineno, String lineSource,
+ int lineOffset)
+ {
+ Context cx = Context.getContext();
+ if (cx.hasFeature(FEATURE_WARNING_AS_ERROR))
+ reportError(message, sourceName, lineno, lineSource, lineOffset);
+ else
+ cx.getErrorReporter().warning(message, sourceName, lineno,
+ lineSource, lineOffset);
+ }
+
+ /**
+ * Report a warning using the error reporter for the current thread.
+ *
+ * @param message the warning message to report
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public static void reportWarning(String message)
+ {
+ int[] linep = { 0 };
+ String filename = getSourcePositionFromStack(linep);
+ Context.reportWarning(message, filename, linep[0], null, 0);
+ }
+
+ public static void reportWarning(String message, Throwable t)
+ {
+ int[] linep = { 0 };
+ String filename = getSourcePositionFromStack(linep);
+ Writer sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ pw.println(message);
+ t.printStackTrace(pw);
+ pw.flush();
+ Context.reportWarning(sw.toString(), filename, linep[0], null, 0);
+ }
+
+ /**
+ * Report an error using the error reporter for the current thread.
+ *
+ * @param message the error message to report
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number
+ * @param lineSource the text of the line (may be null)
+ * @param lineOffset the offset into lineSource where problem was detected
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public static void reportError(String message, String sourceName,
+ int lineno, String lineSource,
+ int lineOffset)
+ {
+ Context cx = getCurrentContext();
+ if (cx != null) {
+ cx.getErrorReporter().error(message, sourceName, lineno,
+ lineSource, lineOffset);
+ } else {
+ throw new EvaluatorException(message, sourceName, lineno,
+ lineSource, lineOffset);
+ }
+ }
+
+ /**
+ * Report an error using the error reporter for the current thread.
+ *
+ * @param message the error message to report
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public static void reportError(String message)
+ {
+ int[] linep = { 0 };
+ String filename = getSourcePositionFromStack(linep);
+ Context.reportError(message, filename, linep[0], null, 0);
+ }
+
+ /**
+ * Report a runtime error using the error reporter for the current thread.
+ *
+ * @param message the error message to report
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number
+ * @param lineSource the text of the line (may be null)
+ * @param lineOffset the offset into lineSource where problem was detected
+ * @return a runtime exception that will be thrown to terminate the
+ * execution of the script
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public static EvaluatorException reportRuntimeError(String message,
+ String sourceName,
+ int lineno,
+ String lineSource,
+ int lineOffset)
+ {
+ Context cx = getCurrentContext();
+ if (cx != null) {
+ return cx.getErrorReporter().
+ runtimeError(message, sourceName, lineno,
+ lineSource, lineOffset);
+ } else {
+ throw new EvaluatorException(message, sourceName, lineno,
+ lineSource, lineOffset);
+ }
+ }
+
+ static EvaluatorException reportRuntimeError0(String messageId)
+ {
+ String msg = ScriptRuntime.getMessage0(messageId);
+ return reportRuntimeError(msg);
+ }
+
+ static EvaluatorException reportRuntimeError1(String messageId,
+ Object arg1)
+ {
+ String msg = ScriptRuntime.getMessage1(messageId, arg1);
+ return reportRuntimeError(msg);
+ }
+
+ static EvaluatorException reportRuntimeError2(String messageId,
+ Object arg1, Object arg2)
+ {
+ String msg = ScriptRuntime.getMessage2(messageId, arg1, arg2);
+ return reportRuntimeError(msg);
+ }
+
+ static EvaluatorException reportRuntimeError3(String messageId,
+ Object arg1, Object arg2,
+ Object arg3)
+ {
+ String msg = ScriptRuntime.getMessage3(messageId, arg1, arg2, arg3);
+ return reportRuntimeError(msg);
+ }
+
+ static EvaluatorException reportRuntimeError4(String messageId,
+ Object arg1, Object arg2,
+ Object arg3, Object arg4)
+ {
+ String msg
+ = ScriptRuntime.getMessage4(messageId, arg1, arg2, arg3, arg4);
+ return reportRuntimeError(msg);
+ }
+
+ /**
+ * Report a runtime error using the error reporter for the current thread.
+ *
+ * @param message the error message to report
+ * @see org.mozilla.javascript.ErrorReporter
+ */
+ public static EvaluatorException reportRuntimeError(String message)
+ {
+ int[] linep = { 0 };
+ String filename = getSourcePositionFromStack(linep);
+ return Context.reportRuntimeError(message, filename, linep[0], null, 0);
+ }
+
+ /**
+ * Initialize the standard objects.
+ *
+ * Creates instances of the standard objects and their constructors
+ * (Object, String, Number, Date, etc.), setting up 'scope' to act
+ * as a global object as in ECMA 15.1.
+ *
+ * This method must be called to initialize a scope before scripts
+ * can be evaluated in that scope.
+ *
+ * This method does not affect the Context it is called upon.
+ *
+ * @return the initialized scope
+ */
+ public final ScriptableObject initStandardObjects()
+ {
+ return initStandardObjects(null, false);
+ }
+
+ /**
+ * Initialize the standard objects.
+ *
+ * Creates instances of the standard objects and their constructors
+ * (Object, String, Number, Date, etc.), setting up 'scope' to act
+ * as a global object as in ECMA 15.1.
+ *
+ * This method must be called to initialize a scope before scripts
+ * can be evaluated in that scope.
+ *
+ * This method does not affect the Context it is called upon.
+ *
+ * @param scope the scope to initialize, or null, in which case a new
+ * object will be created to serve as the scope
+ * @return the initialized scope. The method returns the value of the scope
+ * argument if it is not null or newly allocated scope object which
+ * is an instance {@link ScriptableObject}.
+ */
+ public final Scriptable initStandardObjects(ScriptableObject scope)
+ {
+ return initStandardObjects(scope, false);
+ }
+
+ /**
+ * Initialize the standard objects.
+ *
+ * Creates instances of the standard objects and their constructors
+ * (Object, String, Number, Date, etc.), setting up 'scope' to act
+ * as a global object as in ECMA 15.1.
+ *
+ * This method must be called to initialize a scope before scripts
+ * can be evaluated in that scope.
+ *
+ * This method does not affect the Context it is called upon.
+ *
+ * This form of the method also allows for creating "sealed" standard
+ * objects. An object that is sealed cannot have properties added, changed,
+ * or removed. This is useful to create a "superglobal" that can be shared
+ * among several top-level objects. Note that sealing is not allowed in
+ * the current ECMA/ISO language specification, but is likely for
+ * the next version.
+ *
+ * @param scope the scope to initialize, or null, in which case a new
+ * object will be created to serve as the scope
+ * @param sealed whether or not to create sealed standard objects that
+ * cannot be modified.
+ * @return the initialized scope. The method returns the value of the scope
+ * argument if it is not null or newly allocated scope object.
+ * @since 1.4R3
+ */
+ public ScriptableObject initStandardObjects(ScriptableObject scope,
+ boolean sealed)
+ {
+ return ScriptRuntime.initStandardObjects(this, scope, sealed);
+ }
+
+ /**
+ * Get the singleton object that represents the JavaScript Undefined value.
+ */
+ public static Object getUndefinedValue()
+ {
+ return Undefined.instance;
+ }
+
+ /**
+ * Evaluate a JavaScript source string.
+ *
+ * The provided source name and line number are used for error messages
+ * and for producing debug information.
+ *
+ * @param scope the scope to execute in
+ * @param source the JavaScript source
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number
+ * @param securityDomain an arbitrary object that specifies security
+ * information about the origin or owner of the script. For
+ * implementations that don't care about security, this value
+ * may be null.
+ * @return the result of evaluating the string
+ * @see org.mozilla.javascript.SecurityController
+ */
+ public final Object evaluateString(Scriptable scope, String source,
+ String sourceName, int lineno,
+ Object securityDomain)
+ {
+ Script script = compileString(source, sourceName, lineno,
+ securityDomain);
+ if (script != null) {
+ return script.exec(this, scope);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Evaluate a reader as JavaScript source.
+ *
+ * All characters of the reader are consumed.
+ *
+ * @param scope the scope to execute in
+ * @param in the Reader to get JavaScript source from
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number
+ * @param securityDomain an arbitrary object that specifies security
+ * information about the origin or owner of the script. For
+ * implementations that don't care about security, this value
+ * may be null.
+ * @return the result of evaluating the source
+ *
+ * @exception IOException if an IOException was generated by the Reader
+ */
+ public final Object evaluateReader(Scriptable scope, Reader in,
+ String sourceName, int lineno,
+ Object securityDomain)
+ throws IOException
+ {
+ Script script = compileReader(scope, in, sourceName, lineno,
+ securityDomain);
+ if (script != null) {
+ return script.exec(this, scope);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Execute script that may pause execution by capturing a continuation.
+ * Caller must be prepared to catch a ContinuationPending exception
+ * and resume execution by calling
+ * {@link #resumeContinuation(Object, Scriptable, Object)}.
+ * @param script The script to execute. Script must have been compiled
+ * with interpreted mode (optimization level -1)
+ * @param scope The scope to execute the script against
+ * @throws ContinuationPending if the script calls a function that results
+ * in a call to {@link #captureContinuation()}
+ * @since 1.7 Release 2
+ */
+ public Object executeScriptWithContinuations(Script script,
+ Scriptable scope)
+ throws ContinuationPending
+ {
+ if (!(script instanceof InterpretedFunction) ||
+ !((InterpretedFunction)script).isScript())
+ {
+ // Can only be applied to scripts
+ throw new IllegalArgumentException("Script argument was not" +
+ " a script or was not created by interpreted mode ");
+ }
+ return callFunctionWithContinuations((InterpretedFunction) script,
+ scope, ScriptRuntime.emptyArgs);
+ }
+
+ /**
+ * Call function that may pause execution by capturing a continuation.
+ * Caller must be prepared to catch a ContinuationPending exception
+ * and resume execution by calling
+ * {@link #resumeContinuation(Object, Scriptable, Object)}.
+ * @param function The function to call. The function must have been
+ * compiled with interpreted mode (optimization level -1)
+ * @param scope The scope to execute the script against
+ * @param args The arguments for the function
+ * @throws ContinuationPending if the script calls a function that results
+ * in a call to {@link #captureContinuation()}
+ * @since 1.7 Release 2
+ */
+ public Object callFunctionWithContinuations(Callable function,
+ Scriptable scope, Object[] args)
+ throws ContinuationPending
+ {
+ if (!(function instanceof InterpretedFunction)) {
+ // Can only be applied to scripts
+ throw new IllegalArgumentException("Function argument was not" +
+ " created by interpreted mode ");
+ }
+ if (ScriptRuntime.hasTopCall(this)) {
+ throw new IllegalStateException("Cannot have any pending top " +
+ "calls when executing a script with continuations");
+ }
+ // Annotate so we can check later to ensure no java code in
+ // intervening frames
+ isContinuationsTopCall = true;
+ return ScriptRuntime.doTopCall(function, this, scope, scope, args);
+ }
+
+ /**
+ * Capture a continuation from the current execution. The execution must
+ * have been started via a call to
+ * {@link #executeScriptWithContinuations(Script, Scriptable)} or
+ * {@link #callFunctionWithContinuations(Callable, Scriptable, Object[])}.
+ * This implies that the code calling
+ * this method must have been called as a function from the
+ * JavaScript script. Also, there cannot be any non-JavaScript code
+ * between the JavaScript frames (e.g., a call to eval()). The
+ * ContinuationPending exception returned must be thrown.
+ * @return A ContinuationPending exception that must be thrown
+ * @since 1.7 Release 2
+ */
+ public ContinuationPending captureContinuation() {
+ return new ContinuationPending(
+ Interpreter.captureContinuation(this));
+ }
+
+ /**
+ * Restarts execution of the JavaScript suspended at the call
+ * to {@link #captureContinuation()}. Execution of the code will resume
+ * with the functionResult as the result of the call that captured the
+ * continuation.
+ * Execution of the script will either conclude normally and the
+ * result returned, another continuation will be captured and
+ * thrown, or the script will terminate abnormally and throw an exception.
+ * @param continuation The value returned by
+ * {@link ContinuationPending#getContinuation()}
+ * @param functionResult This value will appear to the code being resumed
+ * as the result of the function that captured the continuation
+ * @throws ContinuationPending if another continuation is captured before
+ * the code terminates
+ * @since 1.7 Release 2
+ */
+ public Object resumeContinuation(Object continuation,
+ Scriptable scope, Object functionResult)
+ throws ContinuationPending
+ {
+ Object[] args = { functionResult };
+ return Interpreter.restartContinuation(
+ (org.mozilla.javascript.NativeContinuation) continuation,
+ this, scope, args);
+ }
+
+ /**
+ * Check whether a string is ready to be compiled.
+ *
+ * stringIsCompilableUnit is intended to support interactive compilation of
+ * JavaScript. If compiling the string would result in an error
+ * that might be fixed by appending more source, this method
+ * returns false. In every other case, it returns true.
+ *
+ * Interactive shells may accumulate source lines, using this
+ * method after each new line is appended to check whether the
+ * statement being entered is complete.
+ *
+ * @param source the source buffer to check
+ * @return whether the source is ready for compilation
+ * @since 1.4 Release 2
+ */
+ public final boolean stringIsCompilableUnit(String source)
+ {
+ boolean errorseen = false;
+ CompilerEnvirons compilerEnv = new CompilerEnvirons();
+ compilerEnv.initFromContext(this);
+ // no source name or source text manager, because we're just
+ // going to throw away the result.
+ compilerEnv.setGeneratingSource(false);
+ Parser p = new Parser(compilerEnv, DefaultErrorReporter.instance);
+ try {
+ p.parse(source, null, 1);
+ } catch (EvaluatorException ee) {
+ errorseen = true;
+ }
+ // Return false only if an error occurred as a result of reading past
+ // the end of the file, i.e. if the source could be fixed by
+ // appending more source.
+ if (errorseen && p.eof())
+ return false;
+ else
+ return true;
+ }
+
+ /**
+ * @deprecated
+ * @see #compileReader(Reader in, String sourceName, int lineno,
+ * Object securityDomain)
+ */
+ public final Script compileReader(Scriptable scope, Reader in,
+ String sourceName, int lineno,
+ Object securityDomain)
+ throws IOException
+ {
+ return compileReader(in, sourceName, lineno, securityDomain);
+ }
+
+ /**
+ * Compiles the source in the given reader.
+ *
+ * Returns a script that may later be executed.
+ * Will consume all the source in the reader.
+ *
+ * @param in the input reader
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number for reporting errors
+ * @param securityDomain an arbitrary object that specifies security
+ * information about the origin or owner of the script. For
+ * implementations that don't care about security, this value
+ * may be null.
+ * @return a script that may later be executed
+ * @exception IOException if an IOException was generated by the Reader
+ * @see org.mozilla.javascript.Script
+ */
+ public final Script compileReader(Reader in, String sourceName,
+ int lineno, Object securityDomain)
+ throws IOException
+ {
+ if (lineno < 0) {
+ // For compatibility IllegalArgumentException can not be thrown here
+ lineno = 0;
+ }
+ return (Script) compileImpl(null, in, null, sourceName, lineno,
+ securityDomain, false, null, null);
+ }
+
+ /**
+ * Compiles the source in the given string.
+ *
+ * Returns a script that may later be executed.
+ *
+ * @param source the source string
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number for reporting errors. Use
+ * 0 if the line number is unknown.
+ * @param securityDomain an arbitrary object that specifies security
+ * information about the origin or owner of the script. For
+ * implementations that don't care about security, this value
+ * may be null.
+ * @return a script that may later be executed
+ * @see org.mozilla.javascript.Script
+ */
+ public final Script compileString(String source,
+ String sourceName, int lineno,
+ Object securityDomain)
+ {
+ if (lineno < 0) {
+ // For compatibility IllegalArgumentException can not be thrown here
+ lineno = 0;
+ }
+ return compileString(source, null, null, sourceName, lineno,
+ securityDomain);
+ }
+
+ final Script compileString(String source,
+ Evaluator compiler,
+ ErrorReporter compilationErrorReporter,
+ String sourceName, int lineno,
+ Object securityDomain)
+ {
+ try {
+ return (Script) compileImpl(null, null, source, sourceName, lineno,
+ securityDomain, false,
+ compiler, compilationErrorReporter);
+ } catch (IOException ex) {
+ // Should not happen when dealing with source as string
+ throw new RuntimeException();
+ }
+ }
+
+ /**
+ * Compile a JavaScript function.
+ *
+ * The function source must be a function definition as defined by
+ * ECMA (e.g., "function f(a) { return a; }").
+ *
+ * @param scope the scope to compile relative to
+ * @param source the function definition source
+ * @param sourceName a string describing the source, such as a filename
+ * @param lineno the starting line number
+ * @param securityDomain an arbitrary object that specifies security
+ * information about the origin or owner of the script. For
+ * implementations that don't care about security, this value
+ * may be null.
+ * @return a Function that may later be called
+ * @see org.mozilla.javascript.Function
+ */
+ public final Function compileFunction(Scriptable scope, String source,
+ String sourceName, int lineno,
+ Object securityDomain)
+ {
+ return compileFunction(scope, source, null, null, sourceName, lineno,
+ securityDomain);
+ }
+
+ final Function compileFunction(Scriptable scope, String source,
+ Evaluator compiler,
+ ErrorReporter compilationErrorReporter,
+ String sourceName, int lineno,
+ Object securityDomain)
+ {
+ try {
+ return (Function) compileImpl(scope, null, source, sourceName,
+ lineno, securityDomain, true,
+ compiler, compilationErrorReporter);
+ }
+ catch (IOException ioe) {
+ // Should never happen because we just made the reader
+ // from a String
+ throw new RuntimeException();
+ }
+ }
+
+ /**
+ * Decompile the script.
+ *
+ * The canonical source of the script is returned.
+ *
+ * @param script the script to decompile
+ * @param indent the number of spaces to indent the result
+ * @return a string representing the script source
+ */
+ public final String decompileScript(Script script, int indent)
+ {
+ NativeFunction scriptImpl = (NativeFunction) script;
+ return scriptImpl.decompile(indent, 0);
+ }
+
+ /**
+ * Decompile a JavaScript Function.
+ *
+ * Decompiles a previously compiled JavaScript function object to
+ * canonical source.
+ *
+ * Returns function body of '[native code]' if no decompilation
+ * information is available.
+ *
+ * @param fun the JavaScript function to decompile
+ * @param indent the number of spaces to indent the result
+ * @return a string representing the function source
+ */
+ public final String decompileFunction(Function fun, int indent)
+ {
+ if (fun instanceof BaseFunction)
+ return ((BaseFunction)fun).decompile(indent, 0);
+ else
+ return "function " + fun.getClassName() +
+ "() {\n\t[native code]\n}\n";
+ }
+
+ /**
+ * Decompile the body of a JavaScript Function.
+ *
+ * Decompiles the body a previously compiled JavaScript Function
+ * object to canonical source, omitting the function header and
+ * trailing brace.
+ *
+ * Returns '[native code]' if no decompilation information is available.
+ *
+ * @param fun the JavaScript function to decompile
+ * @param indent the number of spaces to indent the result
+ * @return a string representing the function body source.
+ */
+ public final String decompileFunctionBody(Function fun, int indent)
+ {
+ if (fun instanceof BaseFunction) {
+ BaseFunction bf = (BaseFunction)fun;
+ return bf.decompile(indent, Decompiler.ONLY_BODY_FLAG);
+ }
+ // ALERT: not sure what the right response here is.
+ return "[native code]\n";
+ }
+
+ /**
+ * Create a new JavaScript object.
+ *
+ * Equivalent to evaluating "new Object()".
+ * @param scope the scope to search for the constructor and to evaluate
+ * against
+ * @return the new object
+ */
+ public Scriptable newObject(Scriptable scope)
+ {
+ NativeObject result = new NativeObject();
+ ScriptRuntime.setBuiltinProtoAndParent(result, scope,
+ TopLevel.Builtins.Object);
+ return result;
+ }
+
+ /**
+ * Create a new JavaScript object by executing the named constructor.
+ *
+ * The call newObject(scope, "Foo") is equivalent to
+ * evaluating "new Foo()".
+ *
+ * @param scope the scope to search for the constructor and to evaluate against
+ * @param constructorName the name of the constructor to call
+ * @return the new object
+ */
+ public Scriptable newObject(Scriptable scope, String constructorName)
+ {
+ return newObject(scope, constructorName, ScriptRuntime.emptyArgs);
+ }
+
+ /**
+ * Creates a new JavaScript object by executing the named constructor.
+ *
+ * Searches scope for the named constructor, calls it with
+ * the given arguments, and returns the result.
+ * is equivalent to evaluating "new Foo('a', 'b')", assuming that the Foo
+ * constructor has been defined in scope.
+ *
+ * @param scope The scope to search for the constructor and to evaluate
+ * against
+ * @param constructorName the name of the constructor to call
+ * @param args the array of arguments for the constructor
+ * @return the new object
+ */
+ public Scriptable newObject(Scriptable scope, String constructorName,
+ Object[] args)
+ {
+ scope = ScriptableObject.getTopLevelScope(scope);
+ Function ctor = ScriptRuntime.getExistingCtor(this, scope,
+ constructorName);
+ if (args == null) { args = ScriptRuntime.emptyArgs; }
+ return ctor.construct(this, scope, args);
+ }
+
+ /**
+ * Create an array with a specified initial length.
+ *
+ * @param scope the scope to create the object in
+ * @param length the initial length (JavaScript arrays may have
+ * additional properties added dynamically).
+ * @return the new array object
+ */
+ public Scriptable newArray(Scriptable scope, int length)
+ {
+ NativeArray result = new NativeArray(length);
+ ScriptRuntime.setBuiltinProtoAndParent(result, scope,
+ TopLevel.Builtins.Array);
+ return result;
+ }
+
+ /**
+ * Create an array with a set of initial elements.
+ *
+ * @param scope the scope to create the object in.
+ * @param elements the initial elements. Each object in this array
+ * must be an acceptable JavaScript type and type
+ * of array should be exactly Object[], not
+ * SomeObjectSubclass[].
+ * @return the new array object.
+ */
+ public Scriptable newArray(Scriptable scope, Object[] elements)
+ {
+ if (elements.getClass().getComponentType() != ScriptRuntime.ObjectClass)
+ throw new IllegalArgumentException();
+ NativeArray result = new NativeArray(elements);
+ ScriptRuntime.setBuiltinProtoAndParent(result, scope,
+ TopLevel.Builtins.Array);
+ return result;
+ }
+
+ /**
+ * Get the elements of a JavaScript array.
+ *
+ * If the object defines a length property convertible to double number,
+ * then the number is converted Uint32 value as defined in Ecma 9.6
+ * and Java array of that size is allocated.
+ * The array is initialized with the values obtained by
+ * calling get() on object for each value of i in [0,length-1]. If
+ * there is not a defined value for a property the Undefined value
+ * is used to initialize the corresponding element in the array. The
+ * Java array is then returned.
+ * If the object doesn't define a length property or it is not a number,
+ * empty array is returned.
+ * @param object the JavaScript array or array-like object
+ * @return a Java array of objects
+ * @since 1.4 release 2
+ */
+ public final Object[] getElements(Scriptable object)
+ {
+ return ScriptRuntime.getArrayElements(object);
+ }
+
+ /**
+ * Convert the value to a JavaScript boolean value.
+ *
+ * See ECMA 9.2.
+ *
+ * @param value a JavaScript value
+ * @return the corresponding boolean value converted using
+ * the ECMA rules
+ */
+ public static boolean toBoolean(Object value)
+ {
+ return ScriptRuntime.toBoolean(value);
+ }
+
+ /**
+ * Convert the value to a JavaScript Number value.
+ *
+ * Returns a Java double for the JavaScript Number.
+ *
+ * See ECMA 9.3.
+ *
+ * @param value a JavaScript value
+ * @return the corresponding double value converted using
+ * the ECMA rules
+ */
+ public static double toNumber(Object value)
+ {
+ return ScriptRuntime.toNumber(value);
+ }
+
+ /**
+ * Convert the value to a JavaScript String value.
+ *
+ * See ECMA 9.8.
+ *
+ * @param value a JavaScript value
+ * @return the corresponding String value converted using
+ * the ECMA rules
+ */
+ public static String toString(Object value)
+ {
+ return ScriptRuntime.toString(value);
+ }
+
+ /**
+ * Convert the value to an JavaScript object value.
+ *
+ * Note that a scope must be provided to look up the constructors
+ * for Number, Boolean, and String.
+ *
+ * See ECMA 9.9.
+ *
+ * Additionally, arbitrary Java objects and classes will be
+ * wrapped in a Scriptable object with its Java fields and methods
+ * reflected as JavaScript properties of the object.
+ *
+ * @param value any Java object
+ * @param scope global scope containing constructors for Number,
+ * Boolean, and String
+ * @return new JavaScript object
+ */
+ public static Scriptable toObject(Object value, Scriptable scope)
+ {
+ return ScriptRuntime.toObject(scope, value);
+ }
+
+ /**
+ * @deprecated
+ * @see #toObject(Object, Scriptable)
+ */
+ public static Scriptable toObject(Object value, Scriptable scope,
+ Class> staticType)
+ {
+ return ScriptRuntime.toObject(scope, value);
+ }
+
+ /**
+ * Convenient method to convert java value to its closest representation
+ * in JavaScript.
+ *
+ * If value is an instance of String, Number, Boolean, Function or
+ * Scriptable, it is returned as it and will be treated as the corresponding
+ * JavaScript type of string, number, boolean, function and object.
+ *
+ * Note that for Number instances during any arithmetic operation in
+ * JavaScript the engine will always use the result of
+ * Number.doubleValue() resulting in a precision loss if
+ * the number can not fit into double.
+ *
+ * If value is an instance of Character, it will be converted to string of
+ * length 1 and its JavaScript type will be string.
+ *
+ * The rest of values will be wrapped as LiveConnect objects
+ * by calling {@link WrapFactory#wrap(Context cx, Scriptable scope,
+ * Object obj, Class staticType)} as in:
+ *
+ *
+ * @param value any Java object
+ * @param scope top scope object
+ * @return value suitable to pass to any API that takes JavaScript values.
+ */
+ public static Object javaToJS(Object value, Scriptable scope)
+ {
+ if (value instanceof String || value instanceof Number
+ || value instanceof Boolean || value instanceof Scriptable)
+ {
+ return value;
+ } else if (value instanceof Character) {
+ return String.valueOf(((Character)value).charValue());
+ } else {
+ Context cx = Context.getContext();
+ return cx.getWrapFactory().wrap(cx, scope, value, null);
+ }
+ }
+
+ /**
+ * Convert a JavaScript value into the desired type.
+ * Uses the semantics defined with LiveConnect3 and throws an
+ * Illegal argument exception if the conversion cannot be performed.
+ * @param value the JavaScript value to convert
+ * @param desiredType the Java type to convert to. Primitive Java
+ * types are represented using the TYPE fields in the corresponding
+ * wrapper class in java.lang.
+ * @return the converted value
+ * @throws EvaluatorException if the conversion cannot be performed
+ */
+ public static Object jsToJava(Object value, Class> desiredType)
+ throws EvaluatorException
+ {
+ return NativeJavaObject.coerceTypeImpl(desiredType, value);
+ }
+
+ /**
+ * @deprecated
+ * @see #jsToJava(Object, Class)
+ * @throws IllegalArgumentException if the conversion cannot be performed.
+ * Note that {@link #jsToJava(Object, Class)} throws
+ * {@link EvaluatorException} instead.
+ */
+ public static Object toType(Object value, Class> desiredType)
+ throws IllegalArgumentException
+ {
+ try {
+ return jsToJava(value, desiredType);
+ } catch (EvaluatorException ex) {
+ IllegalArgumentException
+ ex2 = new IllegalArgumentException(ex.getMessage());
+ Kit.initCause(ex2, ex);
+ throw ex2;
+ }
+ }
+
+ /**
+ * Rethrow the exception wrapping it as the script runtime exception.
+ * Unless the exception is instance of {@link EcmaError} or
+ * {@link EvaluatorException} it will be wrapped as
+ * {@link WrappedException}, a subclass of {@link EvaluatorException}.
+ * The resulting exception object always contains
+ * source name and line number of script that triggered exception.
+ *
+ * This method always throws an exception, its return value is provided
+ * only for convenience to allow a usage like:
+ *
+ * throw Context.throwAsScriptRuntimeEx(ex);
+ *
+ * to indicate that code after the method is unreachable.
+ * @throws EvaluatorException
+ * @throws EcmaError
+ */
+ public static RuntimeException throwAsScriptRuntimeEx(Throwable e)
+ {
+ while ((e instanceof InvocationTargetException)) {
+ e = ((InvocationTargetException) e).getTargetException();
+ }
+ // special handling of Error so scripts would not catch them
+ if (e instanceof Error) {
+ Context cx = getContext();
+ if (cx == null ||
+ !cx.hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS))
+ {
+ throw (Error)e;
+ }
+ }
+ if (e instanceof RhinoException) {
+ throw (RhinoException)e;
+ }
+ throw new WrappedException(e);
+ }
+
+ /**
+ * Tell whether debug information is being generated.
+ * @since 1.3
+ */
+ public final boolean isGeneratingDebug()
+ {
+ return generatingDebug;
+ }
+
+ /**
+ * Specify whether or not debug information should be generated.
+ *
+ * Setting the generation of debug information on will set the
+ * optimization level to zero.
+ * @since 1.3
+ */
+ public final void setGeneratingDebug(boolean generatingDebug)
+ {
+ if (sealed) onSealedMutation();
+ generatingDebugChanged = true;
+ if (generatingDebug && getOptimizationLevel() > 0)
+ setOptimizationLevel(0);
+ this.generatingDebug = generatingDebug;
+ }
+
+ /**
+ * Tell whether source information is being generated.
+ * @since 1.3
+ */
+ public final boolean isGeneratingSource()
+ {
+ return generatingSource;
+ }
+
+ /**
+ * Specify whether or not source information should be generated.
+ *
+ * Without source information, evaluating the "toString" method
+ * on JavaScript functions produces only "[native code]" for
+ * the body of the function.
+ * Note that code generated without source is not fully ECMA
+ * conformant.
+ * @since 1.3
+ */
+ public final void setGeneratingSource(boolean generatingSource)
+ {
+ if (sealed) onSealedMutation();
+ this.generatingSource = generatingSource;
+ }
+
+ /**
+ * Get the current optimization level.
+ *
+ * The optimization level is expressed as an integer between -1 and
+ * 9.
+ * @since 1.3
+ *
+ */
+ public final int getOptimizationLevel()
+ {
+ return optimizationLevel;
+ }
+
+ /**
+ * Set the current optimization level.
+ *
+ * The optimization level is expected to be an integer between -1 and
+ * 9. Any negative values will be interpreted as -1, and any values
+ * greater than 9 will be interpreted as 9.
+ * An optimization level of -1 indicates that interpretive mode will
+ * always be used. Levels 0 through 9 indicate that class files may
+ * be generated. Higher optimization levels trade off compile time
+ * performance for runtime performance.
+ * The optimizer level can't be set greater than -1 if the optimizer
+ * package doesn't exist at run time.
+ * @param optimizationLevel an integer indicating the level of
+ * optimization to perform
+ * @since 1.3
+ *
+ */
+ public final void setOptimizationLevel(int optimizationLevel)
+ {
+ if (sealed) onSealedMutation();
+ if (optimizationLevel == -2) {
+ // To be compatible with Cocoon fork
+ optimizationLevel = -1;
+ }
+ checkOptimizationLevel(optimizationLevel);
+ if (codegenClass == null)
+ optimizationLevel = -1;
+ this.optimizationLevel = optimizationLevel;
+ }
+
+ public static boolean isValidOptimizationLevel(int optimizationLevel)
+ {
+ return -1 <= optimizationLevel && optimizationLevel <= 9;
+ }
+
+ public static void checkOptimizationLevel(int optimizationLevel)
+ {
+ if (isValidOptimizationLevel(optimizationLevel)) {
+ return;
+ }
+ throw new IllegalArgumentException(
+ "Optimization level outside [-1..9]: "+optimizationLevel);
+ }
+
+ /**
+ * Returns the maximum stack depth (in terms of number of call frames)
+ * allowed in a single invocation of interpreter. If the set depth would be
+ * exceeded, the interpreter will throw an EvaluatorException in the script.
+ * Defaults to Integer.MAX_VALUE. The setting only has effect for
+ * interpreted functions (those compiled with optimization level set to -1).
+ * As the interpreter doesn't use the Java stack but rather manages its own
+ * stack in the heap memory, a runaway recursion in interpreted code would
+ * eventually consume all available memory and cause OutOfMemoryError
+ * instead of a StackOverflowError limited to only a single thread. This
+ * setting helps prevent such situations.
+ *
+ * @return The current maximum interpreter stack depth.
+ */
+ public final int getMaximumInterpreterStackDepth()
+ {
+ return maximumInterpreterStackDepth;
+ }
+
+ /**
+ * Sets the maximum stack depth (in terms of number of call frames)
+ * allowed in a single invocation of interpreter. If the set depth would be
+ * exceeded, the interpreter will throw an EvaluatorException in the script.
+ * Defaults to Integer.MAX_VALUE. The setting only has effect for
+ * interpreted functions (those compiled with optimization level set to -1).
+ * As the interpreter doesn't use the Java stack but rather manages its own
+ * stack in the heap memory, a runaway recursion in interpreted code would
+ * eventually consume all available memory and cause OutOfMemoryError
+ * instead of a StackOverflowError limited to only a single thread. This
+ * setting helps prevent such situations.
+ *
+ * @param max the new maximum interpreter stack depth
+ * @throws IllegalStateException if this context's optimization level is not
+ * -1
+ * @throws IllegalArgumentException if the new depth is not at least 1
+ */
+ public final void setMaximumInterpreterStackDepth(int max)
+ {
+ if(sealed) onSealedMutation();
+ if(optimizationLevel != -1) {
+ throw new IllegalStateException("Cannot set maximumInterpreterStackDepth when optimizationLevel != -1");
+ }
+ if(max < 1) {
+ throw new IllegalArgumentException("Cannot set maximumInterpreterStackDepth to less than 1");
+ }
+ maximumInterpreterStackDepth = max;
+ }
+
+ /**
+ * Set the security controller for this context.
+ *
SecurityController may only be set if it is currently null
+ * and {@link SecurityController#hasGlobal()} is false.
+ * Otherwise a SecurityException is thrown.
+ * @param controller a SecurityController object
+ * @throws SecurityException if there is already a SecurityController
+ * object for this Context or globally installed.
+ * @see SecurityController#initGlobal(SecurityController controller)
+ * @see SecurityController#hasGlobal()
+ */
+ public final void setSecurityController(SecurityController controller)
+ {
+ if (sealed) onSealedMutation();
+ if (controller == null) throw new IllegalArgumentException();
+ if (securityController != null) {
+ throw new SecurityException("Can not overwrite existing SecurityController object");
+ }
+ if (SecurityController.hasGlobal()) {
+ throw new SecurityException("Can not overwrite existing global SecurityController object");
+ }
+ securityController = controller;
+ }
+
+ /**
+ * Set the LiveConnect access filter for this context.
+ *
{@link ClassShutter} may only be set if it is currently null.
+ * Otherwise a SecurityException is thrown.
+ * @param shutter a ClassShutter object
+ * @throws SecurityException if there is already a ClassShutter
+ * object for this Context
+ */
+ public synchronized final void setClassShutter(ClassShutter shutter)
+ {
+ if (sealed) onSealedMutation();
+ if (shutter == null) throw new IllegalArgumentException();
+ if (hasClassShutter) {
+ throw new SecurityException("Cannot overwrite existing " +
+ "ClassShutter object");
+ }
+ classShutter = shutter;
+ hasClassShutter = true;
+ }
+
+ final synchronized ClassShutter getClassShutter()
+ {
+ return classShutter;
+ }
+
+ public interface ClassShutterSetter {
+ public void setClassShutter(ClassShutter shutter);
+ public ClassShutter getClassShutter();
+ }
+
+ public final synchronized ClassShutterSetter getClassShutterSetter() {
+ if (hasClassShutter)
+ return null;
+ hasClassShutter = true;
+ return new ClassShutterSetter() {
+ @Override
+ public void setClassShutter(ClassShutter shutter) {
+ classShutter = shutter;
+ }
+ @Override
+ public ClassShutter getClassShutter() {
+ return classShutter;
+ }
+ };
+ }
+
+ /**
+ * Get a value corresponding to a key.
+ *
+ * Since the Context is associated with a thread it can be
+ * used to maintain values that can be later retrieved using
+ * the current thread.
+ *
+ * Note that the values are maintained with the Context, so
+ * if the Context is disassociated from the thread the values
+ * cannot be retrieved. Also, if private data is to be maintained
+ * in this manner the key should be a java.lang.Object
+ * whose reference is not divulged to untrusted code.
+ * @param key the key used to lookup the value
+ * @return a value previously stored using putThreadLocal.
+ */
+ public final Object getThreadLocal(Object key)
+ {
+ if (threadLocalMap == null)
+ return null;
+ return threadLocalMap.get(key);
+ }
+
+ /**
+ * Put a value that can later be retrieved using a given key.
+ *
+ * @param key the key used to index the value
+ * @param value the value to save
+ */
+ public synchronized final void putThreadLocal(Object key, Object value)
+ {
+ if (sealed) onSealedMutation();
+ if (threadLocalMap == null)
+ threadLocalMap = new HashMap