# HG changeset patch # User Chris Seaton # Date 1389028329 0 # Node ID 0fbee3eb71f00b90f1974bbe513d8486b1f4bae3 # Parent 64a23ce736a05045faf19aa84a88e9b9d6e6241f Ruby: import project. diff -r 64a23ce736a0 -r 0fbee3eb71f0 .hgignore --- a/.hgignore Mon Jan 06 14:21:39 2014 +0100 +++ b/.hgignore Mon Jan 06 17:12:09 2014 +0000 @@ -85,3 +85,5 @@ agent/build/* agent/make/filelist agent/make/sa17.tar.gz +graal/com.oracle.truffle.ruby.test/specs/mspec +graal/com.oracle.truffle.ruby.test/specs/rubyspec diff -r 64a23ce736a0 -r 0fbee3eb71f0 LICENSE.EPL --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE.EPL Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,242 @@ +Some parts of this software are marked as being licensed to you under the terms +of the Eclipse Public License version 1.0, as follows. + + THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE + PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION + OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + + 1. DEFINITIONS + + "Contribution" means: + + a) in the case of the initial Contributor, the initial code and + documentation distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + + i) changes to the Program, and + + ii) additions to the Program; + where such changes and/or additions to the Program + originate from and are distributed by that particular + Contributor. A Contribution 'originates' from a + Contributor if it was added to the Program by such + Contributor itself or anyone acting on such + Contributor's behalf. Contributions do not include + additions to the Program which: (i) are separate modules + of software distributed in conjunction with the Program + under their own license agreement, and (ii) are not + derivative works of the Program. + + "Contributor" means any person or entity that distributes the Program. + + "Licensed Patents" mean patent claims licensable by a Contributor + which are necessarily infringed by the use or sale of its + Contribution alone or when combined with the Program. + + "Program" means the Contributions distributed in accordance with + this Agreement. + + "Recipient" means anyone who receives the Program under this + Agreement, including all Contributors. + + 2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor + hereby grants Recipient a non-exclusive, worldwide, + royalty-free copyright license to reproduce, prepare + derivative works of, publicly display, publicly perform, + distribute and sublicense the Contribution of such + Contributor, if any, and such derivative works, in source + code and object code form. + + b) Subject to the terms of this Agreement, each Contributor + hereby grants Recipient a non-exclusive, worldwide, + royalty-free patent license under Licensed Patents to make, + use, sell, offer to sell, import and otherwise transfer the + Contribution of such Contributor, if any, in source code and + object code form. This patent license shall apply to the + combination of the Contribution and the Program if, at the + time the Contribution is added by the Contributor, such + addition of the Contribution causes such combination to be + covered by the Licensed Patents. The patent license shall not + apply to any other combinations which include the + Contribution. No hardware per se is licensed hereunder. + + c) Recipient understands that although each Contributor grants + the licenses to its Contributions set forth herein, no + assurances are provided by any Contributor that the Program + does not infringe the patent or other intellectual property + rights of any other entity. Each Contributor disclaims any + liability to Recipient for claims brought by any other entity + based on infringement of intellectual property rights or + otherwise. As a condition to exercising the rights and + licenses granted hereunder, each Recipient hereby assumes + sole responsibility to secure any other intellectual property + rights needed, if any. For example, if a third party patent + license is required to allow Recipient to distribute the + Program, it is Recipient's responsibility to acquire that + license before distributing the Program. + + d) Each Contributor represents that to its knowledge it has + sufficient copyright rights in its Contribution, if any, to + grant the copyright license set forth in this Agreement. + + 3. REQUIREMENTS + + A Contributor may choose to distribute the Program in object code + form under its own license agreement, provided that: + + a) it complies with the terms and conditions of this Agreement; and + + b) its license agreement: + + i) effectively disclaims on behalf of all Contributors all + warranties and conditions, express and implied, including + warranties or conditions of title and non-infringement, + and implied warranties or conditions of merchantability + and fitness for a particular purpose; + + ii) effectively excludes on behalf of all Contributors all + liability for damages, including direct, indirect, + special, incidental and consequential damages, such as + lost profits; + + iii) states that any provisions which differ from this + Agreement are offered by that Contributor alone and not + by any other party; and + + iv) states that source code for the Program is available + from such Contributor, and informs licensees how to + obtain it in a reasonable manner on or through a medium + customarily used for software exchange. + + When the Program is made available in source code form: + + a) it must be made available under this Agreement; and + + b) a copy of this Agreement must be included with each copy of + the Program. + + Contributors may not remove or alter any copyright notices contained + within the Program. + + Each Contributor must identify itself as the originator of its + Contribution, if any, in a manner that reasonably allows subsequent + Recipients to identify the originator of the Contribution. + + 4. COMMERCIAL DISTRIBUTION + + Commercial distributors of software may accept certain + responsibilities with respect to end users, business partners and + the like. While this license is intended to facilitate the + commercial use of the Program, the Contributor who includes the + Program in a commercial product offering should do so in a manner + which does not create potential liability for other Contributors. + Therefore, if a Contributor includes the Program in a commercial + product offering, such Contributor ("Commercial Contributor") hereby + agrees to defend and indemnify every other Contributor ("Indemnified + Contributor") against any losses, damages and costs (collectively + "Losses") arising from claims, lawsuits and other legal actions + brought by a third party against the Indemnified Contributor to the + extent caused by the acts or omissions of such Commercial + Contributor in connection with its distribution of the Program in a + commercial product offering. The obligations in this section do not + apply to any claims or Losses relating to any actual or alleged + intellectual property infringement. In order to qualify, an + Indemnified Contributor must: a) promptly notify the Commercial + Contributor in writing of such claim, and b) allow the Commercial + Contributor to control, and cooperate with the Commercial + Contributor in, the defense and any related settlement negotiations. + The Indemnified Contributor may participate in any such claim at its + own expense. + + For example, a Contributor might include the Program in a commercial + product offering, Product X. That Contributor is then a Commercial + Contributor. If that Commercial Contributor then makes performance + claims, or offers warranties related to Product X, those performance + claims and warranties are such Commercial Contributor's + responsibility alone. Under this section, the Commercial Contributor + would have to defend claims against the other Contributors related + to those performance claims and warranties, and if a court requires + any other Contributor to pay any damages as a result, the Commercial + Contributor must pay those damages. + + 5. NO WARRANTY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS + PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, + ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient + is solely responsible for determining the appropriateness of using + and distributing the Program and assumes all risks associated with + its exercise of rights under this Agreement , including but not + limited to the risks and costs of program errors, compliance with + applicable laws, damage to or loss of data, programs or equipment, + and unavailability or interruption of operations. + + 6. DISCLAIMER OF LIABILITY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT + NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS + GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + DAMAGES. + + 7. GENERAL + + If any provision of this Agreement is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability + of the remainder of the terms of this Agreement, and without further + action by the parties hereto, such provision shall be reformed to + the minimum extent necessary to make such provision valid and + enforceable. + + If Recipient institutes patent litigation against any entity + (including a cross-claim or counterclaim in a lawsuit) alleging that + the Program itself (excluding combinations of the Program with other + software or hardware) infringes such Recipient's patent(s), then + such Recipient's rights granted under Section 2(b) shall terminate + as of the date such litigation is filed. + + All Recipient's rights under this Agreement shall terminate if it + fails to comply with any of the material terms or conditions of this + Agreement and does not cure such failure in a reasonable period of + time after becoming aware of such noncompliance. If all Recipient's + rights under this Agreement terminate, Recipient agrees to cease use + and distribution of the Program as soon as reasonably practicable. + However, Recipient's obligations under this Agreement and any + licenses granted by Recipient relating to the Program shall continue + and survive. + + Everyone is permitted to copy and distribute copies of this + Agreement, but in order to avoid inconsistency the Agreement is + copyrighted and may only be modified in the following manner. The + Agreement Steward reserves the right to publish new versions + (including revisions) of this Agreement from time to time. No one + other than the Agreement Steward has the right to modify this + Agreement. The Eclipse Foundation is the initial Agreement Steward. + The Eclipse Foundation may assign the responsibility to serve as the + Agreement Steward to a suitable separate entity. Each new version of + the Agreement will be given a distinguishing version number. The + Program (including Contributions) may always be distributed subject + to the version of the Agreement under which it was received. In + addition, after a new version of the Agreement is published, + Contributor may elect to distribute the Program (including its + Contributions) under the new version. Except as expressly stated in + Sections 2(a) and 2(b) above, Recipient receives no rights or + licenses to the intellectual property of any Contributor under this + Agreement, whether expressly, by implication, estoppel or otherwise. + All rights in the Program not expressly granted under this Agreement + are reserved. + + This Agreement is governed by the laws of the State of New York and + the intellectual property laws of the United States of America. No + party to this Agreement will bring a legal action under this + Agreement more than one year after the cause of action arose. Each + party waives its rights to a jury trial in any resulting litigation. diff -r 64a23ce736a0 -r 0fbee3eb71f0 LICENSE.LGPL --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE.LGPL Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,505 @@ +Some parts of this software are marked as being licensed to you under the terms +of the GNU Lesser General Public License version 2.1, as follows. + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + [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 + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest + possible use to the public, we recommend making it free software that + everyone can redistribute and change. You can do so by permitting + redistribution under these terms (or, alternatively, under the terms of the + ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is + safest to attach them to the start of each source file to most effectively + convey the exclusion of warranty; and each file should have at least the + "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Also add information on how to contact you by electronic and paper mail. + + You should also get your employer (if you work as a programmer) or your + school, if any, to sign a "copyright disclaimer" for the library, if + necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + + That's all there is to it! diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/CoreSource.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/CoreSource.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes; + +import java.io.*; + +import com.oracle.truffle.api.*; + +/** + * Singleton source used for core method nodes. + */ +public final class CoreSource implements Source { + + private final String name; + + public CoreSource(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public String getCode() { + return name; + } + + @Override + public String toString() { + return name; + } + + public String getPath() { + return null; + } + + public Reader getReader() { + return null; + } + + public InputStream getInputStream() { + return null; + } + + public String getCode(int lineNumber) { + return null; + } + + public int getLineCount() { + return 0; + } + + public int getLineNumber(int offset) { + return 0; + } + + public int getLineStartOffset(int lineNumber) { + return 0; + } + + public int getLineLength(int lineNumber) { + return 0; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/CoreSourceSection.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/CoreSourceSection.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes; + +import com.oracle.truffle.api.*; + +/** + * Singleton source section used for core method nodes. + */ +public final class CoreSourceSection implements SourceSection { + + private final String name; + + public CoreSourceSection(String name) { + this.name = name; + } + + public Source getSource() { + return new CoreSource(name); + } + + public int getStartLine() { + return 0; + } + + public int getStartColumn() { + return 0; + } + + public int getCharIndex() { + return 0; + } + + @Override + public int getCharLength() { + return 0; + } + + public int getCharEndIndex() { + return 0; + } + + public String getIdentifier() { + return null; + } + + public String getCode() { + return name; + } + + @Override + public String toString() { + return name; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/DefinedNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/DefinedNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Switches execution to the parallel {@link RubyNode#isDefined} semantic path. Represents the + * {@code defined?} keyword in Ruby. + */ +@NodeInfo(shortName = "defined") +public class DefinedNode extends RubyNode { + + @Child protected RubyNode child; + + public DefinedNode(RubyContext context, SourceSection sourceSection, RubyNode child) { + super(context, sourceSection); + this.child = adoptChild(child); + } + + @Override + public Object execute(VirtualFrame frame) { + return child.isDefined(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/InlinableMethodImplementation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/InlinableMethodImplementation.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * A method implementation that also carries the pristine root node and frame descriptor, from which + * we can inline. + * + * @see InlinedUnboxedDispatchNode + * @see InlinedBoxedDispatchNode + * @see InlineHeuristic + */ +public class InlinableMethodImplementation extends CallTargetMethodImplementation { + + private final FrameDescriptor frameDescriptor; + private final RubyRootNode pristineRootNode; + + public final boolean alwaysInline; + public final boolean shouldAppendCallNode; + + public InlinableMethodImplementation(CallTarget callTarget, MaterializedFrame declarationFrame, FrameDescriptor frameDescriptor, RubyRootNode pristineRootNode, boolean alwaysInline, + boolean shouldAppendCallNode) { + super(callTarget, declarationFrame); + + assert frameDescriptor != null; + assert pristineRootNode != null; + + this.frameDescriptor = frameDescriptor; + this.pristineRootNode = pristineRootNode; + this.alwaysInline = alwaysInline; + this.shouldAppendCallNode = shouldAppendCallNode; + } + + public FrameDescriptor getFrameDescriptor() { + return frameDescriptor; + } + + public RubyRootNode getPristineRootNode() { + return pristineRootNode; + } + + public RubyRootNode getCloneOfPristineRootNode() { + return NodeUtil.cloneNode(pristineRootNode); + } + + public boolean alwaysInline() { + return alwaysInline; + } + + public boolean getShouldAppendCallNode() { + return shouldAppendCallNode; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/ReadNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/ReadNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes; + +/** + * Interface for all nodes which read something, providing a method to transform them to write the + * same thing. + */ +public interface ReadNode { + + /** + * Return a new node that performs the equivalent write operation to this node's read, using the + * supplied node for the right-hand-side. + */ + RubyNode makeWriteNode(RubyNode rhs); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/RubyNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/RubyNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes; + +import java.math.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.nodes.debug.*; +import com.oracle.truffle.ruby.nodes.yield.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.core.range.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Base class for most nodes in Ruby. + * + * @see DispatchNode + * @see YieldDispatchNode + */ +@TypeSystemReference(RubyTypes.class) +public abstract class RubyNode extends Node { + + private final RubyContext context; + + public RubyNode(RubyContext context, SourceSection sourceSection) { + super(sourceSection); + + assert context != null; + assert sourceSection != null; + + this.context = context; + } + + public RubyNode(RubyNode prev) { + this(prev.context, prev.getSourceSection()); + } + + public abstract Object execute(VirtualFrame frame); + + /** + * Ruby's parallel semantic path. + * + * @see DefinedNode + */ + public Object isDefined(@SuppressWarnings("unused") VirtualFrame frame) { + throw new UnsupportedOperationException("no definition for " + getClass().getName()); + } + + public RubyArray executeArray(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyArray(execute(frame)); + } + + public BigInteger executeBignum(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectBigInteger(execute(frame)); + } + + public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectBoolean(execute(frame)); + } + + public RubyBignum executeBoxedBignum(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyBignum(execute(frame)); + } + + public RubyFixnum executeBoxedFixnum(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyFixnum(execute(frame)); + } + + public RubyFloat executeBoxedFloat(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyFloat(execute(frame)); + } + + public int executeFixnum(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectInteger(execute(frame)); + } + + public FixnumRange executeFixnumRange(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectFixnumRange(execute(frame)); + } + + public double executeFloat(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectDouble(execute(frame)); + } + + public NilPlaceholder executeNilPlaceholder(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectNilPlaceholder(execute(frame)); + } + + public Node executeNode(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectNode(execute(frame)); + } + + public Object[] executeObjectArray(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectObjectArray(execute(frame)); + } + + public ObjectRange executeObjectRange(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectObjectRange(execute(frame)); + } + + public RubyBasicObject executeRubyBasicObject(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyBasicObject(execute(frame)); + } + + public RubyBinding executeRubyBinding(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyBinding(execute(frame)); + } + + public RubyClass executeRubyClass(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyClass(execute(frame)); + } + + public RubyContinuation executeRubyContinuation(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyContinuation(execute(frame)); + } + + public RubyException executeRubyException(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyException(execute(frame)); + } + + public RubyFiber executeRubyFiber(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyFiber(execute(frame)); + } + + public RubyFile executeRubyFile(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyFile(execute(frame)); + } + + public RubyHash executeRubyHash(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyHash(execute(frame)); + } + + public RubyMatchData executeRubyMatchData(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyMatchData(execute(frame)); + } + + public RubyMethod executeRubyMethod(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyMethod(execute(frame)); + } + + public RubyModule executeRubyModule(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyModule(execute(frame)); + } + + public RubyNilClass executeRubyNilClass(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyNilClass(execute(frame)); + } + + public RubyObject executeRubyObject(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyObject(execute(frame)); + } + + public RubyProc executeRubyProc(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyProc(execute(frame)); + } + + public RubyRange executeRubyRange(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyRange(execute(frame)); + } + + public RubyRegexp executeRubyRegexp(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyRegexp(execute(frame)); + } + + public RubySymbol executeRubySymbol(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubySymbol(execute(frame)); + } + + public RubyThread executeRubyThread(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyThread(execute(frame)); + } + + public RubyTime executeRubyTime(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyTime(execute(frame)); + } + + public RubyString executeString(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectRubyString(execute(frame)); + } + + public UndefinedPlaceholder executeUndefinedPlaceholder(VirtualFrame frame) throws UnexpectedResultException { + return RubyTypesGen.RUBYTYPES.expectUndefinedPlaceholder(execute(frame)); + } + + public void executeVoid(VirtualFrame frame) { + execute(frame); + } + + /** + * If you aren't sure whether you have a normal {@link RubyNode} or a {@link RubyProxyNode}, + * this method will return the real node, whether that is this node, or whether this node is a + * proxy and you actually need the child. + */ + public RubyNode getNonProxyNode() { + return this; + } + + public RubyContext getContext() { + return context; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/RubyRootNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/RubyRootNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * The root node in an AST for a method. Unlike {@link RubyNode}, this has a single entry point, + * {@link #execute}, which Truffle knows about and can create a {@link CallTarget} from. + */ +public class RubyRootNode extends RootNode { + + protected final String indicativeName; + @Child protected RubyNode body; + + public RubyRootNode(SourceSection sourceSection, String indicativeName, RubyNode body) { + super(sourceSection); + + assert indicativeName != null; + assert body != null; + + this.body = adoptChild(body); + this.indicativeName = indicativeName; + } + + @Override + public Object execute(VirtualFrame frame) { + return body.execute(frame); + } + + @Override + public String toString() { + final SourceSection sourceSection = getSourceSection(); + final String source = sourceSection == null ? "" : sourceSection.toString(); + return "Method " + indicativeName + ":" + source + "@" + Integer.toHexString(hashCode()); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/RubyTypes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/RubyTypes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes; + +import java.math.*; + +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.core.range.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * The list of types and type conversions that the AST interpreter knows about and can specialise + * using. Used by the DSL. + */ +@TypeSystem({UndefinedPlaceholder.class, // + NilPlaceholder.class, // + boolean.class, // + int.class, // + double.class, // + BigInteger.class, // + FixnumRange.class, // + ObjectRange.class, // + RubyArray.class, // + RubyBignum.class, // + RubyBinding.class, // + RubyClass.class, // + RubyContinuation.class, // + RubyException.class, // + RubyFiber.class, // + RubyFile.class, // + RubyFixnum.class, // + RubyFloat.class, // + RubyHash.class, // + RubyMatchData.class, // + RubyMethod.class, // + RubyModule.class, // + RubyNilClass.class, // + RubyProc.class, // + RubyRange.class, // + RubyRegexp.class, // + RubyString.class, // + RubySymbol.class, // + RubyThread.class, // + RubyTime.class, // + RubyObject.class, // + RubyBasicObject.class, // + Node.class, // + Object[].class}) +public class RubyTypes { + + /* + * The implicit casts allow the DSL to convert from an object of one type to another to satisfy + * specializations. + */ + + @ImplicitCast + public NilPlaceholder unboxNil(@SuppressWarnings("unused") RubyNilClass value) { + return NilPlaceholder.INSTANCE; + } + + @ImplicitCast + public int unboxFixnum(RubyFixnum value) { + return value.getValue(); + } + + @ImplicitCast + public BigInteger unboxBignum(RubyBignum value) { + return value.getValue(); + } + + @ImplicitCast + public double unboxFloat(RubyFloat value) { + return value.getValue(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/WriteNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/WriteNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes; + +/** + * Interface for all nodes which write something, providing a method to transform them to read the + * same thing. + */ +public interface WriteNode { + + /** + * Return a new node that performs the equivalent read operation to this node's write. + */ + RubyNode makeReadNode(); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/BooleanDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/BooleanDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * An unboxed node in the dispatch chain that dispatches if the node is a boolean. In normal unboxed + * dispatch we look at the Java class of the receiver. However, in Ruby true and false are two + * separate classes, so in this situation we have to dispatch on the value, as well as the Java + * class when we are dealing with booleans. + *

+ * TODO(CS): it would be nice if we could {@link RubyNode#executeBoolean} the receiver, but by the + * time we get to this dispatch node the receiver is already executed. + */ +public class BooleanDispatchNode extends UnboxedDispatchNode { + + private final Assumption falseUnmodifiedAssumption; + private final RubyMethod falseMethod; + + private final Assumption trueUnmodifiedAssumption; + private final RubyMethod trueMethod; + + @Child protected UnboxedDispatchNode next; + + public BooleanDispatchNode(RubyContext context, SourceSection sourceSection, Assumption falseUnmodifiedAssumption, RubyMethod falseMethod, Assumption trueUnmodifiedAssumption, + RubyMethod trueMethod, UnboxedDispatchNode next) { + super(context, sourceSection); + + assert falseUnmodifiedAssumption != null; + assert falseMethod != null; + assert trueUnmodifiedAssumption != null; + assert trueMethod != null; + + this.falseUnmodifiedAssumption = falseUnmodifiedAssumption; + this.falseMethod = falseMethod; + + this.trueUnmodifiedAssumption = trueUnmodifiedAssumption; + this.trueMethod = trueMethod; + + this.next = adoptChild(next); + } + + @Override + public Object dispatch(VirtualFrame frame, Object receiverObject, RubyProc blockObject, Object[] argumentsObjects) { + // Check it's a boolean + + if (!(receiverObject instanceof Boolean)) { + return next.dispatch(frame, receiverObject, blockObject, argumentsObjects); + } + + // Check the value + + Assumption unmodifiedAssumption; + RubyMethod method; + + if ((boolean) receiverObject) { + unmodifiedAssumption = trueUnmodifiedAssumption; + method = trueMethod; + } else { + unmodifiedAssumption = falseUnmodifiedAssumption; + method = falseMethod; + } + + // Check the class has not been modified + + try { + unmodifiedAssumption.check(); + } catch (InvalidAssumptionException e) { + return respecialize("class modified", frame, receiverObject, blockObject, argumentsObjects); + } + + // Call the method + + return method.call(frame.pack(), receiverObject, blockObject, argumentsObjects); + } + + @Override + public void setNext(UnboxedDispatchNode next) { + this.next = adoptChild(next); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/BoxedDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/BoxedDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * A node in the dispatch chain that expects the receiver to be an object boxed into a full + * {@link RubyBasicObject}. + */ +public abstract class BoxedDispatchNode extends DispatchNode { + + public BoxedDispatchNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public abstract Object dispatch(VirtualFrame frame, RubyBasicObject receiverObject, RubyProc blockObject, Object[] argumentsObjects); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/BoxingDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/BoxingDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * A node in the dispatch chain that boxes the receiver into a full Ruby {@link RubyBasicObject}. + * This node is initially created as an {@link UninitializedBoxingDispatchNode} and only becomes + * this node when we know that we do need to box on the fast path. Within this node we specialized + * for the case that the receiver is always already boxed. + */ +public class BoxingDispatchNode extends UnboxedDispatchNode { + + @Child protected BoxedDispatchNode next; + + private final BranchProfile boxBranch = new BranchProfile(); + + public BoxingDispatchNode(RubyContext context, SourceSection sourceSection, BoxedDispatchNode next) { + super(context, sourceSection); + + this.next = adoptChild(next); + } + + @Override + public Object dispatch(VirtualFrame frame, Object receiverObject, RubyProc blockObject, Object[] argumentsObjects) { + RubyBasicObject boxedReceiverObject; + + if (receiverObject instanceof RubyBasicObject) { + boxedReceiverObject = (RubyBasicObject) receiverObject; + } else { + boxBranch.enter(); + boxedReceiverObject = getContext().getCoreLibrary().box(receiverObject); + } + + return next.dispatch(frame, boxedReceiverObject, blockObject, argumentsObjects); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/CachedBoxedDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/CachedBoxedDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.lookup.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * A node in the dispatch chain that comes after the boxing point and caches a method on a full + * boxed Ruby BasicObject, matching it by looking at the lookup node and assuming it has not been + * modified. + */ +public class CachedBoxedDispatchNode extends BoxedDispatchNode { + + private final LookupNode expectedLookupNode; + private final Assumption unmodifiedAssumption; + private final RubyMethod method; + + @Child protected BoxedDispatchNode next; + + public CachedBoxedDispatchNode(RubyContext context, SourceSection sourceSection, LookupNode expectedLookupNode, RubyMethod method, BoxedDispatchNode next) { + super(context, sourceSection); + + assert expectedLookupNode != null; + assert method != null; + + this.expectedLookupNode = expectedLookupNode; + unmodifiedAssumption = expectedLookupNode.getUnmodifiedAssumption(); + this.method = method; + this.next = adoptChild(next); + } + + @Override + public Object dispatch(VirtualFrame frame, RubyBasicObject receiverObject, RubyProc blockObject, Object[] argumentsObjects) { + // Check the lookup node is what we expect + + if (receiverObject.getLookupNode() != expectedLookupNode) { + return next.dispatch(frame, receiverObject, blockObject, argumentsObjects); + } + + // Check the class has not been modified + + try { + unmodifiedAssumption.check(); + } catch (InvalidAssumptionException e) { + return respecialize("class modified", frame, receiverObject, blockObject, argumentsObjects); + } + + // Call the method + + return method.call(frame.pack(), receiverObject, blockObject, argumentsObjects); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/CachedUnboxedDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/CachedUnboxedDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * A node in the dispatch chain that comes before the boxing point and caches a method on a Java + * object, matching it by looking at the class and assuming it has not been modified. + */ +public class CachedUnboxedDispatchNode extends UnboxedDispatchNode { + + private final Class expectedClass; + private final Assumption unmodifiedAssumption; + private final RubyMethod method; + + @Child protected UnboxedDispatchNode next; + + public CachedUnboxedDispatchNode(RubyContext context, SourceSection sourceSection, Class expectedClass, Assumption unmodifiedAssumption, RubyMethod method, UnboxedDispatchNode next) { + super(context, sourceSection); + + assert expectedClass != null; + assert unmodifiedAssumption != null; + assert method != null; + + this.expectedClass = expectedClass; + this.unmodifiedAssumption = unmodifiedAssumption; + this.method = method; + this.next = adoptChild(next); + } + + @Override + public Object dispatch(VirtualFrame frame, Object receiverObject, RubyProc blockObject, Object[] argumentsObjects) { + // Check the class is what we expect + + if (receiverObject.getClass() != expectedClass) { + return next.dispatch(frame, receiverObject, blockObject, argumentsObjects); + } + + // Check the class has not been modified + + try { + unmodifiedAssumption.check(); + } catch (InvalidAssumptionException e) { + return respecialize("class modified", frame, receiverObject, blockObject, argumentsObjects); + } + + // Call the method + + return method.call(frame.pack(), receiverObject, blockObject, argumentsObjects); + } + + @Override + public void setNext(UnboxedDispatchNode next) { + this.next = adoptChild(next); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/CallNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/CallNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * A call node that has a chain of dispatch nodes. + *

+ * The dispatch chain starts as {@link CallNode} -> {@link DispatchHeadNode} -> + * {@link UninitializedBoxingDispatchNode} -> {@link UninitializedDispatchNode}. + *

+ * When the {@link UninitializedDispatchNode} is reached a new node is inserted into the chain. If + * the node dispatches based on some unboxed value (unboxed as in it's not a Ruby object, just a + * Java object) such as {@link Integer}, then that node is inserted before the + * {@link UninitializedBoxingDispatchNode}, otherwise if it dispatches based on some Ruby + * BasicObject, it is inserted afterwards. + *

+ * The {@link UninitializedBoxingDispatchNode} becomes a {@link BoxingDispatchNode} when we find + * that the boxing has to be done on the fast path - when there is some boxed dispatch node. + *

+ * So the general format is {@link CallNode} -> {@link DispatchHeadNode} -> zero or more + * unboxed dispatches -> {@link UninitializedBoxingDispatchNode} | {@link BoxingDispatchNode} + * -> zero or more boxed dispatches -> {@link UninitializedDispatchNode}. + *

+ * There are several special cases of unboxed and boxed dispatch nodes based on the types and + * methods involved. + *

+ * If we have too many dispatch nodes we replace the whole chain with {@link DispatchHeadNode} -> + * {@link BoxingDispatchNode} -> {@link GeneralBoxedDispatchNode}. + *

+ * This system allows us to dispatch based purely on Java class, before we have to turn the object + * into a full {@link RubyBasicObject} and consider the full Ruby lookup process, and something such + * as a math call which may work on Fixnum or Float to work as just a couple of applications of + * {@code instanceof} and assumption checks. + */ +public class CallNode extends RubyNode { + + @Child protected RubyNode receiver; + @Child protected ProcOrNullNode block; + @Children protected final RubyNode[] arguments; + + private final String name; + private final boolean isSplatted; + + @Child protected DispatchHeadNode dispatchHead; + + public CallNode(RubyContext context, SourceSection section, String name, RubyNode receiver, RubyNode block, boolean isSplatted, RubyNode[] arguments) { + super(context, section); + + assert receiver != null; + assert arguments != null; + assert name != null; + + this.receiver = adoptChild(receiver); + + if (block == null) { + this.block = null; + } else { + this.block = adoptChild(ProcOrNullNodeFactory.create(context, section, block)); + } + + this.arguments = adoptChildren(arguments); + this.name = name; + this.isSplatted = isSplatted; + + dispatchHead = adoptChild(adoptChild(new DispatchHeadNode(context, section, name, isSplatted))); + } + + @Override + public Object execute(VirtualFrame frame) { + final Object receiverObject = receiver.execute(frame); + final Object[] argumentsObjects = executeArguments(frame); + final RubyProc blockObject = executeBlock(frame); + + return dispatchHead.dispatch(frame, receiverObject, blockObject, argumentsObjects); + } + + private RubyProc executeBlock(VirtualFrame frame) { + if (block != null) { + return block.executeRubyProc(frame); + } else { + return null; + } + } + + @ExplodeLoop + private Object[] executeArguments(VirtualFrame frame) { + final Object[] argumentsObjects = new Object[arguments.length]; + + for (int i = 0; i < arguments.length; i++) { + argumentsObjects[i] = arguments[i].execute(frame); + assert RubyContext.shouldObjectBeVisible(argumentsObjects[i]) : argumentsObjects[i].getClass(); + } + + if (isSplatted) { + assert argumentsObjects[0] instanceof RubyArray; + return ((RubyArray) argumentsObjects[0]).toObjectArray(); + } else { + return argumentsObjects; + } + } + + @Override + public Object isDefined(VirtualFrame frame) { + final RubyContext context = getContext(); + + Object receiverObject; + + try { + /* + * TODO(CS): Getting a node via an accessor like this doesn't work with Truffle at the + * moment and will cause frame escape errors, so we don't use it in compilation mode. + */ + + CompilerAsserts.neverPartOfCompilation(); + + receiverObject = receiver.execute(frame); + } catch (Exception e) { + return NilPlaceholder.INSTANCE; + } + + final RubyBasicObject receiverBasicObject = context.getCoreLibrary().box(receiverObject); + + final RubyMethod method = receiverBasicObject.getLookupNode().lookupMethod(name); + + final RubyBasicObject self = context.getCoreLibrary().box(frame.getArguments(RubyArguments.class).getSelf()); + + if (method == null || method.isUndefined()) { + return NilPlaceholder.INSTANCE; + } + + if (!method.isVisibleTo(self)) { + return NilPlaceholder.INSTANCE; + } + + return context.makeString("method"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/DispatchHeadNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/DispatchHeadNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * The head of a chain of dispatch nodes. Can be used with {@link CallNode} or on its own. + */ +public class DispatchHeadNode extends DispatchNode { + + private final RubyContext context; + private final String name; + private final boolean isSplatted; + + @Child protected UnboxedDispatchNode dispatch; + + public DispatchHeadNode(RubyContext context, SourceSection sourceSection, String name, boolean isSplatted) { + super(context, sourceSection); + + assert context != null; + assert name != null; + + this.context = context; + this.name = name; + this.isSplatted = isSplatted; + + final UninitializedDispatchNode uninitializedDispatch = new UninitializedDispatchNode(context, sourceSection, name); + dispatch = adoptChild(new UninitializedBoxingDispatchNode(context, sourceSection, uninitializedDispatch)); + } + + public Object dispatch(VirtualFrame frame, Object receiverObject, RubyProc blockObject, Object... argumentsObjects) { + return dispatch.dispatch(frame, receiverObject, blockObject, argumentsObjects); + } + + /** + * Replace the entire dispatch chain with a fresh chain. Used when the situation has changed in + * such a significant way that it's best to start again rather than add new specializations to + * the chain. Used for example when methods appear to have been monkey-patched. + */ + public Object respecialize(VirtualFrame frame, String reason, Object receiverObject, RubyProc blockObject, Object... argumentObjects) { + CompilerAsserts.neverPartOfCompilation(); + + replace(new DispatchHeadNode(context, getSourceSection(), name, isSplatted), reason); + + final RubyBasicObject receiverBasicObject = context.getCoreLibrary().box(receiverObject); + + final RubyMethod method = lookup(frame, receiverBasicObject, name); + return method.call(frame.pack(), receiverBasicObject, blockObject, argumentObjects); + } + + public UnboxedDispatchNode getDispatch() { + return dispatch; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/DispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/DispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Any node in the dispatch chain. + */ +public class DispatchNode extends Node { + + private final RubyContext context; + + public DispatchNode(RubyContext context, SourceSection sourceSection) { + super(sourceSection); + + assert context != null; + assert sourceSection != null; + + this.context = context; + } + + /** + * Get the depth of this node in the dispatch chain. The first node below + * {@link DispatchHeadNode} is at depth 1. + */ + public int getDepth() { + int depth = 1; + Node parent = this.getParent(); + + while (!(parent instanceof DispatchHeadNode)) { + parent = parent.getParent(); + depth++; + } + + return depth; + } + + public Object respecialize(String reason, VirtualFrame frame, Object receiverObject, RubyProc blockObject, Object... argumentsObjects) { + CompilerAsserts.neverPartOfCompilation(); + + final int depth = getDepth(); + final DispatchHeadNode head = (DispatchHeadNode) NodeUtil.getNthParent(this, depth); + + return head.respecialize(frame, reason, receiverObject, blockObject, argumentsObjects); + } + + /** + * The central point for method lookup. + */ + protected RubyMethod lookup(VirtualFrame frame, RubyBasicObject receiverBasicObject, String name) { + final RubyMethod method = receiverBasicObject.getLookupNode().lookupMethod(name); + + final RubyBasicObject self = context.getCoreLibrary().box(frame.getArguments(RubyArguments.class).getSelf()); + + if (method == null || method.isUndefined()) { + CompilerDirectives.transferToInterpreter(); + throw new RaiseException(context.getCoreLibrary().nameErrorNoMethod(name, receiverBasicObject.toString())); + } + + if (!method.isVisibleTo(self)) { + CompilerDirectives.transferToInterpreter(); + throw new RaiseException(context.getCoreLibrary().noMethodError(name, receiverBasicObject.toString())); + } + + return method; + } + + public RubyContext getContext() { + return context; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/GeneralBoxedDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/GeneralBoxedDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * A node in the dispatch chain that does no caching and looks up methods from scratch each time it + * is called. + */ +public class GeneralBoxedDispatchNode extends BoxedDispatchNode { + + private final String name; + + public GeneralBoxedDispatchNode(RubyContext context, SourceSection sourceSection, String name) { + super(context, sourceSection); + + assert name != null; + + this.name = name; + } + + @Override + public Object dispatch(VirtualFrame frame, RubyBasicObject receiverObject, RubyProc blockObject, Object[] argumentsObjects) { + /* + * TODO(CS): we should probably have some kind of cache here - even if it's just a hash map. + * MRI and JRuby do and might avoid some pathological cases. + */ + + final RubyMethod method = lookup(frame, receiverObject, name); + return method.call(frame.pack(), receiverObject, blockObject, argumentsObjects); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/GeneralSuperCallNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/GeneralSuperCallNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents a super call - that is a call with self as the receiver, but the superclass of self + * used for lookup. Currently implemented without any caching, and needs to be replaced with the + * same caching mechanism as for normal calls without complicating the existing calls too much. + */ +@NodeInfo(shortName = "general-super-call") +public class GeneralSuperCallNode extends RubyNode { + + private final String name; + private final boolean isSplatted; + @Child protected RubyNode block; + @Children protected final RubyNode[] arguments; + + public GeneralSuperCallNode(RubyContext context, SourceSection sourceSection, String name, RubyNode block, RubyNode[] arguments, boolean isSplatted) { + super(context, sourceSection); + + assert name != null; + assert arguments != null; + assert !isSplatted || arguments.length == 1; + + this.name = name; + this.block = adoptChild(block); + this.arguments = adoptChildren(arguments); + this.isSplatted = isSplatted; + } + + @ExplodeLoop + @Override + public final Object execute(VirtualFrame frame) { + // This method is only a simple implementation - it needs proper caching + + CompilerAsserts.neverPartOfCompilation(); + + final RubyBasicObject self = (RubyBasicObject) frame.getArguments(RubyArguments.class).getSelf(); + + // Execute the arguments + + final Object[] argumentsObjects = new Object[arguments.length]; + + CompilerAsserts.compilationConstant(arguments.length); + for (int i = 0; i < arguments.length; i++) { + argumentsObjects[i] = arguments[i].execute(frame); + } + + // Execute the block + + RubyProc blockObject; + + if (block != null) { + final Object blockTempObject = block.execute(frame); + + if (blockTempObject instanceof NilPlaceholder) { + blockObject = null; + } else { + blockObject = (RubyProc) blockTempObject; + } + } else { + blockObject = null; + } + + // Lookup method + + final RubyClass selfClass = self.getRubyClass(); + final RubyMethod method = selfClass.getSuperclass().lookupMethod(name); + + if (method == null || method.isUndefined()) { + CompilerDirectives.transferToInterpreter(); + throw new RaiseException(getContext().getCoreLibrary().nameErrorNoMethod(name, self.toString())); + } + + if (!method.isVisibleTo(self)) { + CompilerDirectives.transferToInterpreter(); + throw new RaiseException(getContext().getCoreLibrary().noMethodError("(unknown)")); + } + + // Call the method + + if (isSplatted) { + final RubyArray argumentsArray = (RubyArray) argumentsObjects[0]; + return method.call(frame.pack(), self, blockObject, argumentsArray.asList().toArray()); + } else { + return method.call(frame.pack(), self, blockObject, argumentsObjects); + } + } + + @Override + public Object isDefined(VirtualFrame frame) { + final RubyContext context = getContext(); + + try { + final RubyBasicObject self = context.getCoreLibrary().box(frame.getArguments(RubyArguments.class).getSelf()); + final RubyBasicObject receiverRubyObject = context.getCoreLibrary().box(self); + + final RubyMethod method = receiverRubyObject.getRubyClass().getSuperclass().lookupMethod(name); + + if (method == null || method.isUndefined() || !method.isVisibleTo(self)) { + return NilPlaceholder.INSTANCE; + } else { + return context.makeString("super"); + } + } catch (Exception e) { + return NilPlaceholder.INSTANCE; + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/InlineHeuristic.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/InlineHeuristic.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.ruby.nodes.*; + +public class InlineHeuristic { + + public static boolean shouldInline(InlinableMethodImplementation method) { + if (method.alwaysInline()) { + return true; + } + + return false; + } + + public static boolean shouldInlineYield(@SuppressWarnings("unused") InlinableMethodImplementation method) { + return true; + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/InlinedBoxedDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/InlinedBoxedDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.lookup.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * A node in the dispatch chain that comes after the boxing point and caches a method on a full + * boxed {@link RubyBasicObject}, matching it by looking at the lookup node and assuming it has not + * been modified. + */ +public class InlinedBoxedDispatchNode extends BoxedDispatchNode { + + private final LookupNode expectedLookupNode; + private final Assumption unmodifiedAssumption; + + private final InlinableMethodImplementation method; + private final RubyRootNode rootNode; + + @Child protected BoxedDispatchNode next; + + public InlinedBoxedDispatchNode(RubyContext context, SourceSection sourceSection, LookupNode expectedLookupNode, InlinableMethodImplementation method, BoxedDispatchNode next) { + super(context, sourceSection); + + assert expectedLookupNode != null; + assert method != null; + + this.expectedLookupNode = expectedLookupNode; + unmodifiedAssumption = expectedLookupNode.getUnmodifiedAssumption(); + this.method = method; + this.rootNode = method.getCloneOfPristineRootNode(); + this.next = adoptChild(next); + } + + @Override + public Object dispatch(VirtualFrame frame, RubyBasicObject receiverObject, RubyProc blockObject, Object[] argumentsObjects) { + // Check the lookup node is what we expect + + if (receiverObject.getLookupNode() != expectedLookupNode) { + return next.dispatch(frame, receiverObject, blockObject, argumentsObjects); + } + + // Check the class has not been modified + + try { + unmodifiedAssumption.check(); + } catch (InvalidAssumptionException e) { + return respecialize("class modified", frame, receiverObject, blockObject, argumentsObjects); + } + + // Call the method + + Object[] modifiedArgumentsObjects; + + CompilerAsserts.compilationConstant(method.getShouldAppendCallNode()); + + if (method.getShouldAppendCallNode()) { + modifiedArgumentsObjects = Arrays.copyOf(argumentsObjects, argumentsObjects.length + 1); + modifiedArgumentsObjects[modifiedArgumentsObjects.length - 1] = this; + } else { + modifiedArgumentsObjects = argumentsObjects; + } + + final RubyArguments arguments = new RubyArguments(method.getDeclarationFrame(), receiverObject, blockObject, modifiedArgumentsObjects); + final VirtualFrame inlinedFrame = Truffle.getRuntime().createVirtualFrame(frame.pack(), arguments, method.getFrameDescriptor()); + return rootNode.execute(inlinedFrame); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/InlinedUnboxedDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/InlinedUnboxedDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +public class InlinedUnboxedDispatchNode extends UnboxedDispatchNode { + + private final Class expectedClass; + private final Assumption unmodifiedAssumption; + + private final InlinableMethodImplementation method; + private final RubyRootNode rootNode; + + @Child protected UnboxedDispatchNode next; + + public InlinedUnboxedDispatchNode(RubyContext context, SourceSection sourceSection, Class expectedClass, Assumption unmodifiedAssumption, InlinableMethodImplementation method, + UnboxedDispatchNode next) { + super(context, sourceSection); + + assert expectedClass != null; + assert method != null; + + this.expectedClass = expectedClass; + this.unmodifiedAssumption = unmodifiedAssumption; + this.method = method; + this.rootNode = method.getCloneOfPristineRootNode(); + this.next = adoptChild(next); + } + + @Override + public Object dispatch(VirtualFrame frame, Object receiverObject, RubyProc blockObject, Object[] argumentsObjects) { + // Check the class is what we expect + + if (receiverObject.getClass() != expectedClass) { + return next.dispatch(frame, receiverObject, blockObject, argumentsObjects); + } + + // Check the class has not been modified + + try { + unmodifiedAssumption.check(); + } catch (InvalidAssumptionException e) { + return respecialize("class modified", frame, receiverObject, blockObject, argumentsObjects); + } + + // Call the method + + final RubyArguments arguments = new RubyArguments(method.getDeclarationFrame(), receiverObject, blockObject, argumentsObjects); + final VirtualFrame inlinedFrame = Truffle.getRuntime().createVirtualFrame(frame.pack(), arguments, method.getFrameDescriptor()); + return rootNode.execute(inlinedFrame); + } + + @Override + public void setNext(UnboxedDispatchNode next) { + this.next = adoptChild(next); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/ProcOrNullNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/ProcOrNullNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Wraps some node that will produce either a {@link RubyProc} or a {@link NilPlaceholder} and + * returns {@code null} in case of the latter. Used in parts of the dispatch chain. + */ +@NodeInfo(shortName = "proc-or-null") +@NodeChild(value = "child", type = RubyNode.class) +public abstract class ProcOrNullNode extends RubyNode { + + public ProcOrNullNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ProcOrNullNode(ProcOrNullNode prev) { + super(prev); + } + + @Specialization + public Object doNil(@SuppressWarnings("unused") NilPlaceholder nil) { + return null; + } + + @Specialization + public Object doProc(RubyProc proc) { + return proc; + } + + @Override + public RubyProc executeRubyProc(VirtualFrame frame) { + final Object proc = execute(frame); + + // The standard asRubyProc test doesn't allow for null + assert proc == null || RubyTypesGen.RUBYTYPES.isRubyProc(proc); + + return (RubyProc) proc; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/UnboxedDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/UnboxedDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * A node in the dispatch chain that expects the receiver to be a simple Java object such as a boxed + * primitive, rather than a full {@link RubyBasicObject}. This allows calls to be made with a + * receiver such as {@link Integer} without having to turn it into a {@link RubyFixnum}. Followed at + * some point by an {@link UninitializedBoxingDispatchNode} or {@link BoxingDispatchNode} before we + * try to dispatch on a Ruby BasicObject or the {@link UninitializedDispatchNode}. + */ +public abstract class UnboxedDispatchNode extends DispatchNode { + + public UnboxedDispatchNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public abstract Object dispatch(VirtualFrame frame, Object receiverObject, RubyProc blockObject, Object[] argumentsObjects); + + public void setNext(@SuppressWarnings("unused") UnboxedDispatchNode next) { + throw new UnsupportedOperationException(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/UninitializedBoxingDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/UninitializedBoxingDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * A node in the dispatch chain that transfers to interpreter and then boxes the receiver. + */ +public class UninitializedBoxingDispatchNode extends UnboxedDispatchNode { + + @Child protected BoxedDispatchNode next; + + public UninitializedBoxingDispatchNode(RubyContext context, SourceSection sourceSection, BoxedDispatchNode next) { + super(context, sourceSection); + + this.next = adoptChild(next); + } + + @Override + public Object dispatch(VirtualFrame frame, Object receiverObject, RubyProc blockObject, Object[] argumentsObjects) { + CompilerDirectives.transferToInterpreter(); + + /* + * If the next dispatch node is something other than the uninitialized dispatch node then we + * need to replace this node because it's now on the fast path. If the receiver was already + * boxed. + * + * Note that with this scheme it will take a couple of calls for the chain to become fully + * specialized. + */ + + if (next instanceof UninitializedDispatchNode) { + this.replace(new BoxingDispatchNode(getContext(), getSourceSection(), next)); + } + + return next.dispatch(frame, getContext().getCoreLibrary().box(receiverObject), blockObject, argumentsObjects); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/UninitializedDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/call/UninitializedDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.call; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * The uninitialized dispatch node. Only reached when the method is not expected by any node in the + * dispatch chain, and only creates new nodes or modifies the existing chain. + */ +public class UninitializedDispatchNode extends BoxedDispatchNode { + + /* + * Node at depth 5 is 4 actual dispatches, the boxing dispatch and the final uninitalized + * dispatch. + */ + + private static final int MAX_DEPTH = 5; + + private final String name; + + public UninitializedDispatchNode(RubyContext context, SourceSection sourceSection, String name) { + super(context, sourceSection); + + assert name != null; + + this.name = name; + } + + @Override + public Object dispatch(VirtualFrame frame, RubyBasicObject receiverObject, RubyProc blockObject, Object[] argumentsObjects) { + CompilerDirectives.transferToInterpreter(); + + final RubyContext context = getContext(); + + final RubyMethod method = lookup(frame, receiverObject, name); + + final int depth = getDepth(); + + final DispatchHeadNode dispatchHead = (DispatchHeadNode) NodeUtil.getNthParent(this, depth); + + if (depth == MAX_DEPTH) { + /* + * Replace the chain with DispatchHeadNode -> ExpectBoxedDispatchNode -> + * GeneralDispatchNode. + */ + + context.implementationMessage("resorting to a general call node at %s", getSourceSection()); + NodeUtil.printTree(System.err, dispatchHead); + + final GeneralBoxedDispatchNode newGeneralDispatch = new GeneralBoxedDispatchNode(getContext(), getSourceSection(), name); + final BoxingDispatchNode newBoxing = new BoxingDispatchNode(getContext(), getSourceSection(), newGeneralDispatch); + + dispatchHead.getDispatch().replace(newBoxing); + return newBoxing.dispatch(frame, receiverObject, blockObject, argumentsObjects); + } else if (receiverObject instanceof Unboxable) { + /* + * Unboxed dispatch nodes are prepended to the chain of dispatch nodes, so they're + * before the point where receivers will definitely be boxed. + */ + + final Object receiverUnboxed = ((Unboxable) receiverObject).unbox(); + + final UnboxedDispatchNode firstDispatch = dispatchHead.getDispatch(); + + if (receiverObject instanceof RubyTrueClass || receiverObject instanceof RubyFalseClass) { + final Assumption falseUnmodifiedAssumption = context.getCoreLibrary().getFalseClass().getUnmodifiedAssumption(); + final RubyMethod falseMethod = lookup(frame, context.getCoreLibrary().box(false), name); + final Assumption trueUnmodifiedAssumption = context.getCoreLibrary().getTrueClass().getUnmodifiedAssumption(); + final RubyMethod trueMethod = lookup(frame, context.getCoreLibrary().box(true), name); + + final BooleanDispatchNode newDispatch = new BooleanDispatchNode(getContext(), getSourceSection(), falseUnmodifiedAssumption, falseMethod, trueUnmodifiedAssumption, trueMethod, null); + firstDispatch.replace(newDispatch, "prepending new unboxed dispatch node to chain"); + newDispatch.setNext(firstDispatch); + return newDispatch.dispatch(frame, receiverUnboxed, blockObject, argumentsObjects); + } else { + UnboxedDispatchNode newDispatch; + + if (method.getImplementation() instanceof InlinableMethodImplementation && InlineHeuristic.shouldInline((InlinableMethodImplementation) method.getImplementation())) { + newDispatch = new InlinedUnboxedDispatchNode(getContext(), getSourceSection(), receiverUnboxed.getClass(), receiverObject.getRubyClass().getUnmodifiedAssumption(), + (InlinableMethodImplementation) method.getImplementation(), null); + } else { + newDispatch = new CachedUnboxedDispatchNode(getContext(), getSourceSection(), receiverUnboxed.getClass(), receiverObject.getRubyClass().getUnmodifiedAssumption(), method, null); + } + + firstDispatch.replace(newDispatch, "prepending new unboxed dispatch node to chain"); + newDispatch.setNext(firstDispatch); + + return newDispatch.dispatch(frame, receiverUnboxed, blockObject, argumentsObjects); + } + } else { + /* + * Boxed dispatch nodes are appended to the chain of dispatch nodes, so they're after + * the point where receivers are guaranteed to be boxed. + */ + + final UninitializedDispatchNode newUninitializedDispatch = new UninitializedDispatchNode(getContext(), getSourceSection(), name); + + BoxedDispatchNode newDispatch; + + if (method.getImplementation() instanceof InlinableMethodImplementation && InlineHeuristic.shouldInline((InlinableMethodImplementation) method.getImplementation())) { + newDispatch = new InlinedBoxedDispatchNode(getContext(), getSourceSection(), receiverObject.getLookupNode(), (InlinableMethodImplementation) method.getImplementation(), + newUninitializedDispatch); + } else { + newDispatch = new CachedBoxedDispatchNode(getContext(), getSourceSection(), receiverObject.getLookupNode(), method, newUninitializedDispatch); + } + + replace(newDispatch, "appending new boxed dispatch node to chain"); + + return newDispatch.dispatch(frame, receiverObject, blockObject, argumentsObjects); + } + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/BooleanCastNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/BooleanCastNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.cast; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Casts a value into a boolean. Works at the language level, so doesn't call any Ruby methods to + * cast non-core or monkey-patched objects. + */ +@NodeInfo(shortName = "cast-boolean") +@NodeChild(value = "child", type = RubyNode.class) +public abstract class BooleanCastNode extends RubyNode { + + public BooleanCastNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BooleanCastNode(BooleanCastNode copy) { + super(copy.getContext(), copy.getSourceSection()); + } + + @Specialization + public boolean doBoolean(boolean value) { + return value; + } + + @Specialization + public boolean doNil(@SuppressWarnings("unused") NilPlaceholder nil) { + return false; + } + + @Generic + public boolean doGeneric(Object object) { + if (object instanceof Boolean) { + return (boolean) object; + } else if (object instanceof NilPlaceholder || object instanceof RubyNilClass) { + return false; + } else { + return true; + } + } + + @Override + public abstract boolean executeBoolean(VirtualFrame frame); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/LambdaNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/LambdaNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.cast; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +@NodeInfo(shortName = "lambda") +public class LambdaNode extends RubyNode { + + @Child private RubyNode definition; + + public LambdaNode(RubyContext context, SourceSection sourceSection, RubyNode definition) { + super(context, sourceSection); + this.definition = adoptChild(definition); + } + + @Override + public Object execute(VirtualFrame frame) { + return new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.LAMBDA, frame.getArguments(RubyArguments.class).getSelf(), null, (RubyMethod) definition.execute(frame)); + } + + @Override + public void executeVoid(VirtualFrame frame) { + definition.executeVoid(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/ProcCastNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/ProcCastNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.cast; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Casts an object to a Ruby Proc object. + */ +@NodeInfo(shortName = "cast-proc") +@NodeChild("child") +public abstract class ProcCastNode extends RubyNode { + + @Child protected DispatchHeadNode toProc; + + public ProcCastNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + toProc = adoptChild(new DispatchHeadNode(context, getSourceSection(), "to_proc", false)); + } + + public ProcCastNode(ProcCastNode prev) { + super(prev); + toProc = adoptChild(prev.toProc); + } + + @Specialization + public NilPlaceholder doNil(NilPlaceholder nil) { + return nil; + } + + @Specialization + public RubyProc doRubyProc(RubyProc proc) { + return proc; + } + + @Specialization + public RubyProc doObject(VirtualFrame frame, RubyBasicObject object) { + return (RubyProc) toProc.dispatch(frame, object, null); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/SplatCastNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/SplatCastNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.cast; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * Splat as used to cast a value to an array if it isn't already, as in {@code *value}. + */ +@NodeInfo(shortName = "cast-splat") +@NodeChild("child") +public abstract class SplatCastNode extends RubyNode { + + public SplatCastNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SplatCastNode(SplatCastNode prev) { + super(prev); + } + + protected abstract RubyNode getChild(); + + @Specialization + public RubyArray doArray(RubyArray array) { + return array; + } + + @Specialization + public RubyArray doObject(Object object) { + if (object instanceof RubyArray) { + return (RubyArray) object; + } else { + return RubyArray.specializedFromObject(getContext().getCoreLibrary().getArrayClass(), object); + } + } + + @Override + public void executeVoid(VirtualFrame frame) { + getChild().executeVoid(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/StringToRegexpNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/StringToRegexpNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.cast; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Creates a regex from a string. + */ +@NodeInfo(shortName = "cast-string-to-regexp") +@NodeChild("string") +public abstract class StringToRegexpNode extends RubyNode { + + public StringToRegexpNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public StringToRegexpNode(StringToRegexpNode prev) { + super(prev); + } + + @Specialization + public RubyRegexp doString(RubyString string) { + return new RubyRegexp(getContext().getCoreLibrary().getRegexpClass(), string.toString()); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/StringToSymbolNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/cast/StringToSymbolNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.cast; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Creates a symbol from a string. + */ +@NodeInfo(shortName = "cast-string-to-symbol") +@NodeChild("string") +public abstract class StringToSymbolNode extends RubyNode { + + public StringToSymbolNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public StringToSymbolNode(StringToSymbolNode prev) { + super(prev); + } + + @Specialization + public RubySymbol doString(RubyString string) { + return new RubySymbol(getContext().getCoreLibrary().getSymbolClass(), string.toString()); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/constants/CachedReadConstantNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/constants/CachedReadConstantNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.constants; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents a constant read from some object and cached, with the assumption that the object it + * was read from is unmodified. If that assumption does not hold the read is uninitialized. If the + * class of the receiver changes we also uninitialize. + */ +@NodeInfo(shortName = "cached-read-constant") +public class CachedReadConstantNode extends ReadConstantNode { + + private final RubyClass expectedClass; + private final Assumption unmodifiedAssumption; + + private final Object value; + + private final boolean hasBoolean; + private final boolean booleanValue; + + private final boolean hasInt; + private final int intValue; + + private final boolean hasDouble; + private final double doubleValue; + + private final BranchProfile boxBranchProfile = new BranchProfile(); + + public CachedReadConstantNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, RubyClass expectedClass, Object value) { + super(context, sourceSection, name, receiver); + + this.expectedClass = expectedClass; + unmodifiedAssumption = expectedClass.getUnmodifiedAssumption(); + + this.value = value; + + /* + * We could do this lazily as needed, but I'm sure the compiler will appreciate the fact + * that these fields are all final. + */ + + if (value instanceof Boolean) { + hasBoolean = true; + booleanValue = (boolean) value; + + hasInt = false; + intValue = -1; + + hasDouble = false; + doubleValue = -1; + } else if (value instanceof Integer) { + hasBoolean = false; + booleanValue = false; + + hasInt = true; + intValue = (int) value; + + hasDouble = true; + doubleValue = (int) value; + } else if (value instanceof Double) { + hasBoolean = false; + booleanValue = false; + + hasInt = false; + intValue = -1; + + hasDouble = true; + doubleValue = (double) value; + } else { + hasBoolean = false; + booleanValue = false; + + hasInt = false; + intValue = -1; + + hasDouble = false; + doubleValue = -1; + } + } + + @Override + public Object execute(VirtualFrame frame) { + try { + guard(frame); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + + return value; + } + + @Override + public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException { + guard(frame); + + if (hasBoolean) { + return booleanValue; + } else { + throw new UnexpectedResultException(value); + } + } + + @Override + public int executeFixnum(VirtualFrame frame) throws UnexpectedResultException { + guard(frame); + + if (hasInt) { + return intValue; + } else { + throw new UnexpectedResultException(value); + } + } + + @Override + public double executeFloat(VirtualFrame frame) throws UnexpectedResultException { + guard(frame); + + if (hasDouble) { + return doubleValue; + } else { + throw new UnexpectedResultException(value); + } + } + + @Override + public void executeVoid(VirtualFrame frame) { + } + + public void guard(VirtualFrame frame) throws UnexpectedResultException { + final RubyContext context = getContext(); + + final Object receiverObject = receiver.execute(frame); + + RubyBasicObject receiverRubyObject; + + // TODO(CS): put the boxing into a separate node that can specialize for each type it sees + + if (receiverObject instanceof RubyBasicObject) { + receiverRubyObject = (RubyBasicObject) receiverObject; + } else { + boxBranchProfile.enter(); + receiverRubyObject = context.getCoreLibrary().box(receiverObject); + } + + if (receiverRubyObject.getRubyClass() != expectedClass) { + CompilerDirectives.transferToInterpreter(); + throw new UnexpectedResultException(uninitialize(receiverRubyObject)); + } + + try { + unmodifiedAssumption.check(); + } catch (InvalidAssumptionException e) { + throw new UnexpectedResultException(uninitialize(receiverRubyObject)); + } + } + + private Object uninitialize(RubyBasicObject receiverObject) { + return replace(new UninitializedReadConstantNode(getContext(), getSourceSection(), name, receiver)).execute(receiverObject); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/constants/ReadConstantNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/constants/ReadConstantNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.constants; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; + +public abstract class ReadConstantNode extends RubyNode { + + protected final String name; + @Child protected RubyNode receiver; + + public ReadConstantNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver) { + super(context, sourceSection); + this.name = name; + this.receiver = adoptChild(receiver); + } + + @Override + public Object isDefined(VirtualFrame frame) { + final RubyContext context = getContext(); + + if (name.equals("Encoding")) { + /* + * Work-around so I don't have to load the iconv library - runners/formatters/junit.rb. + */ + return context.makeString("constant"); + } + + Object value; + + try { + value = context.getCoreLibrary().box(receiver.execute(frame)).getLookupNode().lookupConstant(name); + } catch (RaiseException e) { + /* + * If we are looking up a constant in a constant that is itself undefined, we return Nil + * rather than raising the error. Eg.. defined?(Defined::Undefined1::Undefined2) + */ + + if (e.getRubyException().getRubyClass() == context.getCoreLibrary().getNameErrorClass()) { + return NilPlaceholder.INSTANCE; + } + + throw e; + } + + if (value == null) { + return NilPlaceholder.INSTANCE; + } else { + return context.makeString("constant"); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/constants/UninitializedReadConstantNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/constants/UninitializedReadConstantNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.constants; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents an uninitialized constant read from some object. After the first read it will be + * specialized to some other node. This is the starting point for all constant reads. + */ +@NodeInfo(shortName = "uninitialized-read-constant") +public class UninitializedReadConstantNode extends ReadConstantNode { + + public UninitializedReadConstantNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver) { + super(context, sourceSection, name, receiver); + } + + /** + * This execute method allows us to pass in the already executed receiver object, so that during + * uninitialization it is not executed once by the specialized node and again by this node. + */ + public Object execute(RubyBasicObject receiverObject) { + CompilerAsserts.neverPartOfCompilation(); + + final RubyContext context = receiverObject.getRubyClass().getContext(); + + Object value; + + value = receiverObject.getLookupNode().lookupConstant(name); + + if (value == null && receiverObject instanceof RubyModule) { + /* + * FIXME(CS): I'm obviously doing something wrong with constant lookup in nested modules + * here, but explicitly looking in the Module itself, not its lookup node, seems to fix + * it for now. + */ + + value = ((RubyModule) receiverObject).lookupConstant(name); + } + + if (value == null) { + throw new RaiseException(context.getCoreLibrary().nameErrorUninitializedConstant(name)); + } + + replace(new CachedReadConstantNode(context, getSourceSection(), name, receiver, receiverObject.getRubyClass(), value)); + + assert RubyContext.shouldObjectBeVisible(value); + + return value; + } + + @Override + public Object execute(VirtualFrame frame) { + CompilerDirectives.transferToInterpreter(); + + final RubyContext context = getContext(); + + final Object receiverObject = receiver.execute(frame); + final RubyBasicObject receiverRubyObject = context.getCoreLibrary().box(receiverObject); + + return execute(receiverRubyObject); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/constants/WriteConstantNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/constants/WriteConstantNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.constants; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Represents writing a constant into some module. + */ +@NodeInfo(shortName = "write-constant") +public class WriteConstantNode extends RubyNode { + + private final String name; + @Child protected RubyNode module; + @Child protected RubyNode rhs; + + public WriteConstantNode(RubyContext context, SourceSection sourceSection, String name, RubyNode module, RubyNode rhs) { + super(context, sourceSection); + this.name = name; + this.module = adoptChild(module); + this.rhs = adoptChild(rhs); + } + + @Override + public Object execute(VirtualFrame frame) { + // TODO(cs): can module ever not evaluate to a RubyModule? + + final RubyModule moduleObject = (RubyModule) module.execute(frame); + + final Object rhsValue = rhs.execute(frame); + + assert rhsValue != null; + assert !(rhsValue instanceof String); + + moduleObject.setModuleConstant(name, rhsValue); + + return rhsValue; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/AndNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/AndNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Represents a Ruby {@code and} or {@code &&} expression. + */ +@NodeInfo(shortName = "and") +@NodeChildren({@NodeChild("left"), @NodeChild("right")}) +public abstract class AndNode extends RubyNode { + + public AndNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AndNode(AndNode copy) { + super(copy.getContext(), copy.getSourceSection()); + } + + @ShortCircuit("right") + public boolean needsRightNode(Object a) { + return GeneralConversions.toBoolean(a); + } + + @ShortCircuit("right") + public boolean needsRightNode(boolean a) { + return a; + } + + @Specialization + public boolean doBoolean(boolean a, boolean hasB, boolean b) { + return hasB ? b : a; + } + + @Specialization + public Object doObject(boolean a, boolean hasB, Object b) { + return hasB ? b : a; + } + + @Generic + public Object doGeneric(Object a, boolean hasB, Object b) { + return hasB ? b : a; + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/BreakNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/BreakNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; + +@NodeInfo(shortName = "break") +public class BreakNode extends RubyNode { + + @Child private RubyNode child; + + public BreakNode(RubyContext context, SourceSection sourceSection, RubyNode child) { + super(context, sourceSection); + this.child = adoptChild(child); + } + + @Override + public Object execute(VirtualFrame frame) { + throw new BreakException(child.execute(frame)); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/ElidableResultNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/ElidableResultNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * This node has a pair of children - one required and one elidable result. The required node is + * always executed, but its result is discarded. Therefore it should perform some useful side + * effects. The elidable node is executed, and its result value returned, if an execute method with + * a non-void type is used. It is not executed at all if a void typed execute method is used. + * Therefore it should not perform any observable side effects. + */ +@NodeInfo(shortName = "elidable-result") +public class ElidableResultNode extends RubyNode { + + @Child protected RubyNode required; + @Child protected RubyNode elidableResult; + + public ElidableResultNode(RubyContext context, SourceSection sourceSection, RubyNode required, RubyNode elidableResult) { + super(context, sourceSection); + this.required = adoptChild(required); + this.elidableResult = adoptChild(elidableResult); + } + + @Override + public int executeFixnum(VirtualFrame frame) throws UnexpectedResultException { + required.executeVoid(frame); + return elidableResult.executeFixnum(frame); + } + + @Override + public double executeFloat(VirtualFrame frame) throws UnexpectedResultException { + required.executeVoid(frame); + return elidableResult.executeFloat(frame); + } + + @Override + public Object execute(VirtualFrame frame) { + required.executeVoid(frame); + return elidableResult.execute(frame); + } + + @Override + public void executeVoid(VirtualFrame frame) { + required.execute(frame); + } + + @Override + public Object isDefined(VirtualFrame frame) { + return elidableResult.isDefined(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/EnsureNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/EnsureNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Represents an ensure clause in exception handling. Represented separately to the try part. + */ +@NodeInfo(shortName = "ensure") +public class EnsureNode extends RubyNode { + + @Child protected RubyNode tryPart; + @Child protected RubyNode ensurePart; + + public EnsureNode(RubyContext context, SourceSection sourceSection, RubyNode tryPart, RubyNode ensurePart) { + super(context, sourceSection); + this.tryPart = adoptChild(tryPart); + this.ensurePart = adoptChild(ensurePart); + } + + @Override + public Object execute(VirtualFrame frame) { + try { + return tryPart.execute(frame); + } finally { + ensurePart.executeVoid(frame); + } + } + + @Override + public void executeVoid(VirtualFrame frame) { + try { + tryPart.executeVoid(frame); + } finally { + ensurePart.executeVoid(frame); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/FlipFlopNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/FlipFlopNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.cast.*; +import com.oracle.truffle.ruby.nodes.methods.locals.*; +import com.oracle.truffle.ruby.runtime.*; + +@NodeInfo(shortName = "flip-flop") +public class FlipFlopNode extends RubyNode { + + @Child protected BooleanCastNode begin; + @Child protected BooleanCastNode end; + @Child protected FlipFlopStateNode stateNode; + + private final boolean exclusive; + + public FlipFlopNode(RubyContext context, SourceSection sourceSection, BooleanCastNode begin, BooleanCastNode end, FlipFlopStateNode stateNode, boolean exclusive) { + super(context, sourceSection); + this.begin = adoptChild(begin); + this.end = adoptChild(end); + this.stateNode = adoptChild(stateNode); + this.exclusive = exclusive; + } + + @Override + public boolean executeBoolean(VirtualFrame frame) { + if (exclusive) { + if (stateNode.getState(frame)) { + if (end.executeBoolean(frame)) { + stateNode.setState(frame, false); + } + + return true; + } else { + final boolean newState = begin.executeBoolean(frame); + stateNode.setState(frame, newState); + return newState; + } + } else { + if (stateNode.getState(frame)) { + if (end.executeBoolean(frame)) { + stateNode.setState(frame, false); + } + + return true; + } else { + if (begin.executeBoolean(frame)) { + stateNode.setState(frame, !end.executeBoolean(frame)); + return true; + } + + return false; + } + } + } + + @Override + public Object execute(VirtualFrame frame) { + return executeBoolean(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/IfNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/IfNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.cast.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Represents a Ruby {@code if} expression. Note that in this representation we always have an + * {@code else} part. + */ +@NodeInfo(shortName = "if") +public class IfNode extends RubyNode { + + @Child protected BooleanCastNode condition; + @Child protected RubyNode thenBody; + @Child protected RubyNode elseBody; + + private final BranchProfile thenProfile = new BranchProfile(); + private final BranchProfile elseProfile = new BranchProfile(); + + public IfNode(RubyContext context, SourceSection sourceSection, BooleanCastNode condition, RubyNode thenBody, RubyNode elseBody) { + super(context, sourceSection); + this.condition = adoptChild(condition); + this.thenBody = adoptChild(thenBody); + this.elseBody = adoptChild(elseBody); + } + + @Override + public Object execute(VirtualFrame frame) { + if (condition.executeBoolean(frame)) { + thenProfile.enter(); + return thenBody.execute(frame); + } else { + elseProfile.enter(); + return elseBody.execute(frame); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/NextNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/NextNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; + +@NodeInfo(shortName = "next") +public class NextNode extends RubyNode { + + public NextNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + @Override + public Object execute(VirtualFrame frame) { + throw new NextException(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/NotNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/NotNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.cast.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Represents a Ruby {@code not} or {@code !} expression. + */ +@NodeInfo(shortName = "not") +public class NotNode extends RubyNode { + + @Child protected BooleanCastNode child; + + public NotNode(RubyContext context, SourceSection sourceSection, BooleanCastNode child) { + super(context, sourceSection); + this.child = adoptChild(child); + } + + @Override + public boolean executeBoolean(VirtualFrame frame) { + return !child.executeBoolean(frame); + } + + @Override + public Object execute(VirtualFrame frame) { + return executeBoolean(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/OrNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/OrNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Represents a Ruby {@code or} or {@code ||} expression. + */ +@NodeInfo(shortName = "or") +@NodeChildren({@NodeChild("left"), @NodeChild("right")}) +public abstract class OrNode extends RubyNode { + + public OrNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public OrNode(OrNode copy) { + super(copy.getContext(), copy.getSourceSection()); + } + + @ShortCircuit("right") + public boolean needsRightNode(Object a) { + return !GeneralConversions.toBoolean(a); + } + + @ShortCircuit("right") + public boolean needsRightNode(boolean a) { + return !a; + } + + @Specialization + public Object doBoolean(boolean a, boolean hasB, Object b) { + return hasB ? b : a; + } + + @Generic + public Object doGeneric(Object a, boolean hasB, Object b) { + return hasB ? b : a; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RedoNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RedoNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; + +@NodeInfo(shortName = "redo") +public class RedoNode extends RubyNode { + + public RedoNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + @Override + public Object execute(VirtualFrame frame) { + throw new RedoException(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RescueAnyNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RescueAnyNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Rescues any exception. + */ +@NodeInfo(shortName = "rescue-any") +public class RescueAnyNode extends RescueNode { + + public RescueAnyNode(RubyContext context, SourceSection sourceSection, RubyNode body) { + super(context, sourceSection, body); + } + + @Override + public boolean canHandle(VirtualFrame frame, RubyBasicObject exception) { + return true; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RescueClassesNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RescueClassesNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Rescues any of a set of classes. + */ +@NodeInfo(shortName = "rescue-classes") +public class RescueClassesNode extends RescueNode { + + @Children final RubyNode[] handlingClassNodes; + + public RescueClassesNode(RubyContext context, SourceSection sourceSection, RubyNode[] handlingClassNodes, RubyNode body) { + super(context, sourceSection, body); + this.handlingClassNodes = adoptChildren(handlingClassNodes); + } + + @ExplodeLoop + @Override + public boolean canHandle(VirtualFrame frame, RubyBasicObject exception) { + final RubyClass exceptionRubyClass = exception.getRubyClass(); + + for (RubyNode handlingClassNode : handlingClassNodes) { + // TODO(CS): what if we don't get a class? + + final RubyClass handlingClass = (RubyClass) handlingClassNode.execute(frame); + + if (exceptionRubyClass.assignableTo(handlingClass)) { + return true; + } + } + + return false; + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RescueNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RescueNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Base node for all nodes which may be able to rescue an exception. They have a test method + * {@link #canHandle} and a body to execute if that test passes. + */ +public abstract class RescueNode extends RubyNode { + + @Child protected RubyNode body; + + public RescueNode(RubyContext context, SourceSection sourceSection, RubyNode body) { + super(context, sourceSection); + this.body = adoptChild(body); + } + + public abstract boolean canHandle(VirtualFrame frame, RubyBasicObject exception); + + @Override + public Object execute(VirtualFrame frame) { + return body.execute(frame); + } + + @Override + public void executeVoid(VirtualFrame frame) { + body.executeVoid(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RescueSplatNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RescueSplatNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Rescue any of several classes, that we get from an expression that evaluates to an array of + * classes. + * + */ +@NodeInfo(shortName = "rescue-splat") +public class RescueSplatNode extends RescueNode { + + @Child RubyNode handlingClassesArray; + + public RescueSplatNode(RubyContext context, SourceSection sourceSection, RubyNode handlingClassesArray, RubyNode body) { + super(context, sourceSection, body); + this.handlingClassesArray = adoptChild(handlingClassesArray); + } + + @ExplodeLoop + @Override + public boolean canHandle(VirtualFrame frame, RubyBasicObject exception) { + final RubyArray handlingClasses = (RubyArray) handlingClassesArray.execute(frame); + + final RubyClass exceptionRubyClass = exception.getRubyClass(); + + for (Object handlingClass : handlingClasses.asList()) { + if (exceptionRubyClass.assignableTo((RubyClass) handlingClass)) { + return true; + } + } + + return false; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RetryNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/RetryNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; + +@NodeInfo(shortName = "retry") +public class RetryNode extends RubyNode { + + public RetryNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + @Override + public Object execute(VirtualFrame frame) { + throw new RetryException(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/ReturnNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/ReturnNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; + +/** + * Represents an explicit return. The return ID indicates where we should be returning to - this can + * be non-trivial if you have blocks. + */ +@NodeInfo(shortName = "return") +public class ReturnNode extends RubyNode { + + private final long returnID; + @Child protected RubyNode value; + + public ReturnNode(RubyContext context, SourceSection sourceSection, long returnID, RubyNode value) { + super(context, sourceSection); + this.returnID = returnID; + this.value = adoptChild(value); + } + + @Override + public Object execute(VirtualFrame frame) { + throw new ReturnException(returnID, value.execute(frame)); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/SequenceNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/SequenceNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A sequence of statements to be executed in serial. + */ +@NodeInfo(shortName = "sequence") +public final class SequenceNode extends RubyNode { + + @Children protected final RubyNode[] body; + + public SequenceNode(RubyContext context, SourceSection sourceSection, RubyNode... body) { + super(context, sourceSection); + this.body = adoptChildren(body); + } + + @ExplodeLoop + @Override + public Object execute(VirtualFrame frame) { + for (int n = 0; n < body.length - 1; n++) { + body[n].executeVoid(frame); + } + + return body[body.length - 1].execute(frame); + } + + @ExplodeLoop + @Override + public void executeVoid(VirtualFrame frame) { + for (int n = 0; n < body.length; n++) { + body[n].executeVoid(frame); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/TryNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/TryNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents a block of code run with exception handlers. There's no {@code try} keyword in Ruby - + * it's implicit - but it's similar to a try statement in any other language. + */ +@NodeInfo(shortName = "try") +public class TryNode extends RubyNode { + + @Child protected RubyNode tryPart; + @Children final RescueNode[] rescueParts; + @Child protected RubyNode elsePart; + + private final BranchProfile controlFlowProfile = new BranchProfile(); + + public TryNode(RubyContext context, SourceSection sourceSection, RubyNode tryPart, RescueNode[] rescueParts, RubyNode elsePart) { + super(context, sourceSection); + this.tryPart = adoptChild(tryPart); + this.rescueParts = adoptChildren(rescueParts); + this.elsePart = adoptChild(elsePart); + } + + @Override + public Object execute(VirtualFrame frame) { + while (true) { + try { + final Object result = tryPart.execute(frame); + elsePart.executeVoid(frame); + return result; + } catch (ControlFlowException exception) { + controlFlowProfile.enter(); + + throw exception; + } catch (RuntimeException exception) { + CompilerDirectives.transferToInterpreter(); + + try { + return handleException(frame, exception); + } catch (RetryException e) { + continue; + } + } + } + } + + private Object handleException(VirtualFrame frame, RuntimeException exception) { + CompilerAsserts.neverPartOfCompilation(); + + final RubyContext context = getContext(); + + final RubyBasicObject rubyException = ExceptionTranslator.translateException(context, exception); + + context.getCoreLibrary().getGlobalVariablesObject().setInstanceVariable("$!", rubyException); + + for (RescueNode rescue : rescueParts) { + if (rescue.canHandle(frame, rubyException)) { + return rescue.execute(frame); + } + } + + throw exception; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/WhileNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/control/WhileNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.cast.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; + +/** + * Represents a Ruby {@code while} statement. + */ +@NodeInfo(shortName = "while") +public class WhileNode extends RubyNode { + + @Child protected BooleanCastNode condition; + @Child protected RubyNode body; + + private final BranchProfile breakProfile = new BranchProfile(); + private final BranchProfile nextProfile = new BranchProfile(); + private final BranchProfile redoProfile = new BranchProfile(); + + public WhileNode(RubyContext context, SourceSection sourceSection, BooleanCastNode condition, RubyNode body) { + super(context, sourceSection); + this.condition = adoptChild(condition); + this.body = adoptChild(body); + } + + @Override + public Object execute(VirtualFrame frame) { + outer: while (condition.executeBoolean(frame)) { + while (true) { + try { + body.execute(frame); + continue outer; + } catch (BreakException e) { + breakProfile.enter(); + return e.getResult(); + } catch (NextException e) { + nextProfile.enter(); + continue outer; + } catch (RedoException e) { + redoProfile.enter(); + } + } + } + + return NilPlaceholder.INSTANCE; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayConcatNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayConcatNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * Concatenate arrays. + */ +@NodeInfo(shortName = "array-concat") +public final class ArrayConcatNode extends RubyNode { + + @Children protected final RubyNode[] children; + + public ArrayConcatNode(RubyContext context, SourceSection sourceSection, RubyNode[] children) { + super(context, sourceSection); + assert children.length > 1; + this.children = adoptChildren(children); + } + + @ExplodeLoop + @Override + public Object execute(VirtualFrame frame) { + final RubyArray array = new RubyArray(getContext().getCoreLibrary().getArrayClass()); + + for (int n = 0; n < children.length; n++) { + final Object childObject = children[n].execute(frame); + + if (childObject instanceof RubyArray) { + // setRangeArray has special cases for setting a zero-length range at the end + final int end = array.size(); + array.setRangeArrayExclusive(end, end, (RubyArray) childObject); + } else { + array.push(childObject); + } + } + + return array; + } + + @ExplodeLoop + @Override + public void executeVoid(VirtualFrame frame) { + for (int n = 0; n < children.length; n++) { + children[n].executeVoid(frame); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayCoreMethodNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayCoreMethodNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +public abstract class ArrayCoreMethodNode extends CoreMethodNode { + + public ArrayCoreMethodNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ArrayCoreMethodNode(ArrayCoreMethodNode prev) { + super(prev); + } + + protected boolean isEmptyStore(RubyArray receiver) { + return receiver.getArrayStore() instanceof EmptyArrayStore; + } + + protected boolean isFixnumStore(RubyArray receiver) { + return receiver.getArrayStore() instanceof FixnumArrayStore; + } + + protected boolean isFixnumImmutablePairStore(RubyArray receiver) { + return receiver.getArrayStore() instanceof FixnumImmutablePairArrayStore; + } + + protected boolean isObjectStore(RubyArray receiver) { + return receiver.getArrayStore() instanceof ObjectArrayStore; + } + + protected boolean isObjectImmutablePairStore(RubyArray receiver) { + return receiver.getArrayStore() instanceof ObjectImmutablePairArrayStore; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayIndexNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayIndexNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * Index an array, without using any method lookup. This isn't a call - it's an operation on a core + * class. + */ +@NodeInfo(shortName = "array-index") +@NodeChildren({@NodeChild(value = "array", type = RubyNode.class)}) +public abstract class ArrayIndexNode extends RubyNode { + + final int index; + + public ArrayIndexNode(RubyContext context, SourceSection sourceSection, int index) { + super(context, sourceSection); + this.index = index; + } + + public ArrayIndexNode(ArrayIndexNode prev) { + super(prev); + index = prev.index; + } + + @Specialization(guards = "isEmptyStore", order = 1) + public NilPlaceholder indexEmpty(@SuppressWarnings("unused") RubyArray array) { + return NilPlaceholder.INSTANCE; + } + + @Specialization(guards = "isFixnumStore", rewriteOn = UnexpectedResultException.class, order = 2) + public int indexFixnum(RubyArray array) throws UnexpectedResultException { + final FixnumArrayStore store = (FixnumArrayStore) array.getArrayStore(); + return store.getFixnum(ArrayUtilities.normaliseIndex(store.size(), index)); + } + + @Specialization(guards = "isFixnumStore", order = 3) + public Object indexMaybeFixnum(RubyArray array) { + final FixnumArrayStore store = (FixnumArrayStore) array.getArrayStore(); + + try { + return store.getFixnum(ArrayUtilities.normaliseIndex(store.size(), index)); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + + @Specialization(guards = "isFixnumImmutablePairStore", rewriteOn = UnexpectedResultException.class, order = 4) + public int indexFixnumImmutablePair(RubyArray array) throws UnexpectedResultException { + final FixnumImmutablePairArrayStore store = (FixnumImmutablePairArrayStore) array.getArrayStore(); + return store.getFixnum(ArrayUtilities.normaliseIndex(store.size(), index)); + } + + @Specialization(guards = "isFixnumImmutablePairStore", order = 5) + public Object indexMaybeFixnumImmutablePair(RubyArray array) { + final FixnumImmutablePairArrayStore store = (FixnumImmutablePairArrayStore) array.getArrayStore(); + + try { + return store.getFixnum(ArrayUtilities.normaliseIndex(store.size(), index)); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + + @Specialization(guards = "isObjectStore", order = 6) + public Object indexObject(RubyArray array) { + final ObjectArrayStore store = (ObjectArrayStore) array.getArrayStore(); + return store.get(ArrayUtilities.normaliseIndex(store.size(), index)); + } + + @Specialization(guards = "isObjectImmutablePairStore", order = 7) + public Object indexObjectImmutablePair(RubyArray array) { + final ObjectImmutablePairArrayStore store = (ObjectImmutablePairArrayStore) array.getArrayStore(); + return store.get(ArrayUtilities.normaliseIndex(store.size(), index)); + } + + protected boolean isEmptyStore(RubyArray receiver) { + return receiver.getArrayStore() instanceof EmptyArrayStore; + } + + protected boolean isFixnumStore(RubyArray receiver) { + return receiver.getArrayStore() instanceof FixnumArrayStore; + } + + protected boolean isFixnumImmutablePairStore(RubyArray receiver) { + return receiver.getArrayStore() instanceof FixnumImmutablePairArrayStore; + } + + protected boolean isObjectStore(RubyArray receiver) { + return receiver.getArrayStore() instanceof ObjectArrayStore; + } + + protected boolean isObjectImmutablePairStore(RubyArray receiver) { + return receiver.getArrayStore() instanceof ObjectImmutablePairArrayStore; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1066 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.util.*; + +import com.oracle.truffle.api.CompilerDirectives.SlowPath; +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.core.range.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@CoreClass(name = "Array") +public abstract class ArrayNodes { + + @CoreMethod(names = "+", minArgs = 1, maxArgs = 1) + public abstract static class AddNode extends CoreMethodNode { + + public AddNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AddNode(AddNode prev) { + super(prev); + } + + @Specialization + public RubyArray equal(RubyArray a, RubyArray b) { + final RubyArray result = new RubyArray(getContext().getCoreLibrary().getArrayClass()); + result.setRangeArrayExclusive(0, 0, a); + result.setRangeArrayExclusive(a.size(), a.size(), b); + return result; + } + + } + + @CoreMethod(names = "-", minArgs = 1, maxArgs = 1) + public abstract static class SubNode extends CoreMethodNode { + + public SubNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SubNode(SubNode prev) { + super(prev); + } + + @Specialization + public RubyArray equal(RubyArray a, RubyArray b) { + return a.relativeComplement(b); + } + + } + + @CoreMethod(names = "*", minArgs = 1, maxArgs = 1) + public abstract static class MulNode extends CoreMethodNode { + + public MulNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MulNode(MulNode prev) { + super(prev); + } + + @Specialization + public RubyArray mul(RubyArray array, int count) { + // TODO(CS): use the same storage type + + final RubyArray result = new RubyArray(array.getRubyClass().getContext().getCoreLibrary().getArrayClass()); + + for (int n = 0; n < count; n++) { + for (int i = 0; i < array.size(); i++) { + result.push(array.get(i)); + } + } + + return result; + } + + } + + @CoreMethod(names = "==", minArgs = 1, maxArgs = 1) + public abstract static class EqualNode extends CoreMethodNode { + + public EqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EqualNode(EqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(RubyArray a, RubyArray b) { + // TODO(CS) + return a.equals(b); + } + + } + + @CoreMethod(names = "[]", minArgs = 1, maxArgs = 2) + public abstract static class IndexNode extends ArrayCoreMethodNode { + + public IndexNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public IndexNode(IndexNode prev) { + super(prev); + } + + @Specialization(guards = "isEmptyStore", order = 1) + public NilPlaceholder indexEmpty(@SuppressWarnings("unused") RubyArray array, @SuppressWarnings("unused") int index, @SuppressWarnings("unused") UndefinedPlaceholder unused) { + return NilPlaceholder.INSTANCE; + } + + @Specialization(guards = "isFixnumStore", rewriteOn = UnexpectedResultException.class, order = 2) + public int indexFixnum(RubyArray array, int index, @SuppressWarnings("unused") UndefinedPlaceholder unused) throws UnexpectedResultException { + final FixnumArrayStore store = (FixnumArrayStore) array.getArrayStore(); + return store.getFixnum(ArrayUtilities.normaliseIndex(store.size(), index)); + } + + @Specialization(guards = "isFixnumStore", order = 3) + public Object indexMaybeFixnum(RubyArray array, int index, @SuppressWarnings("unused") UndefinedPlaceholder unused) { + final FixnumArrayStore store = (FixnumArrayStore) array.getArrayStore(); + + try { + return store.getFixnum(ArrayUtilities.normaliseIndex(store.size(), index)); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + + @Specialization(guards = "isFixnumImmutablePairStore", rewriteOn = UnexpectedResultException.class, order = 4) + public int indexFixnumImmutablePair(RubyArray array, int index, @SuppressWarnings("unused") UndefinedPlaceholder unused) throws UnexpectedResultException { + final FixnumImmutablePairArrayStore store = (FixnumImmutablePairArrayStore) array.getArrayStore(); + return store.getFixnum(ArrayUtilities.normaliseIndex(store.size(), index)); + } + + @Specialization(guards = "isFixnumImmutablePairStore", order = 5) + public Object indexMaybeFixnumImmutablePair(RubyArray array, int index, @SuppressWarnings("unused") UndefinedPlaceholder unused) { + final FixnumImmutablePairArrayStore store = (FixnumImmutablePairArrayStore) array.getArrayStore(); + + try { + return store.getFixnum(ArrayUtilities.normaliseIndex(store.size(), index)); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + + @Specialization(guards = "isObjectStore", order = 6) + public Object indexObject(RubyArray array, int index, @SuppressWarnings("unused") UndefinedPlaceholder unused) { + final ObjectArrayStore store = (ObjectArrayStore) array.getArrayStore(); + return store.get(ArrayUtilities.normaliseIndex(store.size(), index)); + } + + @Specialization(guards = "isObjectImmutablePairStore", order = 7) + public Object indexObjectImmutablePair(RubyArray array, int index, @SuppressWarnings("unused") UndefinedPlaceholder unused) { + final ObjectImmutablePairArrayStore store = (ObjectImmutablePairArrayStore) array.getArrayStore(); + return store.get(ArrayUtilities.normaliseIndex(store.size(), index)); + } + + @Specialization(order = 8) + public Object indexRange(RubyArray array, int begin, int rangeLength) { + final int length = array.size(); + final int normalisedBegin = ArrayUtilities.normaliseIndex(length, begin); + return array.getRangeExclusive(normalisedBegin, normalisedBegin + rangeLength); + } + + @Specialization(order = 9) + public Object indexRange(RubyArray array, FixnumRange range, @SuppressWarnings("unused") UndefinedPlaceholder unused) { + if (range.doesExcludeEnd()) { + return array.getRangeExclusive(range.getBegin(), range.getExclusiveEnd()); + } else { + return array.getRangeInclusive(range.getBegin(), range.getInclusiveEnd()); + } + } + + } + + @CoreMethod(names = "[]=", minArgs = 2, maxArgs = 3) + public abstract static class IndexSetNode extends ArrayCoreMethodNode { + + public IndexSetNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public IndexSetNode(IndexSetNode prev) { + super(prev); + } + + @Specialization(guards = "isEmptyStore", order = 1) + public Object indexSetEmpty(RubyArray array, int index, Object value, @SuppressWarnings("unused") UndefinedPlaceholder unused) { + array.set(index, value); + return value; + } + + @Specialization(guards = "isFixnumStore", rewriteOn = GeneraliseArrayStoreException.class, order = 2) + public int indexSetFixnum(RubyArray array, int index, int value, @SuppressWarnings("unused") UndefinedPlaceholder unused) throws GeneraliseArrayStoreException { + final FixnumArrayStore store = (FixnumArrayStore) array.getArrayStore(); + final int normalisedIndex = ArrayUtilities.normaliseIndex(store.size(), index); + store.setFixnum(normalisedIndex, value); + return value; + } + + @Specialization(guards = "isFixnumStore", order = 3) + public int indexSetFixnumMayGeneralise(RubyArray array, int index, int value, @SuppressWarnings("unused") UndefinedPlaceholder unused) { + final FixnumArrayStore store = (FixnumArrayStore) array.getArrayStore(); + final int normalisedIndex = ArrayUtilities.normaliseIndex(store.size(), index); + + try { + store.setFixnum(normalisedIndex, value); + } catch (GeneraliseArrayStoreException e) { + array.set(normalisedIndex, value); + } + + return value; + } + + @Specialization(guards = "isObjectStore", order = 4) + public Object indexSetObject(RubyArray array, int index, Object value, @SuppressWarnings("unused") UndefinedPlaceholder unused) { + final ObjectArrayStore store = (ObjectArrayStore) array.getArrayStore(); + final int normalisedIndex = ArrayUtilities.normaliseIndex(store.size(), index); + + try { + store.set(normalisedIndex, value); + } catch (GeneraliseArrayStoreException e) { + array.set(normalisedIndex, value); + } + + return value; + } + + @Specialization(order = 5) + public RubyArray indexSetRange(RubyArray array, FixnumRange range, RubyArray value, @SuppressWarnings("unused") UndefinedPlaceholder unused) { + if (range.doesExcludeEnd()) { + array.setRangeArrayExclusive(range.getBegin(), range.getExclusiveEnd(), value); + } else { + array.setRangeArrayInclusive(range.getBegin(), range.getInclusiveEnd(), value); + } + + return value; + } + + @Specialization(order = 6) + public Object indexSetRange(RubyArray array, FixnumRange range, Object value, @SuppressWarnings("unused") UndefinedPlaceholder unused) { + if (range.doesExcludeEnd()) { + array.setRangeSingleExclusive(range.getBegin(), range.getExclusiveEnd(), value); + } else { + array.setRangeSingleInclusive(range.getBegin(), range.getInclusiveEnd(), value); + } + + return value; + } + + @Specialization(order = 7) + public RubyArray indexSetRange(RubyArray array, int begin, int rangeLength, RubyArray value) { + array.setRangeArrayExclusive(begin, begin + rangeLength, value); + return value; + } + + @Specialization(order = 8) + public Object indexSetRange(RubyArray array, int begin, int rangeLength, Object value) { + if (value instanceof UndefinedPlaceholder) { + if (array.getArrayStore() instanceof EmptyArrayStore) { + return indexSetEmpty(array, begin, rangeLength, UndefinedPlaceholder.INSTANCE); + } else if (array.getArrayStore() instanceof FixnumArrayStore) { + return indexSetFixnumMayGeneralise(array, begin, rangeLength, UndefinedPlaceholder.INSTANCE); + } else { + return indexSetObject(array, begin, rangeLength, UndefinedPlaceholder.INSTANCE); + } + } + + array.setRangeSingleExclusive(begin, begin + rangeLength, value); + return value; + } + + } + + @CoreMethod(names = "all?", needsBlock = true, maxArgs = 0) + public abstract static class AllNode extends YieldingCoreMethodNode { + + public AllNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AllNode(AllNode prev) { + super(prev); + } + + @Specialization + public boolean all(VirtualFrame frame, RubyArray array, RubyProc block) { + for (Object value : array.asList()) { + if (!yieldBoolean(frame, block, value)) { + return false; + } + } + + return true; + } + + } + + @CoreMethod(names = "any?", needsBlock = true, maxArgs = 0) + public abstract static class AnyNode extends YieldingCoreMethodNode { + + public AnyNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AnyNode(AnyNode prev) { + super(prev); + } + + @Specialization + public boolean any(VirtualFrame frame, RubyArray array, RubyProc block) { + for (Object value : array.asList()) { + if (yieldBoolean(frame, block, value)) { + return true; + } + } + + return false; + } + + } + + @CoreMethod(names = "compact", maxArgs = 0) + public abstract static class CompactNode extends ArrayCoreMethodNode { + + public CompactNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public CompactNode(CompactNode prev) { + super(prev); + } + + @Specialization + public RubyArray compat(RubyArray array) { + final RubyArray result = new RubyArray(array.getRubyClass().getContext().getCoreLibrary().getArrayClass()); + + for (Object value : array.asList()) { + if (!(value instanceof NilPlaceholder || value instanceof RubyNilClass)) { + result.push(value); + } + } + + return result; + } + + } + + @CoreMethod(names = "concat", minArgs = 1, maxArgs = 1) + public abstract static class ConcatNode extends CoreMethodNode { + + public ConcatNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ConcatNode(ConcatNode prev) { + super(prev); + } + + @Specialization + public RubyArray concat(RubyArray array, RubyArray other) { + array.setRangeArrayExclusive(array.size(), array.size(), other); + return array; + } + + } + + @CoreMethod(names = "delete", minArgs = 1, maxArgs = 1) + public abstract static class DeleteNode extends CoreMethodNode { + + public DeleteNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DeleteNode(DeleteNode prev) { + super(prev); + } + + @Specialization + public Object delete(RubyArray array, Object value) { + boolean deleted = false; + int n = 0; + + while (n < array.size()) { + if (array.get(n) == value) { + array.deleteAt(n); + deleted = true; + } else { + n++; + } + } + + if (deleted) { + return value; + } else { + return NilPlaceholder.INSTANCE; + } + } + + } + + @CoreMethod(names = "delete_at", minArgs = 1, maxArgs = 1) + public abstract static class DeleteAtNode extends CoreMethodNode { + + public DeleteAtNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DeleteAtNode(DeleteAtNode prev) { + super(prev); + } + + @Specialization + public Object deleteAt(RubyArray array, int index) { + return array.deleteAt(index); + } + + } + + @CoreMethod(names = "dup", maxArgs = 0) + public abstract static class DupNode extends ArrayCoreMethodNode { + + public DupNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DupNode(DupNode prev) { + super(prev); + } + + @Specialization + public Object dup(RubyArray array) { + return array.dup(); + } + + } + + @CoreMethod(names = "each", needsBlock = true, maxArgs = 0) + public abstract static class EachNode extends YieldingCoreMethodNode { + + public EachNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EachNode(EachNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder each(VirtualFrame frame, RubyArray array, RubyProc block) { + for (int n = 0; n < array.size(); n++) { + try { + yield(frame, block, array.get(n)); + } catch (BreakException e) { + break; + } + } + + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "each_with_index", needsBlock = true, maxArgs = 0) + public abstract static class EachWithIndexNode extends YieldingCoreMethodNode { + + public EachWithIndexNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EachWithIndexNode(EachWithIndexNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder eachWithIndex(VirtualFrame frame, RubyArray array, RubyProc block) { + for (int n = 0; n < array.size(); n++) { + try { + yield(frame, block, array.get(n), n); + } catch (BreakException e) { + break; + } + } + + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "empty?", maxArgs = 0) + public abstract static class EmptyNode extends ArrayCoreMethodNode { + + public EmptyNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EmptyNode(EmptyNode prev) { + super(prev); + } + + @Specialization + public boolean isEmpty(RubyArray array) { + return array.isEmpty(); + } + + } + + @CoreMethod(names = "find", needsBlock = true, maxArgs = 0) + public abstract static class FindNode extends YieldingCoreMethodNode { + + public FindNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public FindNode(FindNode prev) { + super(prev); + } + + @Specialization + public Object find(VirtualFrame frame, RubyArray array, RubyProc block) { + for (int n = 0; n < array.size(); n++) { + try { + final Object value = array.get(n); + + if (yieldBoolean(frame, block, value)) { + return value; + } + } catch (BreakException e) { + break; + } + } + + return NilPlaceholder.INSTANCE; + } + } + + @CoreMethod(names = "first", maxArgs = 0) + public abstract static class FirstNode extends ArrayCoreMethodNode { + + public FirstNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public FirstNode(FirstNode prev) { + super(prev); + } + + @Specialization + public Object first(RubyArray array) { + if (array.size() == 0) { + return NilPlaceholder.INSTANCE; + } else { + return array.get(0); + } + } + + } + + @CoreMethod(names = "flatten", maxArgs = 0) + public abstract static class FlattenNode extends ArrayCoreMethodNode { + + public FlattenNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public FlattenNode(FlattenNode prev) { + super(prev); + } + + @Specialization + public RubyArray flatten(RubyArray array) { + final RubyArray result = new RubyArray(array.getRubyClass().getContext().getCoreLibrary().getArrayClass()); + array.flattenTo(result); + return result; + } + + } + + @CoreMethod(names = "include?", minArgs = 1, maxArgs = 1) + public abstract static class IncludeNode extends ArrayCoreMethodNode { + + public IncludeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public IncludeNode(IncludeNode prev) { + super(prev); + } + + @Specialization + public boolean include(RubyArray array, Object value) { + return array.contains(value); + } + + } + + @CoreMethod(names = {"inject", "reduce"}, needsBlock = true, minArgs = 0, maxArgs = 1) + public abstract static class InjectNode extends YieldingCoreMethodNode { + + public InjectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InjectNode(InjectNode prev) { + super(prev); + } + + @Specialization + public Object inject(VirtualFrame frame, RubyArray array, @SuppressWarnings("unused") UndefinedPlaceholder initial, RubyProc block) { + Object accumulator = array.get(0); + + for (int n = 1; n < array.size(); n++) { + accumulator = yield(frame, block, accumulator, array.get(n)); + } + + return accumulator; + } + + @Specialization + public Object inject(VirtualFrame frame, RubyArray array, Object initial, RubyProc block) { + if (initial instanceof UndefinedPlaceholder) { + return inject(frame, array, UndefinedPlaceholder.INSTANCE, block); + } + + Object accumulator = initial; + + for (int n = 0; n < array.size(); n++) { + accumulator = yield(frame, block, accumulator, array.get(n)); + } + + return accumulator; + } + + } + + @CoreMethod(names = "insert", minArgs = 2, maxArgs = 2) + public abstract static class InsertNode extends ArrayCoreMethodNode { + + public InsertNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InsertNode(InsertNode prev) { + super(prev); + } + + @Specialization(guards = "isFixnumStore", rewriteOn = GeneraliseArrayStoreException.class) + public int insert(RubyArray array, int index, int value) throws GeneraliseArrayStoreException { + final FixnumArrayStore store = (FixnumArrayStore) array.getArrayStore(); + store.insertFixnum(ArrayUtilities.normaliseIndex(store.size(), index), value); + return value; + } + + @Specialization + public Object insert(RubyArray array, int index, Object value) { + array.insert(ArrayUtilities.normaliseIndex(array.size(), index), value); + return value; + } + + } + + @CoreMethod(names = {"inspect", "to_s"}, maxArgs = 0) + public abstract static class InspectNode extends CoreMethodNode { + + public InspectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InspectNode(InspectNode prev) { + super(prev); + } + + @Specialization + public RubyString inspect(RubyArray array) { + final RubyContext context = getContext(); + return getContext().makeString(inspect(context, array)); + } + + @SlowPath + private static String inspect(RubyContext context, RubyArray array) { + final StringBuilder builder = new StringBuilder(); + + builder.append("["); + + for (int n = 0; n < array.size(); n++) { + if (n > 0) { + builder.append(", "); + } + + // TODO(CS): slow path send + builder.append(context.getCoreLibrary().box(array.get(n)).send("inspect", null)); + } + + builder.append("]"); + + return builder.toString(); + } + + } + + @CoreMethod(names = "join", minArgs = 1, maxArgs = 1) + public abstract static class JoinNode extends ArrayCoreMethodNode { + + public JoinNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public JoinNode(JoinNode prev) { + super(prev); + } + + @Specialization + public RubyString join(RubyArray array, RubyString separator) { + return array.getRubyClass().getContext().makeString(array.join(separator.toString())); + } + + } + + @CoreMethod(names = "last", maxArgs = 0) + public abstract static class LastNode extends ArrayCoreMethodNode { + + public LastNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LastNode(LastNode prev) { + super(prev); + } + + @Specialization + public Object last(RubyArray array) { + final int size = array.size(); + if (size == 0) { + return NilPlaceholder.INSTANCE; + } else { + return array.get(size - 1); + } + } + + } + + @CoreMethod(names = {"map", "collect"}, needsBlock = true, maxArgs = 0) + public abstract static class MapNode extends YieldingCoreMethodNode { + + public MapNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MapNode(MapNode prev) { + super(prev); + } + + @Specialization + public RubyArray map(VirtualFrame frame, RubyArray array, RubyProc block) { + final RubyArray result = new RubyArray(array.getRubyClass().getContext().getCoreLibrary().getArrayClass()); + + for (int n = 0; n < array.size(); n++) { + result.push(yield(frame, block, array.get(n))); + } + + return result; + } + } + + @CoreMethod(names = {"map!", "collect!"}, needsBlock = true, maxArgs = 0) + public abstract static class MapInPlaceNode extends YieldingCoreMethodNode { + + public MapInPlaceNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MapInPlaceNode(MapInPlaceNode prev) { + super(prev); + } + + @Specialization + public RubyArray mapInPlace(VirtualFrame frame, RubyArray array, RubyProc block) { + for (int n = 0; n < array.size(); n++) { + array.set(n, yield(frame, block, array.get(n))); + } + + return array; + } + } + + @CoreMethod(names = "pop", maxArgs = 0) + public abstract static class PopNode extends ArrayCoreMethodNode { + + public PopNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PopNode(PopNode prev) { + super(prev); + } + + @Specialization + public Object pop(RubyArray array) { + final int size = array.size(); + + if (size == 0) { + return NilPlaceholder.INSTANCE; + } else { + return array.deleteAt(size - 1); + } + } + + } + + @CoreMethod(names = "product", isSplatted = true) + public abstract static class ProductNode extends ArrayCoreMethodNode { + + public ProductNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ProductNode(ProductNode prev) { + super(prev); + } + + @Specialization + public Object product(RubyArray array, Object... args) { + final RubyArray[] arrays = new RubyArray[1 + args.length]; + arrays[0] = array; + System.arraycopy(args, 0, arrays, 1, args.length); + return RubyArray.product(array.getRubyClass().getContext().getCoreLibrary().getArrayClass(), arrays, arrays.length); + } + + } + + @CoreMethod(names = {"push", "<<"}, isSplatted = true) + public abstract static class PushNode extends CoreMethodNode { + + public PushNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PushNode(PushNode prev) { + super(prev); + } + + @Specialization + public RubyArray push(RubyArray array, Object... args) { + for (int n = 0; n < args.length; n++) { + array.push(args[n]); + } + + return array; + } + + } + + @CoreMethod(names = "reject!", needsBlock = true, maxArgs = 0) + public abstract static class RejectInPlaceNode extends YieldingCoreMethodNode { + + public RejectInPlaceNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public RejectInPlaceNode(RejectInPlaceNode prev) { + super(prev); + } + + @Specialization + public Object rejectInPlace(VirtualFrame frame, RubyArray array, RubyProc block) { + boolean modified = false; + int n = 0; + + while (n < array.size()) { + if (yieldBoolean(frame, block, array.get(n))) { + array.deleteAt(n); + modified = true; + } else { + n++; + } + } + + if (modified) { + return array; + } else { + return NilPlaceholder.INSTANCE; + } + } + + } + + @CoreMethod(names = "select", needsBlock = true, maxArgs = 0) + public abstract static class SelectNode extends YieldingCoreMethodNode { + + public SelectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SelectNode(SelectNode prev) { + super(prev); + } + + @Specialization + public Object select(VirtualFrame frame, RubyArray array, RubyProc block) { + final RubyArray result = new RubyArray(getContext().getCoreLibrary().getArrayClass()); + + for (int n = 0; n < array.size(); n++) { + final Object value = array.get(n); + + if (yieldBoolean(frame, block, value)) { + result.push(value); + } + } + + return result; + } + + } + + @CoreMethod(names = "shift", maxArgs = 0) + public abstract static class ShiftNode extends CoreMethodNode { + + public ShiftNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ShiftNode(ShiftNode prev) { + super(prev); + } + + @Specialization + public Object shift(RubyArray array) { + final int size = array.size(); + + if (size == 0) { + return NilPlaceholder.INSTANCE; + } else { + return array.deleteAt(0); + } + } + + } + + @CoreMethod(names = {"size", "length"}, maxArgs = 0) + public abstract static class SizeNode extends ArrayCoreMethodNode { + + public SizeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SizeNode(SizeNode prev) { + super(prev); + } + + @Specialization + public int size(RubyArray array) { + return array.size(); + } + + } + + @CoreMethod(names = "sort", maxArgs = 0) + public abstract static class SortNode extends CoreMethodNode { + + public SortNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SortNode(SortNode prev) { + super(prev); + } + + @Specialization + public RubyArray sort(RubyArray array) { + final RubyContext context = array.getRubyClass().getContext(); + + final Object[] objects = array.asList().toArray(); + + Arrays.sort(objects, new Comparator() { + + @Override + public int compare(Object a, Object b) { + final RubyBasicObject aBoxed = context.getCoreLibrary().box(a); + return (int) aBoxed.getLookupNode().lookupMethod("<=>").call(null, aBoxed, null, b); + } + + }); + + return RubyArray.specializedFromObjects(context.getCoreLibrary().getArrayClass(), objects); + } + + } + + @CoreMethod(names = "unshift", isSplatted = true) + public abstract static class UnshiftNode extends CoreMethodNode { + + public UnshiftNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public UnshiftNode(UnshiftNode prev) { + super(prev); + } + + @Specialization + public Object unshift(RubyArray array, Object... args) { + for (int n = 0; n < args.length; n++) { + array.unshift(args[n]); + } + + return array; + } + + } + + @CoreMethod(names = "zip", isSplatted = true) + public abstract static class ZipNode extends CoreMethodNode { + + public ZipNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ZipNode(ZipNode prev) { + super(prev); + } + + @Specialization + public RubyArray zip(RubyArray array, Object... args) { + final RubyContext context = getContext(); + final RubyClass arrayClass = context.getCoreLibrary().getArrayClass(); + + final RubyArray result = new RubyArray(arrayClass); + + for (int n = 0; n < array.size(); n++) { + final RubyArray tuple = new RubyArray(arrayClass); + + tuple.push(array.get(n)); + + for (Object arg : args) { + final RubyArray argArray = (RubyArray) arg; + tuple.push(argArray.get(n)); + } + + result.push(tuple); + } + + return result; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayPushNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayPushNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +public class ArrayPushNode extends RubyNode { + + @Child protected RubyNode array; + @Child protected RubyNode pushed; + + public ArrayPushNode(RubyContext context, SourceSection sourceSection, RubyNode array, RubyNode pushed) { + super(context, sourceSection); + this.array = adoptChild(array); + this.pushed = adoptChild(pushed); + } + + @Override + public Object execute(VirtualFrame frame) { + RubyArray a = (RubyArray) array.execute(frame); + a = (RubyArray) a.dup(); + a.push(pushed.execute(frame)); + return a; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayRestNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ArrayRestNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * Take the rest of values in an array after an index, without using any method lookup. This isn't a + * call - it's an operation on a core class. + */ +@NodeInfo(shortName = "array-rest") +public final class ArrayRestNode extends RubyNode { + + final int begin; + @Child protected RubyNode array; + + public ArrayRestNode(RubyContext context, SourceSection sourceSection, int begin, RubyNode array) { + super(context, sourceSection); + this.begin = begin; + this.array = adoptChild(array); + } + + @Override + public Object execute(VirtualFrame frame) { + final RubyArray arrayObject = (RubyArray) array.execute(frame); + return arrayObject.getRangeExclusive(begin, arrayObject.size()); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/BasicObjectNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/BasicObjectNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.math.*; +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@CoreClass(name = "BasicObject") +public abstract class BasicObjectNodes { + + @CoreMethod(names = "!", needsSelf = false, maxArgs = 0) + public abstract static class NotNode extends CoreMethodNode { + + public NotNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NotNode(NotNode prev) { + super(prev); + } + + @Specialization + public boolean not() { + return false; + } + + } + + @CoreMethod(names = "==", minArgs = 1, maxArgs = 1) + public abstract static class EqualNode extends CoreMethodNode { + + public EqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EqualNode(EqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(Object a, Object b) { + // TODO(CS) ideally all classes would do this in their own nodes + return a.equals(b); + } + + } + + @CoreMethod(names = "!=", minArgs = 1, maxArgs = 1) + public abstract static class NotEqualNode extends CoreMethodNode { + + public NotEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NotEqualNode(NotEqualNode prev) { + super(prev); + } + + @Specialization + public boolean notEqual(Object a, Object b) { + // TODO(CS) ideally all classes would do this in their own nodes + return !a.equals(b); + } + + } + + @CoreMethod(names = "equal?", minArgs = 1, maxArgs = 1) + public abstract static class ReferenceEqualNode extends CoreMethodNode { + + public ReferenceEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ReferenceEqualNode(ReferenceEqualNode prev) { + super(prev); + } + + @Specialization(order = 1) + public boolean equal(@SuppressWarnings("unused") NilPlaceholder a, @SuppressWarnings("unused") NilPlaceholder b) { + return true; + } + + @Specialization(order = 2) + public boolean equal(int a, int b) { + return a == b; + } + + @Specialization(order = 3) + public boolean equal(double a, double b) { + return a == b; + } + + @Specialization(order = 4) + public boolean equal(BigInteger a, BigInteger b) { + return a.compareTo(b) == 0; + } + + @Specialization(order = 5) + public boolean equal(RubyBasicObject a, RubyBasicObject b) { + return a == b; + } + } + + @CoreMethod(names = "initialize", needsSelf = false, maxArgs = 0) + public abstract static class InitializeNode extends CoreMethodNode { + + public InitializeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InitializeNode(InitializeNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder initiailze() { + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = {"send", "__send__"}, needsSelf = true, needsBlock = true, minArgs = 1, isSplatted = true) + public abstract static class SendNode extends CoreMethodNode { + + public SendNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SendNode(SendNode prev) { + super(prev); + } + + @Specialization + public Object send(RubyBasicObject self, Object[] args, @SuppressWarnings("unused") UndefinedPlaceholder block) { + final String name = args[0].toString(); + final Object[] sendArgs = Arrays.copyOfRange(args, 1, args.length); + return self.send(name, null, sendArgs); + } + + @Specialization + public Object send(RubyBasicObject self, Object[] args, RubyProc block) { + final String name = args[0].toString(); + final Object[] sendArgs = Arrays.copyOfRange(args, 1, args.length); + return self.send(name, block, sendArgs); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/BignumNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/BignumNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,642 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.math.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@CoreClass(name = "Bignum") +public abstract class BignumNodes { + + @CoreMethod(names = "+@", maxArgs = 0) + public abstract static class PosNode extends CoreMethodNode { + + public PosNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PosNode(PosNode prev) { + super(prev); + } + + @Specialization + public BigInteger pos(BigInteger value) { + return value; + } + + } + + @CoreMethod(names = "-@", maxArgs = 0) + public abstract static class NegNode extends CoreMethodNode { + + public NegNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NegNode(NegNode prev) { + super(prev); + } + + @Specialization + public BigInteger neg(BigInteger value) { + return value.negate(); + } + + } + + @CoreMethod(names = "+", minArgs = 1, maxArgs = 1) + public abstract static class AddNode extends CoreMethodNode { + + public AddNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AddNode(AddNode prev) { + super(prev); + } + + @Specialization + public Object add(BigInteger a, int b) { + return a.add(BigInteger.valueOf(b)); + } + + @Specialization + public double add(BigInteger a, double b) { + return a.doubleValue() + b; + } + + @Specialization + public Object add(BigInteger a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(a.add(b)); + } + + } + + @CoreMethod(names = "-", minArgs = 1, maxArgs = 1) + public abstract static class SubNode extends CoreMethodNode { + + public SubNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SubNode(SubNode prev) { + super(prev); + } + + @Specialization + public Object sub(BigInteger a, int b) { + return a.subtract(BigInteger.valueOf(b)); + } + + @Specialization + public double sub(BigInteger a, double b) { + return a.doubleValue() - b; + } + + @Specialization + public Object sub(BigInteger a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(a.subtract(b)); + } + + } + + @CoreMethod(names = "*", minArgs = 1, maxArgs = 1) + public abstract static class MulNode extends CoreMethodNode { + + public MulNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MulNode(MulNode prev) { + super(prev); + } + + @Specialization + public Object mul(BigInteger a, int b) { + return a.multiply(BigInteger.valueOf(b)); + } + + @Specialization + public double mul(BigInteger a, double b) { + return a.doubleValue() * b; + } + + @Specialization + public Object mul(BigInteger a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(a.multiply(b)); + } + + } + + @CoreMethod(names = "**", minArgs = 1, maxArgs = 1) + public abstract static class PowNode extends CoreMethodNode { + + public PowNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PowNode(PowNode prev) { + super(prev); + } + + @Specialization + public BigInteger pow(BigInteger a, int b) { + return a.pow(b); + } + + @Specialization + public double pow(BigInteger a, double b) { + return Math.pow(a.doubleValue(), b); + } + + @Specialization + public BigInteger pow(BigInteger a, BigInteger b) { + BigInteger result = BigInteger.ONE; + + for (BigInteger n = BigInteger.ZERO; b.compareTo(b) < 0; n = n.add(BigInteger.ONE)) { + result = result.multiply(a); + } + + return result; + } + + } + + @CoreMethod(names = "/", minArgs = 1, maxArgs = 1) + public abstract static class DivNode extends CoreMethodNode { + + public DivNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DivNode(DivNode prev) { + super(prev); + } + + @Specialization + public Object div(BigInteger a, int b) { + return a.divide(BigInteger.valueOf(b)); + } + + @Specialization + public double div(BigInteger a, double b) { + return a.doubleValue() / b; + } + + @Specialization + public Object div(BigInteger a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(a.divide(b)); + } + + } + + @CoreMethod(names = "%", minArgs = 1, maxArgs = 1) + public abstract static class ModNode extends CoreMethodNode { + + public ModNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ModNode(ModNode prev) { + super(prev); + } + + @Specialization + public Object mod(BigInteger a, int b) { + return GeneralConversions.fixnumOrBignum(a.mod(BigInteger.valueOf(b))); + } + + @Specialization + public Object mod(@SuppressWarnings("unused") BigInteger a, @SuppressWarnings("unused") double b) { + throw new UnsupportedOperationException(); + } + + @Specialization + public Object mod(BigInteger a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(a.mod(b)); + } + + } + + @CoreMethod(names = "divmod", minArgs = 1, maxArgs = 1) + public abstract static class DivModNode extends CoreMethodNode { + + public DivModNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DivModNode(DivModNode prev) { + super(prev); + } + + @SuppressWarnings("unused") + @Specialization + public RubyArray divMod(VirtualFrame frame, BigInteger a, int b) { + return RubyBignum.divMod(getContext(), a, BigInteger.valueOf(b)); + } + + @Specialization + public RubyArray divMod(@SuppressWarnings("unused") BigInteger a, @SuppressWarnings("unused") double b) { + throw new UnsupportedOperationException(); + } + + @Specialization + public RubyArray divMod(BigInteger a, BigInteger b) { + return RubyBignum.divMod(getContext(), a, b); + } + + } + + @CoreMethod(names = "<", minArgs = 1, maxArgs = 1) + public abstract static class LessNode extends CoreMethodNode { + + public LessNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LessNode(LessNode prev) { + super(prev); + } + + @Specialization + public boolean less(BigInteger a, int b) { + return a.compareTo(BigInteger.valueOf(b)) < 0; + } + + @Specialization + public boolean less(BigInteger a, double b) { + return a.compareTo(BigInteger.valueOf((long) b)) < 0; + } + + @Specialization + public boolean less(BigInteger a, BigInteger b) { + return a.compareTo(b) < 0; + } + } + + @CoreMethod(names = "<=", minArgs = 1, maxArgs = 1) + public abstract static class LessEqualNode extends CoreMethodNode { + + public LessEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LessEqualNode(LessEqualNode prev) { + super(prev); + } + + @Specialization + public boolean lessEqual(BigInteger a, int b) { + return a.compareTo(BigInteger.valueOf(b)) <= 0; + } + + @Specialization + public boolean lessEqual(BigInteger a, double b) { + return a.compareTo(BigInteger.valueOf((long) b)) <= 0; + } + + @Specialization + public boolean lessEqual(BigInteger a, BigInteger b) { + return a.compareTo(b) <= 0; + } + } + + @CoreMethod(names = "==", minArgs = 1, maxArgs = 1) + public abstract static class EqualNode extends CoreMethodNode { + + public EqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EqualNode(EqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(BigInteger a, int b) { + return a.compareTo(BigInteger.valueOf(b)) == 0; + } + + @Specialization + public boolean equal(BigInteger a, double b) { + return a.compareTo(BigInteger.valueOf((long) b)) == 0; + } + + @Specialization + public boolean equal(BigInteger a, BigInteger b) { + return a.compareTo(b) == 0; + } + } + + @CoreMethod(names = "<=>", minArgs = 1, maxArgs = 1) + public abstract static class CompareNode extends CoreMethodNode { + + public CompareNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public CompareNode(CompareNode prev) { + super(prev); + } + + @Specialization + public int compare(BigInteger a, int b) { + return a.compareTo(BigInteger.valueOf(b)); + } + + @Specialization + public int compare(BigInteger a, double b) { + return a.compareTo(BigInteger.valueOf((long) b)); + } + + @Specialization + public int compare(BigInteger a, BigInteger b) { + return a.compareTo(b); + } + } + + @CoreMethod(names = "!=", minArgs = 1, maxArgs = 1) + public abstract static class NotEqualNode extends CoreMethodNode { + + public NotEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NotEqualNode(NotEqualNode prev) { + super(prev); + } + + @Specialization + public boolean notEqual(BigInteger a, int b) { + return a.compareTo(BigInteger.valueOf(b)) != 0; + } + + @Specialization + public boolean notEqual(BigInteger a, double b) { + return a.compareTo(BigInteger.valueOf((long) b)) != 0; + } + + @Specialization + public boolean notEqual(BigInteger a, BigInteger b) { + return a.compareTo(b) != 0; + } + } + + @CoreMethod(names = ">=", minArgs = 1, maxArgs = 1) + public abstract static class GreaterEqualNode extends CoreMethodNode { + + public GreaterEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GreaterEqualNode(GreaterEqualNode prev) { + super(prev); + } + + @Specialization + public boolean greaterEqual(BigInteger a, int b) { + return a.compareTo(BigInteger.valueOf(b)) >= 0; + } + + @Specialization + public boolean greaterEqual(BigInteger a, double b) { + return a.compareTo(BigInteger.valueOf((long) b)) >= 0; + } + + @Specialization + public boolean greaterEqual(BigInteger a, BigInteger b) { + return a.compareTo(b) >= 0; + } + } + + @CoreMethod(names = ">", minArgs = 1, maxArgs = 1) + public abstract static class GreaterNode extends CoreMethodNode { + + public GreaterNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GreaterNode(GreaterNode prev) { + super(prev); + } + + @Specialization + public boolean equal(BigInteger a, int b) { + return a.compareTo(BigInteger.valueOf(b)) > 0; + } + + @Specialization + public boolean equal(BigInteger a, double b) { + return a.compareTo(BigInteger.valueOf((long) b)) > 0; + } + + @Specialization + public boolean equal(BigInteger a, BigInteger b) { + return a.compareTo(b) > 0; + } + } + + @CoreMethod(names = "&", minArgs = 1, maxArgs = 1) + public abstract static class BitAndNode extends CoreMethodNode { + + public BitAndNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BitAndNode(BitAndNode prev) { + super(prev); + } + + @Specialization + public Object bitAnd(BigInteger a, int b) { + return GeneralConversions.fixnumOrBignum(a.and(BigInteger.valueOf(b))); + } + + @Specialization + public Object bitAnd(BigInteger a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(a.and(b)); + } + } + + @CoreMethod(names = "|", minArgs = 1, maxArgs = 1) + public abstract static class BitOrNode extends CoreMethodNode { + + public BitOrNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BitOrNode(BitOrNode prev) { + super(prev); + } + + @Specialization + public Object bitOr(BigInteger a, int b) { + return GeneralConversions.fixnumOrBignum(a.or(BigInteger.valueOf(b))); + } + + @Specialization + public Object bitOr(BigInteger a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(a.or(b)); + } + } + + @CoreMethod(names = "^", minArgs = 1, maxArgs = 1) + public abstract static class BitXOrNode extends CoreMethodNode { + + public BitXOrNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BitXOrNode(BitXOrNode prev) { + super(prev); + } + + @Specialization + public Object bitXOr(BigInteger a, int b) { + return GeneralConversions.fixnumOrBignum(a.xor(BigInteger.valueOf(b))); + } + + @Specialization + public Object bitXOr(BigInteger a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(a.xor(b)); + } + } + + @CoreMethod(names = "<<", minArgs = 1, maxArgs = 1) + public abstract static class LeftShiftNode extends CoreMethodNode { + + public LeftShiftNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LeftShiftNode(LeftShiftNode prev) { + super(prev); + } + + @Specialization + public Object leftShift(BigInteger a, int b) { + if (b >= 0) { + return GeneralConversions.fixnumOrBignum(a.shiftLeft(b)); + } else { + return GeneralConversions.fixnumOrBignum(a.shiftRight(-b)); + } + } + + } + + @CoreMethod(names = ">>", minArgs = 1, maxArgs = 1) + public abstract static class RightShiftNode extends CoreMethodNode { + + public RightShiftNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public RightShiftNode(RightShiftNode prev) { + super(prev); + } + + @Specialization + public Object leftShift(BigInteger a, int b) { + if (b >= 0) { + return GeneralConversions.fixnumOrBignum(a.shiftRight(b)); + } else { + return GeneralConversions.fixnumOrBignum(a.shiftLeft(-b)); + } + } + + } + + @CoreMethod(names = "inspect", maxArgs = 0) + public abstract static class InpsectNode extends CoreMethodNode { + + public InpsectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InpsectNode(InpsectNode prev) { + super(prev); + } + + @Specialization + public RubyString inspect(BigInteger n) { + return getContext().makeString(n.toString()); + } + + } + + @CoreMethod(names = "nonzero?", maxArgs = 0) + public abstract static class NonZeroNode extends CoreMethodNode { + + public NonZeroNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NonZeroNode(NonZeroNode prev) { + super(prev); + } + + @Specialization + public Object nonZero(BigInteger value) { + if (value.compareTo(BigInteger.ZERO) == 0) { + return false; + } else { + return value; + } + } + + } + + @CoreMethod(names = "times", needsBlock = true, maxArgs = 0) + public abstract static class TimesNode extends YieldingCoreMethodNode { + + public TimesNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public TimesNode(TimesNode prev) { + super(prev); + } + + @Specialization + public BigInteger times(VirtualFrame frame, BigInteger n, RubyProc block) { + for (BigInteger i = BigInteger.ZERO; i.compareTo(n) < 0; i = i.add(BigInteger.ONE)) { + yield(frame, block, i); + } + + return n; + } + + } + + @CoreMethod(names = "to_s", maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toS(BigInteger value) { + return getContext().makeString(value.toString()); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ClassNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ClassNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@CoreClass(name = "Class") +public abstract class ClassNodes { + + @CoreMethod(names = "===", minArgs = 1, maxArgs = 1) + public abstract static class ContainsInstanceNode extends CoreMethodNode { + + public ContainsInstanceNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ContainsInstanceNode(ContainsInstanceNode prev) { + super(prev); + } + + @Specialization + public boolean containsInstance(RubyClass rubyClass, RubyBasicObject instance) { + return instance.getRubyClass().assignableTo(rubyClass); + } + } + + @CoreMethod(names = "new", needsBlock = true, isSplatted = true) + public abstract static class NewNode extends CoreMethodNode { + + @Child protected DispatchHeadNode initialize; + + public NewNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + initialize = adoptChild(new DispatchHeadNode(context, getSourceSection(), "initialize", false)); + } + + public NewNode(NewNode prev) { + super(prev); + initialize = adoptChild(prev.initialize); + } + + @Specialization + public RubyBasicObject newInstance(VirtualFrame frame, RubyClass rubyClass, Object[] args, @SuppressWarnings("unused") UndefinedPlaceholder block) { + return doNewInstance(frame, rubyClass, args, null); + } + + @Specialization + public RubyBasicObject newInstance(VirtualFrame frame, RubyClass rubyClass, Object[] args, RubyProc block) { + return doNewInstance(frame, rubyClass, args, block); + } + + private RubyBasicObject doNewInstance(VirtualFrame frame, RubyClass rubyClass, Object[] args, RubyProc block) { + final RubyBasicObject instance = rubyClass.newInstance(); + initialize.dispatch(frame, instance, block, args); + return instance; + } + + } + + @CoreMethod(names = "to_s", maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toS(RubyClass rubyClass) { + return getContext().makeString(rubyClass.getName()); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ComparableNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ComparableNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@CoreClass(name = "Comparable") +public abstract class ComparableNodes { + + public abstract static class ComparableCoreMethodNode extends CoreMethodNode { + + @Child protected DispatchHeadNode compareNode; + + public ComparableCoreMethodNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + compareNode = adoptChild(new DispatchHeadNode(context, getSourceSection(), "<=>", false)); + } + + public ComparableCoreMethodNode(ComparableCoreMethodNode prev) { + super(prev); + compareNode = adoptChild(prev.compareNode); + } + + public int compare(VirtualFrame frame, RubyBasicObject receiverObject, Object comparedTo) { + return (int) compareNode.dispatch(frame, receiverObject, null, comparedTo); + } + + } + + @CoreMethod(names = "<", isModuleMethod = true, minArgs = 1, maxArgs = 1) + public abstract static class LessNode extends ComparableCoreMethodNode { + + public LessNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LessNode(LessNode prev) { + super(prev); + } + + @Specialization + public boolean less(VirtualFrame frame, RubyBasicObject self, Object comparedTo) { + return compare(frame, self, comparedTo) < 0; + } + + } + + @CoreMethod(names = "<=", isModuleMethod = true, minArgs = 1, maxArgs = 1) + public abstract static class LessEqualNode extends ComparableCoreMethodNode { + + public LessEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LessEqualNode(LessEqualNode prev) { + super(prev); + } + + @Specialization + public boolean lessEqual(VirtualFrame frame, RubyBasicObject self, Object comparedTo) { + return compare(frame, self, comparedTo) <= 0; + } + + } + + @CoreMethod(names = "==", isModuleMethod = true, minArgs = 1, maxArgs = 1) + public abstract static class EqualNode extends ComparableCoreMethodNode { + + public EqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EqualNode(EqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(VirtualFrame frame, RubyBasicObject self, Object comparedTo) { + if (self == comparedTo) { + return true; + } + + try { + return compare(frame, self, comparedTo) == 0; + } catch (Exception e) { + // Comparable#== catches and ignores all exceptions in <=>, returning false + return false; + } + } + } + + @CoreMethod(names = ">=", isModuleMethod = true, minArgs = 1, maxArgs = 1) + public abstract static class GreaterEqualNode extends ComparableCoreMethodNode { + + public GreaterEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GreaterEqualNode(GreaterEqualNode prev) { + super(prev); + } + + @Specialization + public boolean greaterEqual(VirtualFrame frame, RubyBasicObject self, Object comparedTo) { + return compare(frame, self, comparedTo) >= 0; + } + + } + + @CoreMethod(names = ">", isModuleMethod = true, minArgs = 1, maxArgs = 1) + public abstract static class GreaterNode extends ComparableCoreMethodNode { + + public GreaterNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GreaterNode(GreaterNode prev) { + super(prev); + } + + @Specialization + public boolean greater(VirtualFrame frame, RubyBasicObject self, Object comparedTo) { + return compare(frame, self, comparedTo) > 0; + } + + } + + @CoreMethod(names = "between?", isModuleMethod = true, minArgs = 2, maxArgs = 2) + public abstract static class BetweenNode extends ComparableCoreMethodNode { + + public BetweenNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BetweenNode(BetweenNode prev) { + super(prev); + } + + @Specialization + public boolean between(VirtualFrame frame, RubyBasicObject self, Object min, Object max) { + return !(compare(frame, self, min) < 0 || compare(frame, self, max) > 0); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ContinuationNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ContinuationNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "Continuation") +public abstract class ContinuationNodes { + + @CoreMethod(names = "call", isSplatted = true) + public abstract static class CallNode extends CoreMethodNode { + + public CallNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public CallNode(CallNode prev) { + super(prev); + } + + @Specialization + public Object call(RubyContinuation continuation, Object[] args) { + continuation.call(args); + return null; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/CoreClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/CoreClass.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.lang.annotation.*; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface CoreClass { + + String name(); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/CoreMethod.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/CoreMethod.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.lang.annotation.*; + +import com.oracle.truffle.ruby.runtime.configuration.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface CoreMethod { + + String[] names(); + + boolean isModuleMethod() default false; + + boolean needsSelf() default true; + + boolean isSplatted() default false; + + boolean needsBlock() default false; + + boolean appendCallNode() default false; + + RubyVersion[] versions() default {RubyVersion.RUBY_18, RubyVersion.RUBY_19, RubyVersion.RUBY_20, RubyVersion.RUBY_21}; + + int minArgs() default Arity.NO_MINIMUM; + + int maxArgs() default Arity.NO_MAXIMUM; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/CoreMethodNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/CoreMethodNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +@NodeChild(value = "arguments", type = RubyNode[].class) +public abstract class CoreMethodNode extends RubyNode { + + public CoreMethodNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public CoreMethodNode(CoreMethodNode prev) { + super(prev); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/CoreMethodNodeManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/CoreMethodNodeManager.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.control.*; +import com.oracle.truffle.ruby.nodes.debug.*; +import com.oracle.truffle.ruby.nodes.methods.arguments.*; +import com.oracle.truffle.ruby.nodes.objects.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +public abstract class CoreMethodNodeManager { + + /** + * Register all the nodes that represent core methods as methods with their respective classes, + * given the Object class object, which should already be initialized with all the core classes. + */ + public static void addMethods(RubyClass rubyObjectClass) { + for (MethodDetails methodDetails : getMethods()) { + if (Arrays.asList(methodDetails.getMethodAnnotation().versions()).contains(rubyObjectClass.getContext().getConfiguration().getRubyVersion())) { + addMethod(rubyObjectClass, methodDetails); + } + } + } + + /** + * Collect up all the core method nodes. Abstracted to allow the SVM to implement at compile + * type. + */ + public static List getMethods() { + final List methods = new ArrayList<>(); + getMethods(methods, ArrayNodesFactory.getFactories()); + getMethods(methods, BasicObjectNodesFactory.getFactories()); + getMethods(methods, BignumNodesFactory.getFactories()); + getMethods(methods, ClassNodesFactory.getFactories()); + getMethods(methods, ContinuationNodesFactory.getFactories()); + getMethods(methods, ComparableNodesFactory.getFactories()); + getMethods(methods, DebugNodesFactory.getFactories()); + getMethods(methods, DirNodesFactory.getFactories()); + getMethods(methods, ExceptionNodesFactory.getFactories()); + getMethods(methods, FalseClassNodesFactory.getFactories()); + getMethods(methods, FiberNodesFactory.getFactories()); + getMethods(methods, FileNodesFactory.getFactories()); + getMethods(methods, FixnumNodesFactory.getFactories()); + getMethods(methods, FloatNodesFactory.getFactories()); + getMethods(methods, HashNodesFactory.getFactories()); + getMethods(methods, KernelNodesFactory.getFactories()); + getMethods(methods, MainNodesFactory.getFactories()); + getMethods(methods, MatchDataNodesFactory.getFactories()); + getMethods(methods, MathNodesFactory.getFactories()); + getMethods(methods, ModuleNodesFactory.getFactories()); + getMethods(methods, NilClassNodesFactory.getFactories()); + getMethods(methods, ObjectNodesFactory.getFactories()); + getMethods(methods, ObjectSpaceNodesFactory.getFactories()); + getMethods(methods, ProcessNodesFactory.getFactories()); + getMethods(methods, ProcNodesFactory.getFactories()); + getMethods(methods, RangeNodesFactory.getFactories()); + getMethods(methods, RegexpNodesFactory.getFactories()); + getMethods(methods, SignalNodesFactory.getFactories()); + getMethods(methods, StringNodesFactory.getFactories()); + getMethods(methods, StructNodesFactory.getFactories()); + getMethods(methods, SymbolNodesFactory.getFactories()); + getMethods(methods, ThreadNodesFactory.getFactories()); + getMethods(methods, TimeNodesFactory.getFactories()); + getMethods(methods, TrueClassNodesFactory.getFactories()); + return methods; + } + + /** + * Collect up the core methods created by a factory. + */ + private static void getMethods(List methods, List> nodeFactories) { + for (NodeFactory nodeFactory : nodeFactories) { + final GeneratedBy generatedBy = nodeFactory.getClass().getAnnotation(GeneratedBy.class); + final Class nodeClass = generatedBy.value(); + final CoreClass classAnnotation = nodeClass.getEnclosingClass().getAnnotation(CoreClass.class); + final CoreMethod methodAnnotation = nodeClass.getAnnotation(CoreMethod.class); + methods.add(new MethodDetails(classAnnotation, methodAnnotation, nodeFactory)); + } + } + + /** + * Take a core method node factory, the annotations for the class and method, and add it as a + * method on the correct class. + */ + private static void addMethod(RubyClass rubyObjectClass, MethodDetails methodDetails) { + assert rubyObjectClass != null; + assert methodDetails != null; + + final RubyContext context = rubyObjectClass.getContext(); + + RubyModule module; + + if (methodDetails.getClassAnnotation().name().equals("main")) { + module = context.getCoreLibrary().getMainObject().getSingletonClass(); + } else { + module = (RubyModule) rubyObjectClass.lookupConstant(methodDetails.getClassAnnotation().name()); + } + + assert module != null : methodDetails.getClassAnnotation().name(); + + final List names = Arrays.asList(methodDetails.getMethodAnnotation().names()); + assert names.size() >= 1; + + final String canonicalName = names.get(0); + final List aliases = names.subList(1, names.size()); + + final UniqueMethodIdentifier uniqueIdentifier = new UniqueMethodIdentifier(); + final Visibility visibility = Visibility.PUBLIC; + + final RubyRootNode pristineRootNode = makeGenericMethod(context, methodDetails); + final CallTarget callTarget = Truffle.getRuntime().createCallTarget(NodeUtil.cloneNode(pristineRootNode)); + + final String intrinsicName = methodDetails.getClassAnnotation().name() + "#" + canonicalName; + + final InlinableMethodImplementation methodImplementation = new InlinableMethodImplementation(callTarget, null, new FrameDescriptor(), pristineRootNode, true, + methodDetails.getMethodAnnotation().appendCallNode()); + final RubyMethod method = new RubyMethod(pristineRootNode.getSourceSection(), module, uniqueIdentifier, intrinsicName, canonicalName, visibility, false, methodImplementation); + + module.addMethod(method); + + if (methodDetails.getMethodAnnotation().isModuleMethod()) { + module.getSingletonClass().addMethod(method); + } + + for (String alias : aliases) { + final RubyMethod withAlias = method.withNewName(alias); + + module.addMethod(withAlias); + + if (methodDetails.getMethodAnnotation().isModuleMethod()) { + module.getSingletonClass().addMethod(withAlias); + } + } + } + + private static RubyRootNode makeGenericMethod(RubyContext context, MethodDetails methodDetails) { + final SourceSection sourceSection = new CoreSourceSection(methodDetails.getClassAnnotation().name() + "#" + methodDetails.getMethodAnnotation().names()[0]); + + final Arity arity = new Arity(methodDetails.getMethodAnnotation().minArgs(), methodDetails.getMethodAnnotation().maxArgs()); + + final List argumentsNodes = new ArrayList<>(); + + if (methodDetails.getMethodAnnotation().needsSelf()) { + argumentsNodes.add(new SelfNode(context, sourceSection)); + } + + if (methodDetails.getMethodAnnotation().isSplatted()) { + argumentsNodes.add(new ReadAllArgumentsNode(context, sourceSection)); + } else { + assert arity.getMaximum() != Arity.NO_MAXIMUM; + + for (int n = 0; n < arity.getMaximum(); n++) { + argumentsNodes.add(new ReadPreArgumentNode(context, sourceSection, n, true)); + } + } + + if (methodDetails.getMethodAnnotation().needsBlock()) { + argumentsNodes.add(new ReadBlockArgumentNode(context, sourceSection, true)); + } + + final RubyNode methodNode = methodDetails.getNodeFactory().createNode(context, sourceSection, argumentsNodes.toArray(new RubyNode[argumentsNodes.size()])); + final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, arity); + final SequenceNode block = new SequenceNode(context, sourceSection, checkArity, methodNode); + + return new RubyRootNode(sourceSection, methodDetails.getClassAnnotation().name() + "#" + methodDetails.getMethodAnnotation().names()[0] + "(core)", block); + } + + public static class MethodDetails { + + private final CoreClass classAnnotation; + private final CoreMethod methodAnnotation; + private final NodeFactory nodeFactory; + + public MethodDetails(CoreClass classAnnotation, CoreMethod methodAnnotation, NodeFactory nodeFactory) { + assert classAnnotation != null; + assert methodAnnotation != null; + assert nodeFactory != null; + this.classAnnotation = classAnnotation; + this.methodAnnotation = methodAnnotation; + this.nodeFactory = nodeFactory; + } + + public CoreClass getClassAnnotation() { + return classAnnotation; + } + + public CoreMethod getMethodAnnotation() { + return methodAnnotation; + } + + public NodeFactory getNodeFactory() { + return nodeFactory; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/DirNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/DirNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.io.*; +import java.nio.file.*; +import java.nio.file.attribute.*; + +import com.oracle.truffle.api.CompilerDirectives.SlowPath; +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@CoreClass(name = "Dir") +public abstract class DirNodes { + + @CoreMethod(names = "[]", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class GlobNode extends CoreMethodNode { + + public GlobNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GlobNode(GlobNode prev) { + super(prev); + } + + @Specialization + public RubyArray glob(RubyString glob) { + return glob(getContext(), glob.toString()); + } + + @SlowPath + private static RubyArray glob(final RubyContext context, String glob) { + /* + * Globbing is quite complicated. We've implemented a subset of the functionality that + * satisfies MSpec, but it will likely break for anyone else. + */ + + context.implementationMessage("globbing %s", glob); + + String absoluteGlob; + + if (!glob.startsWith("/")) { + absoluteGlob = new File(".", glob).getAbsolutePath().toString(); + } else { + absoluteGlob = glob; + } + + // Get the first star + + final int firstStar = absoluteGlob.indexOf('*'); + assert firstStar >= 0; + + // Walk back from that to the first / before that star + + int prefixLength = firstStar; + + while (prefixLength > 0 && absoluteGlob.charAt(prefixLength) == File.separatorChar) { + prefixLength--; + } + + final String prefix = absoluteGlob.substring(0, prefixLength - 1); + + final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + absoluteGlob.substring(prefixLength)); + + final RubyArray array = new RubyArray(context.getCoreLibrary().getArrayClass()); + + try { + Files.walkFileTree(FileSystems.getDefault().getPath(prefix), new SimpleFileVisitor() { + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (matcher.matches(file)) { + array.push(context.makeString(file.toString())); + } + + return FileVisitResult.CONTINUE; + } + + }); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return array; + } + + } + + @CoreMethod(names = "chdir", isModuleMethod = true, needsSelf = false, needsBlock = true, minArgs = 1, maxArgs = 1) + public abstract static class ChdirNode extends YieldingCoreMethodNode { + + public ChdirNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ChdirNode(ChdirNode prev) { + super(prev); + } + + @Specialization + public Object chdir(VirtualFrame frame, RubyString path, RubyProc block) { + final RubyContext context = getContext(); + + final String previous = context.getCurrentDirectory(); + context.setCurrentDirectory(path.toString()); + + if (block != null) { + try { + return yield(frame, block, path); + } finally { + context.setCurrentDirectory(previous); + } + } else { + return 0; + } + } + + } + + @CoreMethod(names = {"exist?", "exists?"}, isModuleMethod = true, needsSelf = false, maxArgs = 1) + public abstract static class ExistsNode extends CoreMethodNode { + + public ExistsNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ExistsNode(ExistsNode prev) { + super(prev); + } + + @Specialization + public boolean exists(RubyString path) { + return new File(path.toString()).isDirectory(); + } + + } + + @CoreMethod(names = "pwd", isModuleMethod = true, needsSelf = false, maxArgs = 0) + public abstract static class PwdNode extends CoreMethodNode { + + public PwdNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PwdNode(PwdNode prev) { + super(prev); + } + + @Specialization + public RubyString pwd() { + return getContext().makeString(getContext().getCurrentDirectory()); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ExceptionNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ExceptionNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@CoreClass(name = "Exception") +public abstract class ExceptionNodes { + + @CoreMethod(names = "initialize", minArgs = 0, maxArgs = 1) + public abstract static class InitializeNode extends CoreMethodNode { + + public InitializeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InitializeNode(InitializeNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder initialize(RubyException exception, @SuppressWarnings("unused") UndefinedPlaceholder message) { + exception.initialize(getContext().makeString(" ")); + return NilPlaceholder.INSTANCE; + } + + @Specialization + public NilPlaceholder initialize(RubyException exception, RubyString message) { + exception.initialize(message); + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "backtrace", needsSelf = false, maxArgs = 0) + public abstract static class BacktraceNode extends CoreMethodNode { + + public BacktraceNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BacktraceNode(BacktraceNode prev) { + super(prev); + } + + @Specialization + public RubyArray backtrace() { + return new RubyArray(getContext().getCoreLibrary().getArrayClass()); + } + + } + + @CoreMethod(names = "message", maxArgs = 0) + public abstract static class MessageNode extends CoreMethodNode { + + public MessageNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MessageNode(MessageNode prev) { + super(prev); + } + + @Specialization + public RubyString message(RubyException exception) { + return exception.getMessage(); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/FalseClassNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/FalseClassNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "FalseClass") +public abstract class FalseClassNodes { + + @CoreMethod(names = "!", needsSelf = false, maxArgs = 0) + public abstract static class NotNode extends CoreMethodNode { + + public NotNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NotNode(NotNode prev) { + super(prev); + } + + @Specialization + public boolean not() { + return true; + } + + } + + @CoreMethod(names = {"==", "===", "=~"}, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class EqualNode extends CoreMethodNode { + + public EqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EqualNode(EqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(boolean other) { + return !other; + } + + @Specialization + public boolean equal(Object other) { + return other instanceof Boolean && !((boolean) other); + } + + } + + @CoreMethod(names = "^", needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class XorNode extends CoreMethodNode { + + public XorNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public XorNode(XorNode prev) { + super(prev); + } + + @Specialization + public boolean xor(boolean other) { + return false ^ other; + } + + } + + @CoreMethod(names = "to_s", needsSelf = false, maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toS() { + return getContext().makeString("false"); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/FiberNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/FiberNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "Fiber") +public abstract class FiberNodes { + + @CoreMethod(names = "resume", isSplatted = true) + public abstract static class ResumeNode extends CoreMethodNode { + + public ResumeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ResumeNode(ResumeNode prev) { + super(prev); + } + + @Specialization + public Object resume(RubyFiber fiberBeingResumed, Object[] args) { + final RubyFiber sendingFiber = getContext().getFiberManager().getCurrentFiber(); + + fiberBeingResumed.resume(sendingFiber, args); + + return sendingFiber.waitForResume(); + } + + } + + @CoreMethod(names = "initialize", needsBlock = true, maxArgs = 0) + public abstract static class InitializeNode extends CoreMethodNode { + + public InitializeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InitializeNode(InitializeNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder initialize(RubyFiber fiber, RubyProc block) { + fiber.initialize(block); + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "yield", isModuleMethod = true, needsSelf = false, isSplatted = true) + public abstract static class YieldNode extends CoreMethodNode { + + public YieldNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public YieldNode(YieldNode prev) { + super(prev); + } + + @Specialization + public Object yield(Object[] args) { + final RubyFiber yieldingFiber = getContext().getFiberManager().getCurrentFiber(); + final RubyFiber fiberYieldedTo = yieldingFiber.lastResumedByFiber; + + fiberYieldedTo.resume(yieldingFiber, args); + + return yieldingFiber.waitForResume(); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/FileNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/FileNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.io.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@CoreClass(name = "File") +public abstract class FileNodes { + + @CoreMethod(names = "absolute_path", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class AbsolutePathNode extends CoreMethodNode { + + public AbsolutePathNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AbsolutePathNode(AbsolutePathNode prev) { + super(prev); + } + + @Specialization + public RubyString absolutePath(RubyString path) { + return getContext().makeString(new File(path.toString()).getAbsolutePath()); + } + + } + + @CoreMethod(names = "close", maxArgs = 0) + public abstract static class CloseNode extends CoreMethodNode { + + public CloseNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public CloseNode(CloseNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder close(RubyFile file) { + file.close(); + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "directory?", isModuleMethod = true, needsSelf = false, maxArgs = 1) + public abstract static class DirectoryNode extends CoreMethodNode { + + public DirectoryNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DirectoryNode(DirectoryNode prev) { + super(prev); + } + + @Specialization + public boolean directory(RubyString path) { + return new File(path.toString()).isDirectory(); + } + + } + + @CoreMethod(names = "dirname", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class DirnameNode extends CoreMethodNode { + + public DirnameNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DirnameNode(DirnameNode prev) { + super(prev); + } + + @Specialization + public RubyString dirname(RubyString path) { + final String parent = new File(path.toString()).getParent(); + + if (parent == null) { + return getContext().makeString("."); + } else { + return getContext().makeString(parent); + } + } + + } + + @CoreMethod(names = "each_line", needsBlock = true, maxArgs = 0) + public abstract static class EachLineNode extends YieldingCoreMethodNode { + + public EachLineNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EachLineNode(EachLineNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder eachLine(VirtualFrame frame, RubyFile file, RubyProc block) { + final RubyContext context = getContext(); + + // TODO(cs): this buffered reader may consume too much + + final BufferedReader lineReader = new BufferedReader(file.getReader()); + + while (true) { + String line; + + try { + line = lineReader.readLine(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + if (line == null) { + break; + } + + yield(frame, block, context.makeString(line)); + } + + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = {"exist?", "exists?"}, isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class ExistsNode extends CoreMethodNode { + + public ExistsNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ExistsNode(ExistsNode prev) { + super(prev); + } + + @Specialization + public boolean exists(RubyString path) { + return new File(path.toString()).isFile(); + } + + } + + @CoreMethod(names = "executable?", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class ExecutableNode extends CoreMethodNode { + + public ExecutableNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ExecutableNode(ExecutableNode prev) { + super(prev); + } + + @Specialization + public boolean executable(RubyString path) { + return new File(path.toString()).canExecute(); + } + + } + + @CoreMethod(names = "expand_path", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 2) + public abstract static class ExpandPathNode extends CoreMethodNode { + + public ExpandPathNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ExpandPathNode(ExpandPathNode prev) { + super(prev); + } + + @Specialization + public RubyString expandPath(RubyString path, @SuppressWarnings("unused") UndefinedPlaceholder dir) { + return getContext().makeString(RubyFile.expandPath(path.toString())); + } + + @Specialization + public RubyString expandPath(RubyString path, RubyString dir) { + return getContext().makeString(RubyFile.expandPath(path.toString(), dir.toString())); + } + + } + + @CoreMethod(names = "file?", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class FileNode extends CoreMethodNode { + + public FileNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public FileNode(FileNode prev) { + super(prev); + } + + @Specialization + public boolean file(RubyString path) { + return new File(path.toString()).isFile(); + } + + } + + @CoreMethod(names = "join", isModuleMethod = true, needsSelf = false, isSplatted = true) + public abstract static class JoinNode extends CoreMethodNode { + + public JoinNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public JoinNode(JoinNode prev) { + super(prev); + } + + @Specialization + public RubyString join(Object[] parts) { + return getContext().makeString(RubyArray.join(parts, File.separator)); + } + } + + @CoreMethod(names = "open", isModuleMethod = true, needsSelf = false, needsBlock = true, minArgs = 2, maxArgs = 2) + public abstract static class OpenNode extends YieldingCoreMethodNode { + + public OpenNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public OpenNode(OpenNode prev) { + super(prev); + } + + @Specialization + public Object open(VirtualFrame frame, RubyString fileName, RubyString mode, RubyProc block) { + final RubyFile file = RubyFile.open(getContext(), fileName.toString(), mode.toString()); + + if (block != null) { + try { + yield(frame, block, file); + } finally { + file.close(); + } + } + + return file; + } + + } + + @CoreMethod(names = "write", maxArgs = 0) + public abstract static class WriteNode extends CoreMethodNode { + + public WriteNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public WriteNode(WriteNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder write(RubyFile file, RubyString string) { + try { + final Writer writer = file.getWriter(); + writer.write(string.toString()); + writer.flush(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return NilPlaceholder.INSTANCE; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/FixnumNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/FixnumNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,853 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.math.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@CoreClass(name = "Fixnum") +public abstract class FixnumNodes { + + @CoreMethod(names = "+@", maxArgs = 0) + public abstract static class PosNode extends CoreMethodNode { + + public PosNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PosNode(PosNode prev) { + super(prev); + } + + @Specialization + public int pos(int value) { + return value; + } + + } + + @CoreMethod(names = "-@", maxArgs = 0) + public abstract static class NegNode extends CoreMethodNode { + + public NegNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NegNode(NegNode prev) { + super(prev); + } + + @Specialization(rewriteOn = ArithmeticException.class) + public int neg(int value) { + return ExactMath.subtractExact(0, value); + } + + @Specialization + public BigInteger negWithOverflow(int value) { + return BigInteger.valueOf(value).negate(); + } + + } + + @CoreMethod(names = "+", minArgs = 1, maxArgs = 1) + public abstract static class AddNode extends CoreMethodNode { + + public AddNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AddNode(AddNode prev) { + super(prev); + } + + @Specialization(rewriteOn = ArithmeticException.class) + public int add(int a, int b) { + return ExactMath.addExact(a, b); + } + + @Specialization + public Object addWithOverflow(int a, int b) { + return GeneralConversions.fixnumOrBignum(BigInteger.valueOf(a).add(BigInteger.valueOf(b))); + } + + @Specialization + public double add(int a, double b) { + return a + b; + } + + @Specialization + public Object add(int a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(BigInteger.valueOf(a).add(b)); + } + + } + + @CoreMethod(names = "-", minArgs = 1, maxArgs = 1) + public abstract static class SubNode extends CoreMethodNode { + + public SubNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SubNode(SubNode prev) { + super(prev); + } + + @Specialization(rewriteOn = ArithmeticException.class) + public int sub(int a, int b) { + return ExactMath.subtractExact(a, b); + } + + @Specialization + public Object subWithOverflow(int a, int b) { + return GeneralConversions.fixnumOrBignum(BigInteger.valueOf(a).subtract(BigInteger.valueOf(b))); + } + + @Specialization + public double sub(int a, double b) { + return a - b; + } + + @Specialization + public Object sub(int a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(BigInteger.valueOf(a).subtract(b)); + } + + } + + @CoreMethod(names = "*", minArgs = 1, maxArgs = 1) + public abstract static class MulNode extends CoreMethodNode { + + public MulNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MulNode(MulNode prev) { + super(prev); + } + + @Specialization(rewriteOn = ArithmeticException.class) + public int mul(int a, int b) { + return ExactMath.multiplyExact(a, b); + } + + @Specialization + public Object mulWithOverflow(int a, int b) { + return GeneralConversions.fixnumOrBignum(BigInteger.valueOf(a).multiply(BigInteger.valueOf(b))); + } + + @Specialization + public double mul(int a, double b) { + return a * b; + } + + @Specialization + public Object mul(int a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(BigInteger.valueOf(a).multiply(b)); + } + + } + + @CoreMethod(names = "**", minArgs = 1, maxArgs = 1) + public abstract static class PowNode extends CoreMethodNode { + + public PowNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PowNode(PowNode prev) { + super(prev); + } + + @Specialization + public Object pow(int a, int b) { + return GeneralConversions.fixnumOrBignum(BigInteger.valueOf(a).pow(b)); + } + + @Specialization + public double pow(int a, double b) { + return Math.pow(a, b); + } + + @Specialization + public Object pow(int a, BigInteger b) { + final BigInteger bigA = BigInteger.valueOf(a); + + BigInteger result = BigInteger.ONE; + + for (BigInteger n = BigInteger.ZERO; b.compareTo(b) < 0; n = n.add(BigInteger.ONE)) { + result = result.multiply(bigA); + } + + return result; + } + + } + + @CoreMethod(names = "/", minArgs = 1, maxArgs = 1) + public abstract static class DivNode extends CoreMethodNode { + + public DivNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DivNode(DivNode prev) { + super(prev); + } + + @Specialization + public int div(int a, int b) { + return a / b; + } + + @Specialization + public double div(int a, double b) { + return a / b; + } + + @Specialization + public int div(@SuppressWarnings("unused") int a, @SuppressWarnings("unused") BigInteger b) { + return 0; + } + } + + @CoreMethod(names = "%", minArgs = 1, maxArgs = 1) + public abstract static class ModNode extends CoreMethodNode { + + public ModNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ModNode(ModNode prev) { + super(prev); + } + + @Specialization + public int mod(int a, int b) { + return a % b; + } + + @Specialization + public double mod(@SuppressWarnings("unused") int a, @SuppressWarnings("unused") double b) { + throw new UnsupportedOperationException(); + } + + @Specialization + public BigInteger mod(@SuppressWarnings("unused") int a, BigInteger b) { + return b; + } + } + + @CoreMethod(names = "divmod", minArgs = 1, maxArgs = 1) + public abstract static class DivModNode extends CoreMethodNode { + + public DivModNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DivModNode(DivModNode prev) { + super(prev); + } + + @Specialization + public RubyArray divMod(int a, int b) { + int q; + + if (b < 0) { + if (a < 0) { + q = -a / -b; + } else { + q = -(a / -b); + } + } else { + if (a < 0) { + q = -(-a / b); + } else { + q = a / b; + } + } + + int r = a - q * b; + + if ((r < 0 && b > 0) || (r > 0 && b < 0)) { + r += b; + q -= 1; + } + + final FixnumImmutablePairArrayStore store = new FixnumImmutablePairArrayStore(q, r); + return new RubyArray(getContext().getCoreLibrary().getArrayClass(), store); + } + + @Specialization + public RubyArray divMod(@SuppressWarnings("unused") int a, @SuppressWarnings("unused") double b) { + throw new UnsupportedOperationException(); + } + + @Specialization + public RubyArray divMod(int a, BigInteger b) { + return RubyBignum.divMod(getContext(), BigInteger.valueOf(a), b); + } + } + + @CoreMethod(names = "<", minArgs = 1, maxArgs = 1) + public abstract static class LessNode extends CoreMethodNode { + + public LessNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LessNode(LessNode prev) { + super(prev); + } + + @Specialization + public boolean less(int a, int b) { + return a < b; + } + + @Specialization + public boolean less(int a, double b) { + return a < b; + } + + @Specialization + public boolean less(int a, BigInteger b) { + return BigInteger.valueOf(a).compareTo(b) < 0; + } + } + + @CoreMethod(names = "<=", minArgs = 1, maxArgs = 1) + public abstract static class LessEqualNode extends CoreMethodNode { + + public LessEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LessEqualNode(LessEqualNode prev) { + super(prev); + } + + @Specialization + public boolean lessEqual(int a, int b) { + return a <= b; + } + + @Specialization + public boolean lessEqual(int a, double b) { + return a <= b; + } + + @Specialization + public boolean lessEqual(int a, BigInteger b) { + return BigInteger.valueOf(a).compareTo(b) <= 0; + } + } + + @CoreMethod(names = {"==", "==="}, minArgs = 1, maxArgs = 1) + public abstract static class EqualNode extends CoreMethodNode { + + public EqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EqualNode(EqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(int a, int b) { + return a == b; + } + + @Specialization + public boolean equal(int a, double b) { + return a == b; + } + + @Specialization + public boolean equal(int a, BigInteger b) { + return BigInteger.valueOf(a).compareTo(b) == 0; + } + } + + @CoreMethod(names = "<=>", minArgs = 1, maxArgs = 1) + public abstract static class CompareNode extends CoreMethodNode { + + public CompareNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public CompareNode(CompareNode prev) { + super(prev); + } + + @Specialization + public int compare(int a, int b) { + return Integer.compare(a, b); + } + + @Specialization + public int compare(int a, double b) { + return Double.compare(a, b); + } + + @Specialization + public int compare(int a, BigInteger b) { + return BigInteger.valueOf(a).compareTo(b); + } + } + + @CoreMethod(names = "!=", minArgs = 1, maxArgs = 1) + public abstract static class NotEqualNode extends CoreMethodNode { + + public NotEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NotEqualNode(NotEqualNode prev) { + super(prev); + } + + @Specialization + public boolean notEqual(int a, int b) { + return a != b; + } + + @Specialization + public boolean notEqual(int a, double b) { + return a != b; + } + + @Specialization + public boolean notEqual(int a, BigInteger b) { + return BigInteger.valueOf(a).compareTo(b) != 0; + } + } + + @CoreMethod(names = ">=", minArgs = 1, maxArgs = 1) + public abstract static class GreaterEqualNode extends CoreMethodNode { + + public GreaterEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GreaterEqualNode(GreaterEqualNode prev) { + super(prev); + } + + @Specialization + public boolean greaterEqual(int a, int b) { + return a >= b; + } + + @Specialization + public boolean greaterEqual(int a, double b) { + return a >= b; + } + + @Specialization + public boolean greaterEqual(int a, BigInteger b) { + return BigInteger.valueOf(a).compareTo(b) >= 0; + } + } + + @CoreMethod(names = ">", minArgs = 1, maxArgs = 1) + public abstract static class GreaterNode extends CoreMethodNode { + + public GreaterNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GreaterNode(GreaterNode prev) { + super(prev); + } + + @Specialization + public boolean equal(int a, int b) { + return a > b; + } + + @Specialization + public boolean equal(int a, double b) { + return a > b; + } + + @Specialization + public boolean equal(int a, BigInteger b) { + return BigInteger.valueOf(a).compareTo(b) > 0; + } + } + + @CoreMethod(names = "~", maxArgs = 0) + public abstract static class ComplementNode extends CoreMethodNode { + + public ComplementNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ComplementNode(ComplementNode prev) { + super(prev); + } + + @Specialization + public int complement(int n) { + return ~n; + } + + } + + @CoreMethod(names = "&", minArgs = 1, maxArgs = 1) + public abstract static class BitAndNode extends CoreMethodNode { + + public BitAndNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BitAndNode(BitAndNode prev) { + super(prev); + } + + @Specialization + public int bitAnd(int a, int b) { + return a & b; + } + + @Specialization + public Object bitAnd(int a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(BigInteger.valueOf(a).and(b)); + } + } + + @CoreMethod(names = "|", minArgs = 1, maxArgs = 1) + public abstract static class BitOrNode extends CoreMethodNode { + + public BitOrNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BitOrNode(BitOrNode prev) { + super(prev); + } + + @Specialization + public int bitOr(int a, int b) { + return a | b; + } + + @Specialization + public Object bitOr(int a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(BigInteger.valueOf(a).or(b)); + } + } + + @CoreMethod(names = "^", minArgs = 1, maxArgs = 1) + public abstract static class BitXOrNode extends CoreMethodNode { + + public BitXOrNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BitXOrNode(BitXOrNode prev) { + super(prev); + } + + @Specialization + public int bitXOr(int a, int b) { + return a ^ b; + } + + @Specialization + public Object bitXOr(int a, BigInteger b) { + return GeneralConversions.fixnumOrBignum(BigInteger.valueOf(a).xor(b)); + } + } + + @CoreMethod(names = "<<", minArgs = 1, maxArgs = 1) + public abstract static class LeftShiftNode extends CoreMethodNode { + + public LeftShiftNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LeftShiftNode(LeftShiftNode prev) { + super(prev); + } + + @Specialization + public Object leftShift(int a, int b) { + if (b > 0) { + if (RubyFixnum.SIZE - Integer.numberOfLeadingZeros(a) + b > RubyFixnum.SIZE - 1) { + return GeneralConversions.fixnumOrBignum(BigInteger.valueOf(a).shiftLeft(b)); + } else { + return a << b; + } + } else { + if (-b >= Integer.SIZE) { + return 0; + } else { + return a >> -b; + } + } + } + + } + + @CoreMethod(names = ">>", minArgs = 1, maxArgs = 1) + public abstract static class RightShiftNode extends CoreMethodNode { + + public RightShiftNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public RightShiftNode(RightShiftNode prev) { + super(prev); + } + + @Specialization + public int rightShift(int a, int b) { + if (b > 0) { + return a >> b; + } else { + if (-b >= RubyFixnum.SIZE) { + return 0; + } else { + return a >> -b; + } + } + } + + } + + @CoreMethod(names = "[]", minArgs = 1, maxArgs = 1) + public abstract static class GetIndexNode extends CoreMethodNode { + + public GetIndexNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GetIndexNode(GetIndexNode prev) { + super(prev); + } + + @Specialization + public int getIndex(int self, int index) { + if ((self & (1 << index)) == 0) { + return 0; + } else { + return 1; + } + } + + } + + @CoreMethod(names = "chr", maxArgs = 0) + public abstract static class ChrNode extends CoreMethodNode { + + public ChrNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ChrNode(ChrNode prev) { + super(prev); + } + + @Specialization + public RubyString chr(int n) { + // TODO(CS): not sure about encoding here + return getContext().makeString((char) n); + } + + } + + @CoreMethod(names = "inspect", maxArgs = 0) + public abstract static class InpsectNode extends CoreMethodNode { + + public InpsectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InpsectNode(InpsectNode prev) { + super(prev); + } + + @Specialization + public RubyString inspect(int n) { + return getContext().makeString(Integer.toString(n)); + } + + } + + @CoreMethod(names = "nonzero?", maxArgs = 0) + public abstract static class NonZeroNode extends CoreMethodNode { + + public NonZeroNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NonZeroNode(NonZeroNode prev) { + super(prev); + } + + @Specialization + public Object nonZero(int value) { + if (value == 0) { + return false; + } else { + return value; + } + } + + } + + @CoreMethod(names = "size", needsSelf = false, maxArgs = 0) + public abstract static class SizeNode extends CoreMethodNode { + + public SizeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SizeNode(SizeNode prev) { + super(prev); + } + + @Specialization + public int size() { + return Integer.SIZE / Byte.SIZE; + } + + } + + @CoreMethod(names = "step", needsBlock = true, minArgs = 2, maxArgs = 2) + public abstract static class StepNode extends YieldingCoreMethodNode { + + public StepNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public StepNode(StepNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder step(VirtualFrame frame, int from, int to, int step, RubyProc block) { + for (int i = from; i <= to; i += step) { + yield(frame, block, i); + } + + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "times", needsBlock = true, maxArgs = 0) + public abstract static class TimesNode extends YieldingCoreMethodNode { + + public TimesNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public TimesNode(TimesNode prev) { + super(prev); + } + + @Specialization + public int times(VirtualFrame frame, int n, RubyProc block) { + for (int i = 0; i < n; i++) { + yield(frame, block, i); + } + + return n; + } + + } + + @CoreMethod(names = {"to_i", "to_int"}, maxArgs = 0) + public abstract static class ToINode extends CoreMethodNode { + + public ToINode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToINode(ToINode prev) { + super(prev); + } + + @Specialization + public int toI(int n) { + return n; + } + + } + + @CoreMethod(names = "to_f", maxArgs = 0) + public abstract static class ToFNode extends CoreMethodNode { + + public ToFNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToFNode(ToFNode prev) { + super(prev); + } + + @Specialization + public double toF(int n) { + return n; + } + + } + + @CoreMethod(names = "to_s", maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toS(int n) { + return getContext().makeString(Integer.toString(n)); + } + + } + + @CoreMethod(names = "upto", needsBlock = true, minArgs = 1, maxArgs = 1) + public abstract static class UpToNode extends YieldingCoreMethodNode { + + public UpToNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public UpToNode(UpToNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder upto(VirtualFrame frame, int from, int to, RubyProc block) { + for (int i = from; i <= to; i++) { + yield(frame, block, i); + } + + return NilPlaceholder.INSTANCE; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/FloatNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/FloatNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.math.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@CoreClass(name = "Float") +public abstract class FloatNodes { + + @CoreMethod(names = "+@", maxArgs = 0) + public abstract static class PosNode extends CoreMethodNode { + + public PosNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PosNode(PosNode prev) { + super(prev); + } + + @Specialization + public double pos(double value) { + return value; + } + + } + + @CoreMethod(names = "-@", maxArgs = 0) + public abstract static class NegNode extends CoreMethodNode { + + public NegNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NegNode(NegNode prev) { + super(prev); + } + + @Specialization + public double neg(double value) { + return -value; + } + + } + + @CoreMethod(names = "+", minArgs = 1, maxArgs = 1) + public abstract static class AddNode extends CoreMethodNode { + + public AddNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AddNode(AddNode prev) { + super(prev); + } + + @Specialization + public double add(double a, int b) { + return a + b; + } + + @Specialization + public double add(double a, double b) { + return a + b; + } + + @Specialization + public double add(double a, BigInteger b) { + return a + b.doubleValue(); + } + + } + + @CoreMethod(names = "-", minArgs = 1, maxArgs = 1) + public abstract static class SubNode extends CoreMethodNode { + + public SubNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SubNode(SubNode prev) { + super(prev); + } + + @Specialization + public double sub(double a, int b) { + return a - b; + } + + @Specialization + public double sub(double a, double b) { + return a - b; + } + + @Specialization + public double sub(double a, BigInteger b) { + return a - b.doubleValue(); + } + + } + + @CoreMethod(names = "*", minArgs = 1, maxArgs = 1) + public abstract static class MulNode extends CoreMethodNode { + + public MulNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MulNode(MulNode prev) { + super(prev); + } + + @Specialization + public double mul(double a, int b) { + return a * b; + } + + @Specialization + public double mul(double a, double b) { + return a * b; + } + + @Specialization + public double mul(double a, BigInteger b) { + return a * b.doubleValue(); + } + + } + + @CoreMethod(names = "**", minArgs = 1, maxArgs = 1) + public abstract static class PowNode extends CoreMethodNode { + + public PowNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PowNode(PowNode prev) { + super(prev); + } + + @Specialization + public double mul(double a, int b) { + return Math.pow(a, b); + } + + @Specialization + public double mul(double a, double b) { + return Math.pow(a, b); + } + + @Specialization + public double mul(double a, BigInteger b) { + return Math.pow(a, b.doubleValue()); + } + + } + + @CoreMethod(names = "/", minArgs = 1, maxArgs = 1) + public abstract static class DivNode extends CoreMethodNode { + + public DivNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DivNode(DivNode prev) { + super(prev); + } + + @Specialization + public double div(double a, int b) { + return a / b; + } + + @Specialization + public double div(double a, double b) { + return a / b; + } + + @Specialization + public double div(double a, BigInteger b) { + return a / b.doubleValue(); + } + + } + + @CoreMethod(names = "%", minArgs = 1, maxArgs = 1) + public abstract static class ModNode extends CoreMethodNode { + + public ModNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ModNode(ModNode prev) { + super(prev); + } + + @Specialization + public double mod(@SuppressWarnings("unused") double a, @SuppressWarnings("unused") int b) { + throw new UnsupportedOperationException(); + } + + @Specialization + public double mod(@SuppressWarnings("unused") double a, @SuppressWarnings("unused") double b) { + throw new UnsupportedOperationException(); + } + + @Specialization + public double mod(@SuppressWarnings("unused") double a, @SuppressWarnings("unused") BigInteger b) { + throw new UnsupportedOperationException(); + } + + } + + @CoreMethod(names = "divmod", minArgs = 1, maxArgs = 1) + public abstract static class DivModNode extends CoreMethodNode { + + public DivModNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DivModNode(DivModNode prev) { + super(prev); + } + + @Specialization + public RubyArray divMod(@SuppressWarnings("unused") double a, @SuppressWarnings("unused") int b) { + throw new UnsupportedOperationException(); + } + + @Specialization + public RubyArray divMod(@SuppressWarnings("unused") double a, @SuppressWarnings("unused") double b) { + throw new UnsupportedOperationException(); + } + + @Specialization + public RubyArray divMod(@SuppressWarnings("unused") double a, @SuppressWarnings("unused") BigInteger b) { + throw new UnsupportedOperationException(); + } + + } + + @CoreMethod(names = "<", minArgs = 1, maxArgs = 1) + public abstract static class LessNode extends CoreMethodNode { + + public LessNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LessNode(LessNode prev) { + super(prev); + } + + @Specialization + public boolean less(double a, int b) { + return a < b; + } + + @Specialization + public boolean less(double a, double b) { + return a < b; + } + + @Specialization + public boolean less(double a, BigInteger b) { + return BigInteger.valueOf((long) a).compareTo(b) < 0; + } + } + + @CoreMethod(names = "<=", minArgs = 1, maxArgs = 1) + public abstract static class LessEqualNode extends CoreMethodNode { + + public LessEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LessEqualNode(LessEqualNode prev) { + super(prev); + } + + @Specialization + public boolean lessEqual(double a, int b) { + return a <= b; + } + + @Specialization + public boolean lessEqual(double a, double b) { + return a <= b; + } + + @Specialization + public boolean lessEqual(double a, BigInteger b) { + return BigInteger.valueOf((long) a).compareTo(b) <= 0; + } + } + + @CoreMethod(names = "==", minArgs = 1, maxArgs = 1) + public abstract static class EqualNode extends CoreMethodNode { + + public EqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EqualNode(EqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(double a, int b) { + return a == b; + } + + @Specialization + public boolean equal(double a, double b) { + return a == b; + } + + @Specialization + public boolean equal(double a, BigInteger b) { + return BigInteger.valueOf((long) a).compareTo(b) == 0; + } + } + + @CoreMethod(names = "!=", minArgs = 1, maxArgs = 1) + public abstract static class NotEqualNode extends CoreMethodNode { + + public NotEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NotEqualNode(NotEqualNode prev) { + super(prev); + } + + @Specialization + public boolean notEqual(double a, int b) { + return a != b; + } + + @Specialization + public boolean notEqual(double a, double b) { + return a != b; + } + + @Specialization + public boolean notEqual(double a, BigInteger b) { + return BigInteger.valueOf((long) a).compareTo(b) != 0; + } + } + + @CoreMethod(names = ">=", minArgs = 1, maxArgs = 1) + public abstract static class GreaterEqualNode extends CoreMethodNode { + + public GreaterEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GreaterEqualNode(GreaterEqualNode prev) { + super(prev); + } + + @Specialization + public boolean greaterEqual(double a, int b) { + return a >= b; + } + + @Specialization + public boolean greaterEqual(double a, double b) { + return a >= b; + } + + @Specialization + public boolean greaterEqual(double a, BigInteger b) { + return BigInteger.valueOf((long) a).compareTo(b) >= 0; + } + } + + @CoreMethod(names = ">", minArgs = 1, maxArgs = 1) + public abstract static class GreaterNode extends CoreMethodNode { + + public GreaterNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GreaterNode(GreaterNode prev) { + super(prev); + } + + @Specialization + public boolean equal(double a, int b) { + return a > b; + } + + @Specialization + public boolean equal(double a, double b) { + return a > b; + } + + @Specialization + public boolean equal(double a, BigInteger b) { + return BigInteger.valueOf((long) a).compareTo(b) > 0; + } + } + + @CoreMethod(names = "abs", maxArgs = 0) + public abstract static class AbsNode extends CoreMethodNode { + + public AbsNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AbsNode(AbsNode prev) { + super(prev); + } + + @Specialization + public double abs(double n) { + return Math.abs(n); + } + + } + + @CoreMethod(names = "inspect", maxArgs = 0) + public abstract static class InpsectNode extends CoreMethodNode { + + public InpsectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InpsectNode(InpsectNode prev) { + super(prev); + } + + @Specialization + public RubyString inspect(double n) { + return getContext().makeString(Double.toString(n)); + } + + } + + @CoreMethod(names = "nonzero?", maxArgs = 0) + public abstract static class NonZeroNode extends CoreMethodNode { + + public NonZeroNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NonZeroNode(NonZeroNode prev) { + super(prev); + } + + @Specialization + public Object nonZero(double value) { + if (value == 0) { + return false; + } else { + return value; + } + } + + } + + @CoreMethod(names = "to_s", maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toS(double value) { + return getContext().makeString(Double.toString(value)); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/HashNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/HashNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@CoreClass(name = "Hash") +public abstract class HashNodes { + + @CoreMethod(names = "[]", isModuleMethod = true, needsSelf = false, isSplatted = true) + public abstract static class ConstructNode extends CoreMethodNode { + + public ConstructNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ConstructNode(ConstructNode prev) { + super(prev); + } + + @Specialization + public RubyHash construct(Object[] args) { + final RubyHash hash = new RubyHash(getContext().getCoreLibrary().getHashClass()); + + if (args.length == 1) { + final RubyArray array = (RubyArray) args[0]; + + for (int n = 0; n < array.size(); n++) { + final RubyArray keyValue = (RubyArray) array.get(n); + hash.put(keyValue.get(0), keyValue.get(1)); + } + } else { + assert args.length % 2 == 0; + + for (int n = 0; n < args.length; n += 2) { + hash.put(args[n], args[n + 1]); + } + } + + return hash; + } + + } + + @CoreMethod(names = "[]", minArgs = 1, maxArgs = 1) + public abstract static class GetIndexNode extends CoreMethodNode { + + public GetIndexNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GetIndexNode(GetIndexNode prev) { + super(prev); + } + + @Specialization + public Object construct(VirtualFrame frame, RubyHash hash, Object index) { + final Object value = hash.get(index); + + if (value == null) { + if (hash.defaultBlock == null) { + return NilPlaceholder.INSTANCE; + } else { + return hash.defaultBlock.call(frame.pack(), hash, index); + } + } else { + return value; + } + } + + } + + @CoreMethod(names = "[]=", minArgs = 2, maxArgs = 2) + public abstract static class SetIndexNode extends CoreMethodNode { + + public SetIndexNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SetIndexNode(SetIndexNode prev) { + super(prev); + } + + @Specialization + public Object construct(RubyHash hash, Object index, Object value) { + hash.put(index, value); + return value; + } + + } + + @CoreMethod(names = "delete", minArgs = 1, maxArgs = 1) + public abstract static class DeleteNode extends CoreMethodNode { + + public DeleteNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DeleteNode(DeleteNode prev) { + super(prev); + } + + @Specialization + public Object delete(RubyHash hash, Object index) { + hash.checkFrozen(); + + final Object value = hash.getMap().remove(index); + + if (value == null) { + return NilPlaceholder.INSTANCE; + } else { + return value; + } + } + + } + + @CoreMethod(names = "each", needsBlock = true, maxArgs = 0) + public abstract static class EachNode extends YieldingCoreMethodNode { + + public EachNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EachNode(EachNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder each(VirtualFrame frame, RubyHash hash, RubyProc block) { + for (Map.Entry entry : hash.storage.entrySet()) { + yield(frame, block, entry.getKey(), entry.getValue()); + } + + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "empty?", maxArgs = 0) + public abstract static class EmptyNode extends CoreMethodNode { + + public EmptyNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EmptyNode(EmptyNode prev) { + super(prev); + } + + @Specialization + public boolean empty(RubyHash hash) { + return hash.storage.isEmpty(); + } + + } + + @CoreMethod(names = "initialize", needsBlock = true, maxArgs = 0) + public abstract static class InitializeNode extends CoreMethodNode { + + public InitializeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InitializeNode(InitializeNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder initialize(RubyHash hash, @SuppressWarnings("unused") UndefinedPlaceholder block) { + hash.initialize(null); + return NilPlaceholder.INSTANCE; + } + + @Specialization + public NilPlaceholder initialize(RubyHash hash, RubyProc block) { + hash.initialize(block); + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = {"map", "collect"}, needsBlock = true, maxArgs = 0) + public abstract static class MapNode extends YieldingCoreMethodNode { + + public MapNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MapNode(MapNode prev) { + super(prev); + } + + @Specialization + public RubyArray map(VirtualFrame frame, RubyHash hash, RubyProc block) { + final RubyArray result = new RubyArray(getContext().getCoreLibrary().getArrayClass()); + + for (Map.Entry entry : hash.storage.entrySet()) { + result.push(yield(frame, block, entry.getKey(), entry.getValue())); + } + + return result; + } + + } + + @CoreMethod(names = "key?", minArgs = 1, maxArgs = 1) + public abstract static class KeyNode extends CoreMethodNode { + + public KeyNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public KeyNode(KeyNode prev) { + super(prev); + } + + @Specialization + public boolean key(RubyHash hash, Object key) { + return hash.storage.containsKey(key); + } + + } + + @CoreMethod(names = "keys", maxArgs = 0) + public abstract static class KeysNode extends CoreMethodNode { + + public KeysNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public KeysNode(KeysNode prev) { + super(prev); + } + + @Specialization + public RubyArray keys(RubyHash hash) { + final RubyArray array = new RubyArray(getContext().getCoreLibrary().getArrayClass()); + + for (Object key : hash.storage.keySet()) { + array.push(key); + } + + return array; + } + + } + + @CoreMethod(names = "size", maxArgs = 0) + public abstract static class SizeNode extends CoreMethodNode { + + public SizeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SizeNode(SizeNode prev) { + super(prev); + } + + @Specialization + public int size(RubyHash hash) { + return hash.storage.size(); + } + + } + + @CoreMethod(names = "values", maxArgs = 0) + public abstract static class ValuesNode extends CoreMethodNode { + + public ValuesNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ValuesNode(ValuesNode prev) { + super(prev); + } + + @Specialization + public RubyArray values(RubyHash hash) { + final RubyArray array = new RubyArray(getContext().getCoreLibrary().getArrayClass()); + + for (Object value : hash.storage.values()) { + array.push(value); + } + + return array; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/InterpolatedStringNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/InterpolatedStringNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A list of expressions to build up into a string. + */ +@NodeInfo(shortName = "interpolated-string") +public final class InterpolatedStringNode extends RubyNode { + + @CompilationFinal private int expectedLength = 64; + + @Children protected final RubyNode[] children; + + public InterpolatedStringNode(RubyContext context, SourceSection sourceSection, RubyNode[] children) { + super(context, sourceSection); + this.children = adoptChildren(children); + } + + @ExplodeLoop + @Override + public Object execute(VirtualFrame frame) { + final StringBuilder builder = new StringBuilder(expectedLength); + + for (int n = 0; n < children.length; n++) { + builder.append(children[n].execute(frame).toString()); + } + + if (builder.length() > expectedLength) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + expectedLength = builder.length() * 2; + } + + return getContext().makeString(builder.toString()); + } + + @ExplodeLoop + @Override + public void executeVoid(VirtualFrame frame) { + for (int n = 0; n < children.length; n++) { + children[n].executeVoid(frame); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/KernelNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/KernelNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,823 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.io.*; +import java.math.*; +import java.util.*; + +import com.oracle.truffle.api.CompilerDirectives.SlowPath; +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.nodes.cast.*; +import com.oracle.truffle.ruby.nodes.control.*; +import com.oracle.truffle.ruby.nodes.literal.*; +import com.oracle.truffle.ruby.nodes.yield.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.configuration.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.objects.*; +import com.oracle.truffle.ruby.runtime.subsystems.*; + +@CoreClass(name = "Kernel") +public abstract class KernelNodes { + + @CoreMethod(names = "Array", isModuleMethod = true, needsSelf = false, isSplatted = true) + public abstract static class ArrayNode extends CoreMethodNode { + + public ArrayNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ArrayNode(ArrayNode prev) { + super(prev); + } + + @Specialization + public RubyArray array(Object[] args) { + if (args.length == 1 && args[0] instanceof RubyArray) { + return (RubyArray) args[0]; + } else { + return RubyArray.specializedFromObjects(getContext().getCoreLibrary().getArrayClass(), args); + } + } + + } + + @CoreMethod(names = "at_exit", isModuleMethod = true, needsSelf = false, needsBlock = true, maxArgs = 0) + public abstract static class AtExitNode extends CoreMethodNode { + + public AtExitNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AtExitNode(AtExitNode prev) { + super(prev); + } + + @Specialization + public Object atExit(RubyProc block) { + getContext().getAtExitManager().add(block); + return NilPlaceholder.INSTANCE; + } + } + + @CoreMethod(names = "binding", isModuleMethod = true, needsSelf = true, maxArgs = 0) + public abstract static class BindingNode extends CoreMethodNode { + + public BindingNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BindingNode(BindingNode prev) { + super(prev); + } + + @Specialization + public Object binding(VirtualFrame frame, Object self) { + return new RubyBinding(getContext().getCoreLibrary().getBindingClass(), self, frame.getCaller().unpack().materialize()); + } + } + + @CoreMethod(names = "block_given?", isModuleMethod = true, needsSelf = false, maxArgs = 0) + public abstract static class BlockGivenNode extends CoreMethodNode { + + public BlockGivenNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BlockGivenNode(BlockGivenNode prev) { + super(prev); + } + + @Specialization + public boolean blockGiven(VirtualFrame frame) { + return frame.getCaller().unpack().getArguments(RubyArguments.class).getBlock() != null; + } + } + + // TODO(CS): should hide this in a feature + + @CoreMethod(names = "callcc", isModuleMethod = true, needsSelf = false, needsBlock = true, maxArgs = 0) + public abstract static class CallccNode extends CoreMethodNode { + + public CallccNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public CallccNode(CallccNode prev) { + super(prev); + } + + @Specialization + public Object callcc(RubyProc block) { + final RubyContext context = getContext(); + + if (block == null) { + // TODO(CS): should really have acceptsBlock and needsBlock to do this automatically + throw new RaiseException(context.getCoreLibrary().localJumpError("no block given")); + } + + final RubyContinuation continuation = new RubyContinuation(context.getCoreLibrary().getContinuationClass()); + return continuation.enter(block); + } + } + + @CoreMethod(names = "catch", isModuleMethod = true, needsSelf = false, needsBlock = true, minArgs = 1, maxArgs = 1) + public abstract static class CatchNode extends YieldingCoreMethodNode { + + public CatchNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public CatchNode(CatchNode prev) { + super(prev); + } + + @Specialization + public Object doCatch(VirtualFrame frame, Object tag, RubyProc block) { + try { + return yield(frame, block); + } catch (ThrowException e) { + if (e.getTag().equals(tag)) { + // TODO(cs): unset rather than set to Nil? + getContext().getCoreLibrary().getGlobalVariablesObject().setInstanceVariable("$!", NilPlaceholder.INSTANCE); + return e.getValue(); + } else { + throw e; + } + } + } + } + + @CoreMethod(names = "eval", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 2) + public abstract static class EvalNode extends CoreMethodNode { + + public EvalNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EvalNode(EvalNode prev) { + super(prev); + } + + @Specialization + public Object eval(RubyString source, @SuppressWarnings("unused") UndefinedPlaceholder binding) { + return getContext().eval(source.toString()); + } + + @Specialization + public Object eval(RubyString source, RubyBinding binding) { + return getContext().eval(source.toString(), binding); + } + + } + + @CoreMethod(names = "exec", isModuleMethod = true, needsSelf = false, minArgs = 1, isSplatted = true) + public abstract static class ExecNode extends CoreMethodNode { + + public ExecNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ExecNode(ExecNode prev) { + super(prev); + } + + @Specialization + public Object require(Object[] args) { + final String[] commandLine = new String[args.length]; + + for (int n = 0; n < args.length; n++) { + commandLine[n] = args[n].toString(); + } + + exec(getContext(), commandLine); + + return null; + } + + @SlowPath + private static void exec(RubyContext context, String[] commandLine) { + context.implementationMessage("starting child process to simulate exec: "); + + for (int n = 0; n < commandLine.length; n++) { + if (n > 0) { + System.err.print(" "); + } + + System.err.print(commandLine[n]); + } + + final ProcessBuilder builder = new ProcessBuilder(commandLine); + builder.inheritIO(); + + final RubyHash env = (RubyHash) context.getCoreLibrary().getObjectClass().lookupConstant("ENV"); + + for (Map.Entry entry : env.getMap().entrySet()) { + builder.environment().put(entry.getKey().toString(), entry.getValue().toString()); + } + + Process process; + + try { + process = builder.start(); + } catch (IOException e) { + // TODO(cs): proper Ruby exception + throw new RuntimeException(e); + } + + int exitCode; + + while (true) { + try { + exitCode = process.waitFor(); + break; + } catch (InterruptedException e) { + continue; + } + } + + context.implementationMessage("child process simulating exec finished"); + + System.exit(exitCode); + } + + } + + @CoreMethod(names = "exit", isModuleMethod = true, needsSelf = false, minArgs = 0, maxArgs = 1) + public abstract static class ExitNode extends CoreMethodNode { + + public ExitNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ExitNode(ExitNode prev) { + super(prev); + } + + @Specialization + public Object exit(@SuppressWarnings("unused") UndefinedPlaceholder exitCode) { + getContext().shutdown(); + System.exit(0); + return null; + } + + @Specialization + public Object exit(int exitCode) { + getContext().shutdown(); + System.exit(exitCode); + return null; + } + + } + + @CoreMethod(names = "gets", isModuleMethod = true, needsSelf = false, maxArgs = 0) + public abstract static class GetsNode extends CoreMethodNode { + + public GetsNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GetsNode(GetsNode prev) { + super(prev); + } + + @Specialization + public RubyString gets(VirtualFrame frame) { + final RubyContext context = getContext(); + + final ThreadManager threadManager = context.getThreadManager(); + + RubyString line; + + try { + final RubyThread runningThread = threadManager.leaveGlobalLock(); + + try { + line = context.makeString(context.getConfiguration().getInputReader().readLine("")); + } finally { + threadManager.enterGlobalLock(runningThread); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + // Set the local variable $_ in the caller + + final Frame unpacked = frame.getCaller().unpack(); + final FrameSlot slot = unpacked.getFrameDescriptor().findFrameSlot("$_"); + + if (slot != null) { + unpacked.setObject(slot, line); + } + + return line; + } + } + + @CoreMethod(names = "Integer", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class IntegerNode extends CoreMethodNode { + + @Child protected DispatchHeadNode toInt; + + public IntegerNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + toInt = adoptChild(new DispatchHeadNode(context, getSourceSection(), "to_int", false)); + } + + public IntegerNode(IntegerNode prev) { + super(prev); + toInt = adoptChild(prev.toInt); + } + + @Specialization + public int integer(int value) { + return value; + } + + @Specialization + public BigInteger integer(BigInteger value) { + return value; + } + + @Specialization + public int integer(double value) { + return (int) value; + } + + @Specialization + public Object integer(RubyString value) { + return value.toInteger(); + } + + @Specialization + public Object integer(VirtualFrame frame, Object value) { + return toInt.dispatch(frame, value, null); + } + + } + + @CoreMethod(names = "lambda", isModuleMethod = true, needsBlock = true, maxArgs = 0) + public abstract static class LambdaNode extends CoreMethodNode { + + public LambdaNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LambdaNode(LambdaNode prev) { + super(prev); + } + + @Specialization + public RubyProc proc(Object self, RubyProc block) { + return new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.LAMBDA, self, block, block.getMethod()); + + } + } + + @CoreMethod(names = "load", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class LoadNode extends CoreMethodNode { + + public LoadNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LoadNode(LoadNode prev) { + super(prev); + } + + @Specialization + public boolean load(RubyString file) { + getContext().loadFile(file.toString()); + return true; + } + } + + @CoreMethod(names = "loop", isModuleMethod = true, needsSelf = false, maxArgs = 0) + public abstract static class LoopNode extends CoreMethodNode { + + @Child protected WhileNode whileNode; + + public LoopNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + whileNode = adoptChild(new WhileNode(context, sourceSection, BooleanCastNodeFactory.create(context, sourceSection, new BooleanLiteralNode(context, sourceSection, true)), new YieldNode( + context, getSourceSection(), new RubyNode[]{}))); + } + + public LoopNode(LoopNode prev) { + super(prev); + whileNode = adoptChild(prev.whileNode); + } + + @Specialization + public Object loop(VirtualFrame frame) { + return whileNode.execute(frame); + } + } + + @CoreMethod(names = "print", isModuleMethod = true, needsSelf = false, isSplatted = true) + public abstract static class PrintNode extends CoreMethodNode { + + public PrintNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PrintNode(PrintNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder print(Object[] args) { + final RubyContext context = getContext(); + final ThreadManager threadManager = context.getThreadManager(); + + final RubyThread runningThread = threadManager.leaveGlobalLock(); + + try { + for (Object arg : args) { + /* + * TODO(cs): If it's a RubyString and made up of bytes, just write the bytes out + * - using toString will mess up the encoding. We need to stop using toString + * everywhere, and write our own bytes, possibly using JRuby's library for this. + */ + + if (arg instanceof RubyString && !((RubyString) arg).isFromJavaString()) { + try { + context.getConfiguration().getStandardOut().write(((RubyString) arg).getBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + context.getConfiguration().getStandardOut().print(arg); + } + } + } finally { + threadManager.enterGlobalLock(runningThread); + } + + return NilPlaceholder.INSTANCE; + } + } + + @CoreMethod(names = "printf", isModuleMethod = true, needsSelf = false, isSplatted = true) + public abstract static class PrintfNode extends CoreMethodNode { + + public PrintfNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PrintfNode(PrintfNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder printf(Object[] args) { + final RubyContext context = getContext(); + final ThreadManager threadManager = context.getThreadManager(); + + if (args.length > 0) { + final String format = ((RubyString) args[0]).toString(); + final List values = Arrays.asList(args).subList(1, args.length); + + final RubyThread runningThread = threadManager.leaveGlobalLock(); + + try { + StringFormatter.format(context.getConfiguration().getStandardOut(), format, values); + } finally { + threadManager.enterGlobalLock(runningThread); + } + } + + return NilPlaceholder.INSTANCE; + } + } + + /* + * Kernel#pretty_inspect is normally part of stdlib, in pp.rb, but we aren't able to execute + * that file yet. Instead we implement a very simple version here, which is the solution + * suggested by RubySpec. + */ + + @CoreMethod(names = "pretty_inspect", maxArgs = 0) + public abstract static class PrettyInspectNode extends CoreMethodNode { + + @Child protected DispatchHeadNode toS; + + public PrettyInspectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + toS = adoptChild(new DispatchHeadNode(context, getSourceSection(), "to_s", false)); + } + + public PrettyInspectNode(PrettyInspectNode prev) { + super(prev); + toS = adoptChild(prev.toS); + } + + @Specialization + public Object prettyInspect(VirtualFrame frame, Object self) { + return toS.dispatch(frame, self, null); + + } + } + + @CoreMethod(names = "proc", isModuleMethod = true, needsBlock = true, maxArgs = 0, versions = RubyVersion.RUBY_18) + public abstract static class Proc18Node extends CoreMethodNode { + + public Proc18Node(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public Proc18Node(Proc18Node prev) { + super(prev); + } + + @Specialization + public RubyProc proc(Object self, RubyProc block) { + return new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.LAMBDA, self, block, block.getMethod()); + + } + } + + @CoreMethod(names = "proc", isModuleMethod = true, needsBlock = true, maxArgs = 0, versions = {RubyVersion.RUBY_19, RubyVersion.RUBY_20, RubyVersion.RUBY_21}) + public abstract static class ProcNode extends CoreMethodNode { + + public ProcNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ProcNode(ProcNode prev) { + super(prev); + } + + @Specialization + public RubyProc proc(Object self, RubyProc block) { + return new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.PROC, self, block, block.getMethod()); + + } + } + + @CoreMethod(names = "puts", isModuleMethod = true, needsSelf = false, isSplatted = true) + public abstract static class PutsNode extends CoreMethodNode { + + public PutsNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PutsNode(PutsNode prev) { + super(prev); + } + + @ExplodeLoop + @Specialization + public NilPlaceholder puts(Object[] args) { + final RubyContext context = getContext(); + final ThreadManager threadManager = context.getThreadManager(); + final PrintStream standardOut = context.getConfiguration().getStandardOut(); + + final RubyThread runningThread = threadManager.leaveGlobalLock(); + + try { + if (args.length == 0) { + standardOut.println(); + } else { + for (int n = 0; n < args.length; n++) { + puts(context, standardOut, args[n]); + } + } + } finally { + threadManager.enterGlobalLock(runningThread); + } + + return NilPlaceholder.INSTANCE; + } + + @SlowPath + private void puts(RubyContext context, PrintStream standardOut, Object value) { + if (value instanceof RubyArray) { + final RubyArray array = (RubyArray) value; + + for (int n = 0; n < array.size(); n++) { + puts(context, standardOut, array.get(n)); + } + } else { + // TODO(CS): slow path send + standardOut.println(context.getCoreLibrary().box(value).send("to_s", null)); + } + } + + } + + @CoreMethod(names = "raise", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 2) + public abstract static class RaiseNode extends CoreMethodNode { + + @Child protected DispatchHeadNode initialize; + + public RaiseNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + initialize = adoptChild(new DispatchHeadNode(context, getSourceSection(), "initialize", false)); + } + + public RaiseNode(RaiseNode prev) { + super(prev); + initialize = adoptChild(prev.initialize); + } + + @Specialization(order = 1) + public Object raise(VirtualFrame frame, RubyString message, @SuppressWarnings("unused") UndefinedPlaceholder undefined) { + return raise(frame, getContext().getCoreLibrary().getRuntimeErrorClass(), message); + } + + @Specialization(order = 2) + public Object raise(VirtualFrame frame, RubyClass exceptionClass, @SuppressWarnings("unused") UndefinedPlaceholder undefined) { + return raise(frame, exceptionClass, getContext().makeString("")); + } + + @Specialization(order = 3) + public Object raise(VirtualFrame frame, RubyClass exceptionClass, RubyString message) { + final RubyContext context = getContext(); + + if (context.getConfiguration().getPrintRubyExceptions()) { + context.implementationMessage("Ruby raise: %s", message); + new Exception().printStackTrace(); + } + + final RubyBasicObject exception = exceptionClass.newInstance(); + initialize.dispatch(frame, exception, null, message); + throw new RaiseException(exception); + } + + } + + @CoreMethod(names = "require", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class RequireNode extends CoreMethodNode { + + public RequireNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public RequireNode(RequireNode prev) { + super(prev); + } + + @Specialization + public boolean require(RubyString feature) { + try { + getContext().getFeatureManager().require(feature.toString()); + } catch (IOException e) { + throw new RuntimeException(e); + } + + return true; + } + } + + @CoreMethod(names = "set_trace_func", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class SetTraceFuncNode extends CoreMethodNode { + + public SetTraceFuncNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SetTraceFuncNode(SetTraceFuncNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder setTraceFunc(NilPlaceholder proc) { + getContext().getTraceManager().setTraceProc(null); + return proc; + } + + @Specialization + public RubyProc setTraceFunc(RubyProc proc) { + getContext().getTraceManager().setTraceProc(proc); + return proc; + } + + } + + @CoreMethod(names = "String", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class StringNode extends CoreMethodNode { + + @Child protected DispatchHeadNode toS; + + public StringNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + toS = adoptChild(new DispatchHeadNode(context, getSourceSection(), "to_s", false)); + } + + public StringNode(StringNode prev) { + super(prev); + toS = adoptChild(prev.toS); + } + + @Specialization + public RubyString string(int value) { + return getContext().makeString(Integer.toString(value)); + } + + @Specialization + public RubyString string(BigInteger value) { + return getContext().makeString(value.toString()); + } + + @Specialization + public RubyString string(double value) { + return getContext().makeString(Double.toString(value)); + } + + @Specialization + public RubyString string(RubyString value) { + return value; + } + + @Specialization + public Object string(VirtualFrame frame, Object value) { + return toS.dispatch(frame, value, null); + } + + } + + @CoreMethod(names = "sleep", isModuleMethod = true, needsSelf = false, maxArgs = 1) + public abstract static class SleepNode extends CoreMethodNode { + + public SleepNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SleepNode(SleepNode prev) { + super(prev); + } + + @Specialization + public double sleep(double duration) { + final RubyContext context = getContext(); + + final RubyThread runningThread = context.getThreadManager().leaveGlobalLock(); + + try { + final long start = System.nanoTime(); + + try { + Thread.sleep((long) (duration * 1000)); + } catch (InterruptedException e) { + // Ignore interruption + } + + final long end = System.nanoTime(); + + return (end - start) / 1e9; + } finally { + context.getThreadManager().enterGlobalLock(runningThread); + } + } + + @Specialization + public double sleep(int duration) { + return sleep((double) duration); + } + + } + + @CoreMethod(names = "throw", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 2) + public abstract static class ThrowNode extends CoreMethodNode { + + public ThrowNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ThrowNode(ThrowNode prev) { + super(prev); + } + + @Specialization + public Object doThrow(Object tag, UndefinedPlaceholder value) { + return doThrow(tag, (Object) value); + } + + @Specialization + public Object doThrow(Object tag, Object value) { + if (value instanceof UndefinedPlaceholder) { + throw new ThrowException(tag, NilPlaceholder.INSTANCE); + } else { + throw new ThrowException(tag, value); + } + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/MainNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/MainNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "main") +public abstract class MainNodes { + + @CoreMethod(names = "include", isSplatted = true, minArgs = 1) + public abstract static class IncludeNode extends CoreMethodNode { + + public IncludeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public IncludeNode(IncludeNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder include(RubyObject main, Object[] args) { + // TODO(cs): copied from Module - but where does this method really come from? + + // Note that we traverse the arguments backwards + + for (int n = args.length - 1; n >= 0; n--) { + if (args[n] instanceof RubyModule) { + final RubyModule included = (RubyModule) args[n]; + + // Note that we do appear to do full method lookup here + included.getLookupNode().lookupMethod("append_features").call(null, included, null, main.getSingletonClass()); + + // TODO(cs): call included hook + } + } + + return NilPlaceholder.INSTANCE; + } + } + + @CoreMethod(names = "to_s", needsSelf = false, maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toS() { + return getContext().makeString("main"); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/MatchDataNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/MatchDataNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@CoreClass(name = "MatchData") +public abstract class MatchDataNodes { + + @CoreMethod(names = "[]", minArgs = 1, maxArgs = 1) + public abstract static class GetIndexNode extends CoreMethodNode { + + public GetIndexNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GetIndexNode(GetIndexNode prev) { + super(prev); + } + + @Specialization + public Object getIndex(RubyMatchData matchData, int index) { + return matchData.getValues()[index]; + } + + } + + @CoreMethod(names = "to_a", maxArgs = 0) + public abstract static class ToANode extends CoreMethodNode { + + public ToANode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToANode(ToANode prev) { + super(prev); + } + + @Specialization + public RubyArray toA(RubyMatchData matchData) { + return RubyArray.specializedFromObjects(getContext().getCoreLibrary().getArrayClass(), matchData.getValues()); + } + + } + + @CoreMethod(names = "values_at", isSplatted = true) + public abstract static class ValuesAtNode extends CoreMethodNode { + + public ValuesAtNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ValuesAtNode(ValuesAtNode prev) { + super(prev); + } + + @Specialization + public RubyArray valuesAt(RubyMatchData matchData, Object[] args) { + final int[] indicies = new int[args.length]; + + for (int n = 0; n < args.length; n++) { + indicies[n] = (int) args[n]; + } + + return RubyArray.specializedFromObjects(getContext().getCoreLibrary().getArrayClass(), matchData.valuesAt(indicies)); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/MathNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/MathNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.math.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; + +@CoreClass(name = "Math") +public abstract class MathNodes { + + @CoreMethod(names = "sqrt", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class SqrtNode extends CoreMethodNode { + + public SqrtNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SqrtNode(SqrtNode prev) { + super(prev); + } + + @Specialization + public double sqrt(int a) { + return Math.sqrt(a); + } + + @Specialization + public double sqrt(BigInteger a) { + return Math.sqrt(a.doubleValue()); + } + + @Specialization + public double sqrt(double a) { + return Math.sqrt(a); + } + + } + + @CoreMethod(names = "exp", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class ExpNode extends CoreMethodNode { + + public ExpNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ExpNode(ExpNode prev) { + super(prev); + } + + @Specialization + public double exp(int a) { + return Math.exp(a); + } + + @Specialization + public double exp(BigInteger a) { + return Math.exp(a.doubleValue()); + } + + @Specialization + public double exp(double a) { + return Math.exp(a); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ModuleNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ModuleNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,652 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.control.*; +import com.oracle.truffle.ruby.nodes.methods.arguments.*; +import com.oracle.truffle.ruby.nodes.objects.*; +import com.oracle.truffle.ruby.nodes.objects.instancevariables.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.RubyParser.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +@CoreClass(name = "Module") +public abstract class ModuleNodes { + + @CoreMethod(names = "alias_method", minArgs = 2, maxArgs = 2) + public abstract static class AliasMethodNode extends CoreMethodNode { + + public AliasMethodNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AliasMethodNode(AliasMethodNode prev) { + super(prev); + } + + @Specialization + public RubyModule aliasMethod(RubyModule module, RubySymbol newName, RubySymbol oldName) { + module.alias(newName.toString(), oldName.toString()); + return module; + } + } + + @CoreMethod(names = "append_features", minArgs = 1, maxArgs = 1) + public abstract static class AppendFeaturesNode extends CoreMethodNode { + + public AppendFeaturesNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AppendFeaturesNode(AppendFeaturesNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder appendFeatures(RubyModule module, RubyModule other) { + module.appendFeatures(other); + return NilPlaceholder.INSTANCE; + } + } + + @CoreMethod(names = "attr_reader", isSplatted = true, appendCallNode = true) + public abstract static class AttrReaderNode extends CoreMethodNode { + + public AttrReaderNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AttrReaderNode(AttrReaderNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder attrReader(RubyModule module, Object[] args) { + final Node callSite = (Node) args[args.length - 1]; + final SourceSection sourceSection = callSite.getSourceSection(); + + for (int n = 0; n < args.length - 1; n++) { + attrReader(getContext(), sourceSection, module, args[n].toString()); + } + + return NilPlaceholder.INSTANCE; + } + + public static void attrReader(RubyContext context, SourceSection sourceSection, RubyModule module, String name) { + CompilerDirectives.transferToInterpreter(); + + final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, Arity.NO_ARGS); + + final SelfNode self = new SelfNode(context, sourceSection); + final UninitializedReadInstanceVariableNode readInstanceVariable = new UninitializedReadInstanceVariableNode(context, sourceSection, name, self); + + final SequenceNode block = new SequenceNode(context, sourceSection, checkArity, readInstanceVariable); + + final RubyRootNode pristineRoot = new RubyRootNode(sourceSection, name + "(attr_reader)", block); + final CallTarget callTarget = Truffle.getRuntime().createCallTarget(NodeUtil.cloneNode(pristineRoot)); + final InlinableMethodImplementation methodImplementation = new InlinableMethodImplementation(callTarget, null, new FrameDescriptor(), pristineRoot, true, false); + final RubyMethod method = new RubyMethod(sourceSection, module, new UniqueMethodIdentifier(), null, name, Visibility.PUBLIC, false, methodImplementation); + + module.addMethod(method); + } + } + + @CoreMethod(names = "attr_writer", isSplatted = true, appendCallNode = true) + public abstract static class AttrWriterNode extends CoreMethodNode { + + public AttrWriterNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AttrWriterNode(AttrWriterNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder attrWriter(RubyModule module, Object[] args) { + final Node callSite = (Node) args[args.length - 1]; + final SourceSection sourceSection = callSite.getSourceSection(); + + for (int n = 0; n < args.length - 1; n++) { + attrWriter(getContext(), sourceSection, module, args[n].toString()); + } + + return NilPlaceholder.INSTANCE; + } + + public static void attrWriter(RubyContext context, SourceSection sourceSection, RubyModule module, String name) { + CompilerDirectives.transferToInterpreter(); + + final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, Arity.ONE_ARG); + + final SelfNode self = new SelfNode(context, sourceSection); + final ReadPreArgumentNode readArgument = new ReadPreArgumentNode(context, sourceSection, 0, false); + final UninitializedWriteInstanceVariableNode writeInstanceVariable = new UninitializedWriteInstanceVariableNode(context, sourceSection, name, self, readArgument); + + final SequenceNode block = new SequenceNode(context, sourceSection, checkArity, writeInstanceVariable); + + final RubyRootNode pristineRoot = new RubyRootNode(sourceSection, name + "(attr_writer)", block); + final CallTarget callTarget = Truffle.getRuntime().createCallTarget(NodeUtil.cloneNode(pristineRoot)); + final InlinableMethodImplementation methodImplementation = new InlinableMethodImplementation(callTarget, null, new FrameDescriptor(), pristineRoot, true, false); + final RubyMethod method = new RubyMethod(sourceSection, module, new UniqueMethodIdentifier(), null, name + "=", Visibility.PUBLIC, false, methodImplementation); + + module.addMethod(method); + } + } + + @CoreMethod(names = {"attr_accessor", "attr"}, isSplatted = true, appendCallNode = true) + public abstract static class AttrAccessorNode extends CoreMethodNode { + + public AttrAccessorNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AttrAccessorNode(AttrAccessorNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder attrAccessor(RubyModule module, Object[] args) { + final Node callSite = (Node) args[args.length - 1]; + final SourceSection sourceSection = callSite.getSourceSection(); + + for (int n = 0; n < args.length - 1; n++) { + attrAccessor(getContext(), sourceSection, module, args[n].toString()); + } + + return NilPlaceholder.INSTANCE; + } + + public static void attrAccessor(RubyContext context, SourceSection sourceSection, RubyModule module, String name) { + CompilerDirectives.transferToInterpreter(); + AttrReaderNode.attrReader(context, sourceSection, module, name); + AttrWriterNode.attrWriter(context, sourceSection, module, name); + } + + } + + @CoreMethod(names = "class_eval", minArgs = 1, maxArgs = 3) + public abstract static class ClassEvalNode extends CoreMethodNode { + + public ClassEvalNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ClassEvalNode(ClassEvalNode prev) { + super(prev); + } + + @Specialization + public Object classEval(VirtualFrame frame, RubyModule module, RubyString code, @SuppressWarnings("unused") UndefinedPlaceholder file, @SuppressWarnings("unused") UndefinedPlaceholder line) { + final Source source = getContext().getSourceManager().get("(eval)", code.toString()); + return getContext().execute(getContext(), source, ParserContext.MODULE, module, frame.materialize()); + } + + @Specialization + public Object classEval(VirtualFrame frame, RubyModule module, RubyString code, RubyString file, @SuppressWarnings("unused") UndefinedPlaceholder line) { + final Source source = getContext().getSourceManager().get(file.toString(), code.toString()); + return getContext().execute(getContext(), source, ParserContext.MODULE, module, frame.materialize()); + } + + @Specialization + public Object classEval(VirtualFrame frame, RubyModule module, RubyString code, RubyString file, @SuppressWarnings("unused") int line) { + final Source source = getContext().getSourceManager().get(file.toString(), code.toString()); + return getContext().execute(getContext(), source, ParserContext.MODULE, module, frame.materialize()); + } + + } + + @CoreMethod(names = "class_variable_defined?", maxArgs = 0) + public abstract static class ClassVariableDefinedNode extends CoreMethodNode { + + public ClassVariableDefinedNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ClassVariableDefinedNode(ClassVariableDefinedNode prev) { + super(prev); + } + + @Specialization + public boolean isClassVariableDefined(RubyModule module, RubyString name) { + return module.lookupClassVariable(name.toString()) != null; + } + + @Specialization + public boolean isClassVariableDefined(RubyModule module, RubySymbol name) { + return module.lookupClassVariable(name.toString()) != null; + } + + } + + @CoreMethod(names = "constants", maxArgs = 0) + public abstract static class ConstantsNode extends CoreMethodNode { + + public ConstantsNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ConstantsNode(ConstantsNode prev) { + super(prev); + } + + @Specialization + public RubyArray constants(@SuppressWarnings("unused") RubyModule module) { + getContext().implementationMessage("Module#constants returns an empty array"); + return new RubyArray(getContext().getCoreLibrary().getArrayClass()); + } + } + + @CoreMethod(names = "const_defined?", minArgs = 1, maxArgs = 2) + public abstract static class ConstDefinedNode extends CoreMethodNode { + + public ConstDefinedNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ConstDefinedNode(ConstDefinedNode prev) { + super(prev); + } + + @Specialization(order = 1) + public boolean isConstDefined(RubyModule module, RubyString name, @SuppressWarnings("unused") UndefinedPlaceholder inherit) { + return module.lookupConstant(name.toString()) != null; + } + + @Specialization(order = 2) + public boolean isConstDefined(RubyModule module, RubyString name, boolean inherit) { + if (inherit) { + return module.lookupConstant(name.toString()) != null; + } else { + return module.getConstants().containsKey(name.toString()); + } + } + + @Specialization(order = 3) + public boolean isConstDefined(RubyModule module, RubySymbol name, @SuppressWarnings("unused") UndefinedPlaceholder inherit) { + return module.lookupConstant(name.toString()) != null; + } + + public boolean isConstDefined(RubyModule module, RubySymbol name, boolean inherit) { + if (inherit) { + return module.lookupConstant(name.toString()) != null; + } else { + return module.getConstants().containsKey(name.toString()); + } + } + + } + + @CoreMethod(names = "define_method", needsBlock = true, minArgs = 1, maxArgs = 2) + public abstract static class DefineMethodNode extends CoreMethodNode { + + public DefineMethodNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DefineMethodNode(DefineMethodNode prev) { + super(prev); + } + + @Specialization(order = 1) + public RubyMethod defineMethod(RubyModule module, RubyString name, @SuppressWarnings("unused") UndefinedPlaceholder proc, RubyProc block) { + final RubyMethod method = block.getMethod(); + module.addMethod(method.withNewName(name.toString())); + return method; + } + + @Specialization(order = 2) + public RubyMethod defineMethod(RubyModule module, RubyString name, RubyProc proc, @SuppressWarnings("unused") UndefinedPlaceholder block) { + final RubyMethod method = proc.getMethod(); + module.addMethod(method.withNewName(name.toString())); + return method; + } + + @Specialization(order = 3) + public RubyMethod defineMethod(RubyModule module, RubySymbol name, @SuppressWarnings("unused") UndefinedPlaceholder proc, RubyProc block) { + final RubyMethod method = block.getMethod(); + module.addMethod(method.withNewName(name.toString())); + return method; + } + + @Specialization(order = 4) + public RubyMethod defineMethod(RubyModule module, RubySymbol name, RubyProc proc, @SuppressWarnings("unused") UndefinedPlaceholder block) { + final RubyMethod method = proc.getMethod(); + module.addMethod(method.withNewName(name.toString())); + return method; + } + + } + + @CoreMethod(names = "include", isSplatted = true, minArgs = 1) + public abstract static class IncludeNode extends CoreMethodNode { + + public IncludeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public IncludeNode(IncludeNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder include(RubyModule module, Object[] args) { + // Note that we traverse the arguments backwards + + for (int n = args.length - 1; n >= 0; n--) { + if (args[n] instanceof RubyModule) { + final RubyModule included = (RubyModule) args[n]; + + // Note that we do appear to do full method lookup here + included.getLookupNode().lookupMethod("append_features").call(null, included, null, module); + + // TODO(cs): call included hook + } + } + + return NilPlaceholder.INSTANCE; + } + } + + @CoreMethod(names = "method_defined?", minArgs = 1, maxArgs = 2) + public abstract static class MethodDefinedNode extends CoreMethodNode { + + public MethodDefinedNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MethodDefinedNode(MethodDefinedNode prev) { + super(prev); + } + + @Specialization(order = 1) + public boolean isMethodDefined(RubyModule module, RubyString name, @SuppressWarnings("unused") UndefinedPlaceholder inherit) { + return module.lookupMethod(name.toString()) != null; + } + + @Specialization(order = 2) + public boolean isMethodDefined(RubyModule module, RubyString name, boolean inherit) { + if (inherit) { + return module.lookupMethod(name.toString()) != null; + } else { + return module.getMethods().containsKey(name.toString()); + } + } + + @Specialization(order = 3) + public boolean isMethodDefined(RubyModule module, RubySymbol name, @SuppressWarnings("unused") UndefinedPlaceholder inherit) { + return module.lookupMethod(name.toString()) != null; + } + + public boolean isMethodDefined(RubyModule module, RubySymbol name, boolean inherit) { + if (inherit) { + return module.lookupMethod(name.toString()) != null; + } else { + return module.getMethods().containsKey(name.toString()); + } + } + } + + @CoreMethod(names = "module_eval", minArgs = 1, maxArgs = 3) + public abstract static class ModuleEvalNode extends CoreMethodNode { + + public ModuleEvalNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ModuleEvalNode(ModuleEvalNode prev) { + super(prev); + } + + @Specialization + public RubyModule moduleEval(RubyModule module, RubyString code, @SuppressWarnings("unused") Object file, @SuppressWarnings("unused") Object line) { + module.moduleEval(code.toString()); + return module; + } + } + + @CoreMethod(names = "module_function", isSplatted = true) + public abstract static class ModuleFunctionNode extends CoreMethodNode { + + public ModuleFunctionNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ModuleFunctionNode(ModuleFunctionNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder moduleFunction(VirtualFrame frame, RubyModule module, Object... args) { + if (args.length == 0) { + final Frame unpacked = frame.getCaller().unpack(); + + final FrameSlot slot = unpacked.getFrameDescriptor().findFrameSlot(RubyModule.MODULE_FUNCTION_FLAG_FRAME_SLOT_ID); + + /* + * setObject, even though it's a boolean, so we can getObject and either get the + * default Nil or the boolean value without triggering deoptimization. + */ + + unpacked.setObject(slot, true); + } else { + for (Object argument : args) { + final String methodName = argument.toString(); + module.getSingletonClass().addMethod(module.lookupMethod(methodName)); + } + } + + return NilPlaceholder.INSTANCE; + } + } + + @CoreMethod(names = "public", isSplatted = true) + public abstract static class PublicNode extends CoreMethodNode { + + public PublicNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PublicNode(PublicNode prev) { + super(prev); + } + + @Specialization + public RubyModule doPublic(VirtualFrame frame, RubyModule module, Object... args) { + module.visibilityMethod(frame.getCaller(), args, Visibility.PUBLIC); + return module; + } + } + + @CoreMethod(names = "private", isSplatted = true) + public abstract static class PrivateNode extends CoreMethodNode { + + public PrivateNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PrivateNode(PrivateNode prev) { + super(prev); + } + + @Specialization + public RubyModule doPrivate(VirtualFrame frame, RubyModule module, Object... args) { + module.visibilityMethod(frame.getCaller(), args, Visibility.PRIVATE); + return module; + } + } + + @CoreMethod(names = "private_class_method", isSplatted = true) + public abstract static class PrivateClassMethodNode extends CoreMethodNode { + + public PrivateClassMethodNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PrivateClassMethodNode(PrivateClassMethodNode prev) { + super(prev); + } + + @Specialization + public RubyModule privateClassMethod(RubyModule module, Object... args) { + final RubyClass moduleSingleton = module.getSingletonClass(); + + for (Object arg : args) { + final RubyMethod method = moduleSingleton.lookupMethod(arg.toString()); + + if (method == null) { + throw new RuntimeException("Couldn't find method " + arg.toString()); + } + + moduleSingleton.addMethod(method.withNewVisibility(Visibility.PRIVATE)); + } + + return module; + } + } + + @CoreMethod(names = "private_constant", isSplatted = true) + public abstract static class PrivateConstantNode extends CoreMethodNode { + + public PrivateConstantNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PrivateConstantNode(PrivateConstantNode prev) { + super(prev); + } + + @Specialization + public RubyModule privateConstnat(RubyModule module, @SuppressWarnings("unused") Object... args) { + getContext().implementationMessage("private_constant does nothing at the moment"); + return module; + } + } + + @CoreMethod(names = "protected", isSplatted = true) + public abstract static class ProtectedNode extends CoreMethodNode { + + public ProtectedNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ProtectedNode(ProtectedNode prev) { + super(prev); + } + + @Specialization + public RubyModule doProtected(RubyModule module, @SuppressWarnings("unused") Object... args) { + getContext().implementationMessage("protected does nothing at the moment"); + return module; + } + } + + @CoreMethod(names = "remove_class_variable", minArgs = 1, maxArgs = 1) + public abstract static class RemoveClassVariableNode extends CoreMethodNode { + + public RemoveClassVariableNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public RemoveClassVariableNode(RemoveClassVariableNode prev) { + super(prev); + } + + @Specialization + public RubyModule removeClassVariable(RubyModule module, RubyString name) { + module.removeClassVariable(name.toString()); + return module; + } + + @Specialization + public RubyModule removeClassVariable(RubyModule module, RubySymbol name) { + module.removeClassVariable(name.toString()); + return module; + } + + } + + @CoreMethod(names = "remove_method", minArgs = 1, maxArgs = 1) + public abstract static class RemoveMethodNode extends CoreMethodNode { + + public RemoveMethodNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public RemoveMethodNode(RemoveMethodNode prev) { + super(prev); + } + + @Specialization + public RubyModule removeMethod(RubyModule module, RubyString name) { + module.removeMethod(name.toString()); + return module; + } + + @Specialization + public RubyModule removeMethod(RubyModule module, RubySymbol name) { + module.removeMethod(name.toString()); + return module; + } + + } + + @CoreMethod(names = "to_s", maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toS(RubyModule module) { + return getContext().makeString(module.getName()); + } + } + + @CoreMethod(names = "undef_method", minArgs = 1, maxArgs = 1) + public abstract static class UndefMethodNode extends CoreMethodNode { + + public UndefMethodNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public UndefMethodNode(UndefMethodNode prev) { + super(prev); + } + + @Specialization + public RubyModule undefMethod(RubyModule module, RubyString name) { + final RubyMethod method = module.lookupMethod(name.toString()); + module.undefMethod(method); + return module; + } + + @Specialization + public RubyModule undefMethod(RubyModule module, RubySymbol name) { + final RubyMethod method = module.lookupMethod(name.toString()); + module.undefMethod(method); + return module; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/NilClassNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/NilClassNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "NilClass") +public abstract class NilClassNodes { + + @CoreMethod(names = "!", needsSelf = false, maxArgs = 0) + public abstract static class NotNode extends CoreMethodNode { + + public NotNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NotNode(NotNode prev) { + super(prev); + } + + @Specialization + public boolean not() { + return true; + } + } + + @CoreMethod(names = "==", needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class EqualNode extends CoreMethodNode { + + public EqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EqualNode(EqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(Object b) { + return b instanceof NilPlaceholder || b instanceof RubyNilClass; + } + + } + + @CoreMethod(names = "!=", needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class NotEqualNode extends CoreMethodNode { + + public NotEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NotEqualNode(NotEqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(Object b) { + return !(b instanceof NilPlaceholder || b instanceof RubyNilClass); + } + + } + + @CoreMethod(names = "inspect", needsSelf = false, maxArgs = 0) + public abstract static class InpsectNode extends CoreMethodNode { + + public InpsectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InpsectNode(InpsectNode prev) { + super(prev); + } + + @Specialization + public RubyString inspect() { + return getContext().makeString("nil"); + } + } + + @CoreMethod(names = "nil?", needsSelf = false, maxArgs = 0) + public abstract static class NilNode extends CoreMethodNode { + + public NilNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NilNode(NilNode prev) { + super(prev); + } + + @Specialization + public boolean nil() { + return true; + } + } + + @CoreMethod(names = "to_i", needsSelf = false, maxArgs = 0) + public abstract static class ToINode extends CoreMethodNode { + + public ToINode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToINode(ToINode prev) { + super(prev); + } + + @Specialization + public int toI() { + return 0; + } + } + + @CoreMethod(names = "to_s", needsSelf = false, maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toS() { + return getContext().makeString(""); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ObjectNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ObjectNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,514 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.math.*; +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@CoreClass(name = "Object") +public abstract class ObjectNodes { + + @CoreMethod(names = "class", maxArgs = 0) + public abstract static class ClassNode extends CoreMethodNode { + + public ClassNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ClassNode(ClassNode prev) { + super(prev); + } + + @Specialization + public RubyClass getClass(boolean value) { + if (value) { + return getContext().getCoreLibrary().getTrueClass(); + } else { + return getContext().getCoreLibrary().getFalseClass(); + } + } + + @Specialization + public RubyClass getClass(@SuppressWarnings("unused") int value) { + return getContext().getCoreLibrary().getFixnumClass(); + } + + @Specialization + public RubyClass getClass(@SuppressWarnings("unused") BigInteger value) { + return getContext().getCoreLibrary().getBignumClass(); + } + + @Specialization + public RubyClass getClass(@SuppressWarnings("unused") double value) { + return getContext().getCoreLibrary().getFloatClass(); + } + + @Specialization + public RubyClass getClass(RubyBasicObject self) { + return self.getRubyClass(); + } + + } + + @CoreMethod(names = "dup", maxArgs = 0) + public abstract static class DupNode extends CoreMethodNode { + + public DupNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DupNode(DupNode prev) { + super(prev); + } + + @Specialization + public Object dup(RubyObject self) { + return self.dup(); + } + + } + + @CoreMethod(names = "extend", isSplatted = true, minArgs = 1) + public abstract static class ExtendNode extends CoreMethodNode { + + public ExtendNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ExtendNode(ExtendNode prev) { + super(prev); + } + + @Specialization + public RubyBasicObject extend(RubyBasicObject self, Object[] args) { + for (int n = 0; n < args.length; n++) { + self.extend((RubyModule) args[n]); + } + + return self; + } + + } + + @CoreMethod(names = "freeze", maxArgs = 0) + public abstract static class FreezeNode extends CoreMethodNode { + + public FreezeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public FreezeNode(FreezeNode prev) { + super(prev); + } + + @Specialization + public RubyObject freeze(RubyObject self) { + self.frozen = true; + return self; + } + + } + + @CoreMethod(names = "frozen?", maxArgs = 0) + public abstract static class FrozenNode extends CoreMethodNode { + + public FrozenNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public FrozenNode(FrozenNode prev) { + super(prev); + } + + @Specialization + public boolean isFrozen(RubyObject self) { + return self.frozen; + } + + } + + @CoreMethod(names = "inspect", maxArgs = 0) + public abstract static class InspectNode extends CoreMethodNode { + + public InspectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InspectNode(InspectNode prev) { + super(prev); + } + + @Specialization + public RubyString inspect(boolean value) { + return getContext().makeString(Boolean.toString(value)); + } + + @Specialization + public RubyString inspect(int value) { + return getContext().makeString(Integer.toString(value)); + } + + @Specialization + public RubyString inspect(BigInteger value) { + return getContext().makeString(value.toString()); + } + + @Specialization + public RubyString inspect(double value) { + return getContext().makeString(Double.toString(value)); + } + + @Specialization + public RubyString inspect(RubyObject self) { + return getContext().makeString(self.inspect()); + } + + } + + @CoreMethod(names = "instance_eval", needsBlock = true, maxArgs = 0) + public abstract static class InstanceEvalNode extends CoreMethodNode { + + public InstanceEvalNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InstanceEvalNode(InstanceEvalNode prev) { + super(prev); + } + + @Specialization + public Object instanceEval(VirtualFrame frame, RubyObject self, RubyProc block) { + return block.callWithModifiedSelf(frame.pack(), self); + } + + } + + @CoreMethod(names = "instance_variable_defined?", minArgs = 1, maxArgs = 1) + public abstract static class InstanceVariableDefinedNode extends CoreMethodNode { + + public InstanceVariableDefinedNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InstanceVariableDefinedNode(InstanceVariableDefinedNode prev) { + super(prev); + } + + @Specialization + public boolean isInstanceVariableDefined(RubyBasicObject object, RubyString name) { + return object.isInstanceVariableDefined(RubyObject.checkInstanceVariableName(getContext(), name.toString())); + } + + @Specialization + public boolean isInstanceVariableDefined(RubyBasicObject object, RubySymbol name) { + return object.isInstanceVariableDefined(RubyObject.checkInstanceVariableName(getContext(), name.toString())); + } + + } + + @CoreMethod(names = "instance_variable_get", minArgs = 1, maxArgs = 1) + public abstract static class InstanceVariableGetNode extends CoreMethodNode { + + public InstanceVariableGetNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InstanceVariableGetNode(InstanceVariableGetNode prev) { + super(prev); + } + + @Specialization + public Object isInstanceVariableGet(RubyBasicObject object, RubyString name) { + return object.getInstanceVariable(RubyObject.checkInstanceVariableName(getContext(), name.toString())); + } + + @Specialization + public Object isInstanceVariableGet(RubyBasicObject object, RubySymbol name) { + return object.getInstanceVariable(RubyObject.checkInstanceVariableName(getContext(), name.toString())); + } + + } + + @CoreMethod(names = "instance_variable_set", minArgs = 2, maxArgs = 2) + public abstract static class InstanceVariableSetNode extends CoreMethodNode { + + public InstanceVariableSetNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InstanceVariableSetNode(InstanceVariableSetNode prev) { + super(prev); + } + + @Specialization + public Object isInstanceVariableSet(RubyBasicObject object, RubyString name, Object value) { + object.setInstanceVariable(RubyObject.checkInstanceVariableName(getContext(), name.toString()), value); + return value; + } + + @Specialization + public Object isInstanceVariableSet(RubyBasicObject object, RubySymbol name, Object value) { + object.setInstanceVariable(RubyObject.checkInstanceVariableName(getContext(), name.toString()), value); + return value; + } + + } + + @CoreMethod(names = "instance_variables", maxArgs = 0) + public abstract static class InstanceVariablesNode extends CoreMethodNode { + + public InstanceVariablesNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InstanceVariablesNode(InstanceVariablesNode prev) { + super(prev); + } + + @Specialization + public RubyArray instanceVariables(RubyObject self) { + final String[] instanceVariableNames = self.getInstanceVariableNames(); + + Arrays.sort(instanceVariableNames); + + final RubyArray array = new RubyArray(getContext().getCoreLibrary().getArrayClass()); + + for (String name : instanceVariableNames) { + array.push(new RubyString(getContext().getCoreLibrary().getStringClass(), name)); + } + + return array; + } + + } + + @CoreMethod(names = {"is_a?", "instance_of?", "kind_of?"}, minArgs = 1, maxArgs = 1) + public abstract static class IsANode extends CoreMethodNode { + + public IsANode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public IsANode(IsANode prev) { + super(prev); + } + + @Specialization + public boolean isA(@SuppressWarnings("unused") RubyObject self, @SuppressWarnings("unused") NilPlaceholder nil) { + return false; + } + + @Specialization + public boolean isA(RubyObject self, RubyClass rubyClass) { + return self.getRubyClass().assignableTo(rubyClass); + } + + } + + @CoreMethod(names = "methods", minArgs = 0, maxArgs = 1) + public abstract static class MethodsNode extends CoreMethodNode { + + public MethodsNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MethodsNode(MethodsNode prev) { + super(prev); + } + + @Specialization + public RubyArray methods(RubyObject self, boolean includeInherited) { + if (!includeInherited) { + self.getRubyClass().getContext().implementationMessage("Object#methods always returns inherited methods at the moment"); + } + + return methods(self, UndefinedPlaceholder.INSTANCE); + } + + @Specialization + public RubyArray methods(RubyObject self, @SuppressWarnings("unused") UndefinedPlaceholder includeInherited) { + final RubyArray array = new RubyArray(self.getRubyClass().getContext().getCoreLibrary().getArrayClass()); + + final Map methods = new HashMap<>(); + + self.getLookupNode().getMethods(methods); + + for (RubyMethod method : methods.values()) { + if (method.getVisibility() == Visibility.PUBLIC || method.getVisibility() == Visibility.PROTECTED) { + array.push(new RubySymbol(self.getRubyClass().getContext().getCoreLibrary().getSymbolClass(), method.getName())); + } + } + + return array; + } + + } + + @CoreMethod(names = "nil?", needsSelf = false, maxArgs = 0) + public abstract static class NilNode extends CoreMethodNode { + + public NilNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NilNode(NilNode prev) { + super(prev); + } + + @Specialization + public boolean nil() { + return false; + } + } + + @CoreMethod(names = "object_id", needsSelf = true, maxArgs = 0) + public abstract static class ObjectIDNode extends CoreMethodNode { + + public ObjectIDNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ObjectIDNode(ObjectIDNode prev) { + super(prev); + } + + @Specialization + public Object objectID(RubyBasicObject object) { + return GeneralConversions.fixnumOrBignum(object.getObjectID()); + } + + } + + @CoreMethod(names = "respond_to?", minArgs = 1, maxArgs = 2) + public abstract static class RespondToNode extends CoreMethodNode { + + public RespondToNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public RespondToNode(RespondToNode prev) { + super(prev); + } + + @Specialization(order = 1) + public boolean doesRespondTo(Object object, RubyString name, @SuppressWarnings("unused") UndefinedPlaceholder checkVisibility) { + return doesRespondTo(getContext().getCoreLibrary().box(object), name.toString(), false); + } + + @Specialization(order = 2) + public boolean doesRespondTo(Object object, RubyString name, boolean dontCheckVisibility) { + return doesRespondTo(getContext().getCoreLibrary().box(object), name.toString(), dontCheckVisibility); + } + + @Specialization(order = 3) + public boolean doesRespondTo(Object object, RubySymbol name, @SuppressWarnings("unused") UndefinedPlaceholder checkVisibility) { + return doesRespondTo(getContext().getCoreLibrary().box(object), name.toString(), false); + } + + @Specialization(order = 4) + public boolean doesRespondTo(Object object, RubySymbol name, boolean dontCheckVisibility) { + return doesRespondTo(getContext().getCoreLibrary().box(object), name.toString(), dontCheckVisibility); + } + + private static boolean doesRespondTo(RubyBasicObject object, String name, boolean dontCheckVisibility) { + final RubyMethod method = object.getLookupNode().lookupMethod(name); + + if (method == null || method.isUndefined()) { + return false; + } + + if (dontCheckVisibility) { + return true; + } else { + return method.getVisibility() == Visibility.PUBLIC; + } + } + + } + + @CoreMethod(names = "singleton_class", maxArgs = 0) + public abstract static class SingletonClassNode extends CoreMethodNode { + + public SingletonClassNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SingletonClassNode(SingletonClassNode prev) { + super(prev); + } + + @Specialization + public RubyClass singletonClass(RubyBasicObject self) { + return self.getSingletonClass(); + } + + } + + @CoreMethod(names = "singleton_methods", minArgs = 0, maxArgs = 1) + public abstract static class SingletonMethodsNode extends CoreMethodNode { + + public SingletonMethodsNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SingletonMethodsNode(SingletonMethodsNode prev) { + super(prev); + } + + @Specialization + public RubyArray singletonMethods(RubyObject self, boolean includeInherited) { + if (!includeInherited) { + self.getRubyClass().getContext().implementationMessage("Object#singleton_methods always returns inherited methods at the moment"); + } + + return singletonMethods(self, UndefinedPlaceholder.INSTANCE); + } + + @Specialization + public RubyArray singletonMethods(RubyObject self, @SuppressWarnings("unused") UndefinedPlaceholder includeInherited) { + final RubyArray array = new RubyArray(self.getRubyClass().getContext().getCoreLibrary().getArrayClass()); + + for (RubyMethod method : self.getSingletonClass().getDeclaredMethods()) { + array.push(new RubySymbol(self.getRubyClass().getContext().getCoreLibrary().getSymbolClass(), method.getName())); + } + + return array; + } + + } + + @CoreMethod(names = "to_s", maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toS(RubyObject self) { + return getContext().makeString(self.toString()); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ObjectSpaceNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ObjectSpaceNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.math.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@CoreClass(name = "ObjectSpace") +public abstract class ObjectSpaceNodes { + + @CoreMethod(names = "_id2ref", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class ID2RefNode extends CoreMethodNode { + + public ID2RefNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ID2RefNode(ID2RefNode prev) { + super(prev); + } + + @Specialization + public Object id2Ref(int id) { + final Object object = getContext().getObjectSpaceManager().lookupId(id); + + if (object == null) { + return NilPlaceholder.INSTANCE; + } else { + return object; + } + } + + @Specialization + public Object id2Ref(BigInteger id) { + final Object object = getContext().getObjectSpaceManager().lookupId(id.longValue()); + + if (object == null) { + return NilPlaceholder.INSTANCE; + } else { + return object; + } + } + + } + + @CoreMethod(names = "each_object", isModuleMethod = true, needsSelf = false, needsBlock = true, minArgs = 0, maxArgs = 1) + public abstract static class EachObjectNode extends YieldingCoreMethodNode { + + public EachObjectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EachObjectNode(EachObjectNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder eachObject(VirtualFrame frame, @SuppressWarnings("unused") UndefinedPlaceholder ofClass, RubyProc block) { + for (RubyBasicObject object : getContext().getObjectSpaceManager().getObjects()) { + yield(frame, block, object); + } + return NilPlaceholder.INSTANCE; + } + + @Specialization + public NilPlaceholder eachObject(VirtualFrame frame, RubyClass ofClass, RubyProc block) { + for (RubyBasicObject object : getContext().getObjectSpaceManager().getObjects()) { + if (object.getRubyClass().assignableTo(ofClass)) { + yield(frame, block, object); + } + } + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "define_finalizer", isModuleMethod = true, needsSelf = false, minArgs = 2, maxArgs = 2) + public abstract static class DefineFinalizerNode extends CoreMethodNode { + + public DefineFinalizerNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DefineFinalizerNode(DefineFinalizerNode prev) { + super(prev); + } + + @Specialization + public RubyProc defineFinalizer(Object object, RubyProc finalizer) { + getContext().getObjectSpaceManager().defineFinalizer((RubyBasicObject) object, finalizer); + return finalizer; + } + } + + @CoreMethod(names = {"garbage_collect", "start"}, isModuleMethod = true, needsSelf = false, maxArgs = 0) + public abstract static class GarbageCollectNode extends CoreMethodNode { + + public GarbageCollectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GarbageCollectNode(GarbageCollectNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder garbageCollect() { + final RubyThread runningThread = getContext().getThreadManager().leaveGlobalLock(); + + try { + System.gc(); + } finally { + getContext().getThreadManager().enterGlobalLock(runningThread); + } + + return NilPlaceholder.INSTANCE; + } + } + + @CoreMethod(names = "undefine_finalizer", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class UndefineFinalizerNode extends CoreMethodNode { + + public UndefineFinalizerNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public UndefineFinalizerNode(UndefineFinalizerNode prev) { + super(prev); + } + + @Specialization + public Object undefineFinalizer(Object object) { + getContext().getObjectSpaceManager().undefineFinalizer((RubyBasicObject) object); + return object; + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ProcNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ProcNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "Proc") +public abstract class ProcNodes { + + @CoreMethod(names = {"call", "[]"}, isSplatted = true) + public abstract static class CallNode extends CoreMethodNode { + + public CallNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public CallNode(CallNode prev) { + super(prev); + } + + @Specialization + public Object call(VirtualFrame frame, RubyProc proc, Object[] args) { + return proc.call(frame.getCaller(), args); + } + + } + + @CoreMethod(names = "initialize", needsBlock = true, maxArgs = 0) + public abstract static class InitializeNode extends CoreMethodNode { + + public InitializeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InitializeNode(InitializeNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder initialize(VirtualFrame frame, RubyProc proc, RubyProc block) { + final RubyArguments callerArguments = frame.getCaller().unpack().getArguments(RubyArguments.class); + proc.initialize(RubyProc.Type.PROC, callerArguments.getSelf(), callerArguments.getBlock(), block.getMethod()); + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "lambda?", maxArgs = 0) + public abstract static class LambdaNode extends CoreMethodNode { + + public LambdaNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LambdaNode(LambdaNode prev) { + super(prev); + } + + @Specialization + public boolean lambda(RubyProc proc) { + return proc.getType() == RubyProc.Type.LAMBDA; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ProcessNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ProcessNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; + +@CoreClass(name = "Process") +public abstract class ProcessNodes { + + @CoreMethod(names = "pid", isModuleMethod = true, needsSelf = false, maxArgs = 0) + public abstract static class PidNode extends CoreMethodNode { + + public PidNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public PidNode(PidNode prev) { + super(prev); + } + + @Specialization + public int pid() { + return getContext().getPOSIX().getpid(); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/RangeNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/RangeNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.core.range.*; + +@CoreClass(name = "Range") +public abstract class RangeNodes { + + @CoreMethod(names = {"collect", "map"}, needsBlock = true, maxArgs = 0) + public abstract static class CollectNode extends YieldingCoreMethodNode { + + public CollectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public CollectNode(CollectNode prev) { + super(prev); + } + + @Specialization + public RubyArray collect(VirtualFrame frame, FixnumRange range, RubyProc block) { + final RubyContext context = getContext(); + + final RubyArray array = new RubyArray(context.getCoreLibrary().getArrayClass()); + + for (int n = range.getBegin(); n < range.getExclusiveEnd(); n++) { + array.push(yield(frame, block, n)); + } + + return array; + } + + } + + @CoreMethod(names = "each", needsBlock = true, maxArgs = 0) + public abstract static class EachNode extends YieldingCoreMethodNode { + + public EachNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EachNode(EachNode prev) { + super(prev); + } + + @Specialization + public FixnumRange each(VirtualFrame frame, FixnumRange range, RubyProc block) { + for (int n = range.getBegin(); n < range.getExclusiveEnd(); n++) { + yield(frame, block, n); + } + + return range; + } + + } + + @CoreMethod(names = "exclude_end?", maxArgs = 0) + public abstract static class ExcludeEndNode extends CoreMethodNode { + + public ExcludeEndNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ExcludeEndNode(ExcludeEndNode prev) { + super(prev); + } + + @Specialization + public boolean excludeEnd(RubyRange range) { + return range.doesExcludeEnd(); + } + + } + + @CoreMethod(names = "first", maxArgs = 0) + public abstract static class FirstNode extends CoreMethodNode { + + public FirstNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public FirstNode(FirstNode prev) { + super(prev); + } + + @Specialization + public int each(FixnumRange range) { + return range.getBegin(); + } + + @Specialization + public Object each(ObjectRange range) { + return range.getBegin(); + } + + } + + @CoreMethod(names = "include?", maxArgs = 1) + public abstract static class IncludeNode extends CoreMethodNode { + + @Child protected DispatchHeadNode callLess; + @Child protected DispatchHeadNode callGreater; + @Child protected DispatchHeadNode callGreaterEqual; + + public IncludeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + callLess = adoptChild(new DispatchHeadNode(context, getSourceSection(), "<", false)); + callGreater = adoptChild(new DispatchHeadNode(context, getSourceSection(), ">", false)); + callGreaterEqual = adoptChild(new DispatchHeadNode(context, getSourceSection(), ">=", false)); + } + + public IncludeNode(IncludeNode prev) { + super(prev); + callLess = adoptChild(prev.callLess); + callGreater = adoptChild(prev.callGreater); + callGreaterEqual = adoptChild(prev.callGreaterEqual); + } + + @Specialization + public boolean include(FixnumRange range, int value) { + return value >= range.getBegin() && value < range.getExclusiveEnd(); + } + + @Specialization + public boolean include(VirtualFrame frame, ObjectRange range, Object value) { + if ((boolean) callLess.dispatch(frame, value, null, range.getBegin())) { + return false; + } + + if (range.doesExcludeEnd()) { + if ((boolean) callGreaterEqual.dispatch(frame, value, null, range.getEnd())) { + return false; + } + } else { + if ((boolean) callGreater.dispatch(frame, value, null, range.getEnd())) { + return false; + } + } + + return true; + } + } + + @CoreMethod(names = "last", maxArgs = 0) + public abstract static class LastNode extends CoreMethodNode { + + public LastNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LastNode(LastNode prev) { + super(prev); + } + + @Specialization + public int last(FixnumRange range) { + return range.getEnd(); + } + + @Specialization + public Object last(ObjectRange range) { + return range.getEnd(); + } + + } + + @CoreMethod(names = "step", needsBlock = true, minArgs = 1, maxArgs = 1) + public abstract static class StepNode extends YieldingCoreMethodNode { + + public StepNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public StepNode(StepNode prev) { + super(prev); + } + + @Specialization + public FixnumRange step(VirtualFrame frame, FixnumRange range, int step, RubyProc block) { + for (int n = range.getBegin(); n < range.getExclusiveEnd(); n += step) { + yield(frame, block, n); + } + + return range; + } + + } + + @CoreMethod(names = "to_a", maxArgs = 0) + public abstract static class ToANode extends CoreMethodNode { + + public ToANode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToANode(ToANode prev) { + super(prev); + } + + @Specialization + public RubyArray toA(RubyRange range) { + return range.toArray(); + } + + } + + @CoreMethod(names = "to_s", maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toS(RubyRange range) { + return getContext().makeString(range.toString()); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/RegexpNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/RegexpNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.util.regex.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "Regexp") +public abstract class RegexpNodes { + + @CoreMethod(names = {"=~", "==="}, minArgs = 1, maxArgs = 1) + public abstract static class MatchOperatorNode extends CoreMethodNode { + + public MatchOperatorNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MatchOperatorNode(MatchOperatorNode prev) { + super(prev); + } + + @Specialization + public Object match(VirtualFrame frame, RubyRegexp regexp, RubyString string) { + return regexp.matchOperator(frame.getCaller().unpack(), string.toString()); + } + + } + + @CoreMethod(names = "!~", minArgs = 1, maxArgs = 1) + public abstract static class NotMatchOperatorNode extends CoreMethodNode { + + public NotMatchOperatorNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NotMatchOperatorNode(NotMatchOperatorNode prev) { + super(prev); + } + + @Specialization + public Object match(VirtualFrame frame, RubyRegexp regexp, RubyString string) { + return regexp.matchOperator(frame.getCaller().unpack(), string.toString()) == NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "escape", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class EscapeNode extends CoreMethodNode { + + public EscapeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EscapeNode(EscapeNode prev) { + super(prev); + } + + @Specialization + public RubyString sqrt(RubyString pattern) { + return getContext().makeString(Pattern.quote(pattern.toString())); + } + + } + + @CoreMethod(names = "initialize", minArgs = 1, maxArgs = 1) + public abstract static class InitializeNode extends CoreMethodNode { + + public InitializeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InitializeNode(InitializeNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder initialize(RubyRegexp regexp, RubyString string) { + regexp.initialize(string.toString()); + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "match", minArgs = 1, maxArgs = 1) + public abstract static class MatchNode extends CoreMethodNode { + + public MatchNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MatchNode(MatchNode prev) { + super(prev); + } + + @Specialization + public Object match(RubyRegexp regexp, RubyString string) { + return regexp.match(string.toString()); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/SignalNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/SignalNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; + +@CoreClass(name = "Signal") +public abstract class SignalNodes { + + @CoreMethod(names = "trap", isModuleMethod = true, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class SignalNode extends CoreMethodNode { + + public SignalNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SignalNode(SignalNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder trap(@SuppressWarnings("unused") Object signal) { + getContext().implementationMessage("Signal#trap doesn't do anything"); + return NilPlaceholder.INSTANCE; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/StringNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/StringNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.util.*; +import java.util.regex.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@CoreClass(name = "String") +public abstract class StringNodes { + + @CoreMethod(names = "+", minArgs = 1, maxArgs = 1) + public abstract static class AddNode extends CoreMethodNode { + + public AddNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public AddNode(AddNode prev) { + super(prev); + } + + @Specialization + public RubyString add(RubyString a, RubyString b) { + return new RubyString(a.getRubyClass().getContext().getCoreLibrary().getStringClass(), a.toString() + b.toString()); + } + } + + @CoreMethod(names = {"==", "==="}, minArgs = 1, maxArgs = 1) + public abstract static class EqualNode extends CoreMethodNode { + + public EqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EqualNode(EqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(@SuppressWarnings("unused") RubyString a, @SuppressWarnings("unused") NilPlaceholder b) { + return false; + } + + @Specialization + public boolean equal(RubyString a, RubyString b) { + return a.toString().equals(b.toString()); + } + } + + @CoreMethod(names = "!=", minArgs = 1, maxArgs = 1) + public abstract static class NotEqualNode extends CoreMethodNode { + + public NotEqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NotEqualNode(NotEqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(@SuppressWarnings("unused") RubyString a, @SuppressWarnings("unused") NilPlaceholder b) { + return true; + } + + @Specialization + public boolean notEqual(RubyString a, RubyString b) { + return !a.toString().equals(b.toString()); + } + + } + + @CoreMethod(names = "<=>", minArgs = 1, maxArgs = 1) + public abstract static class CompareNode extends CoreMethodNode { + + public CompareNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public CompareNode(CompareNode prev) { + super(prev); + } + + @Specialization + public int compare(RubyString a, RubyString b) { + return a.toString().compareTo(b.toString()); + } + } + + @CoreMethod(names = "<<", minArgs = 1, maxArgs = 1) + public abstract static class ConcatNode extends CoreMethodNode { + + public ConcatNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ConcatNode(ConcatNode prev) { + super(prev); + } + + @Specialization + public RubyString concat(RubyString string, RubyString other) { + string.replace(string.toString() + other.toString()); + return string; + } + } + + @CoreMethod(names = "%", minArgs = 1, maxArgs = 1, isSplatted = true) + public abstract static class FormatNode extends CoreMethodNode { + + public FormatNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public FormatNode(FormatNode prev) { + super(prev); + } + + @Specialization + public RubyString format(RubyString format, Object[] args) { + final RubyContext context = getContext(); + + if (args.length == 1 && args[0] instanceof RubyArray) { + return context.makeString(StringFormatter.format(format.toString(), ((RubyArray) args[0]).asList())); + } else { + return context.makeString(StringFormatter.format(format.toString(), Arrays.asList(args))); + } + } + } + + @CoreMethod(names = "[]", minArgs = 1, maxArgs = 2, isSplatted = true) + public abstract static class GetIndexNode extends CoreMethodNode { + + public GetIndexNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GetIndexNode(GetIndexNode prev) { + super(prev); + } + + @Specialization + public Object getIndex(RubyString string, Object[] args) { + return RubyString.getIndex(getContext(), string.toString(), args); + } + } + + @CoreMethod(names = "=~", minArgs = 1, maxArgs = 1) + public abstract static class MatchOperatorNode extends CoreMethodNode { + + public MatchOperatorNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MatchOperatorNode(MatchOperatorNode prev) { + super(prev); + } + + @Specialization + public Object match(VirtualFrame frame, RubyString string, RubyRegexp regexp) { + return regexp.matchOperator(frame, string.toString()); + } + } + + @CoreMethod(names = "chomp", maxArgs = 0) + public abstract static class ChompNode extends CoreMethodNode { + + public ChompNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ChompNode(ChompNode prev) { + super(prev); + } + + @Specialization + public RubyString chomp(RubyString string) { + return string.getRubyClass().getContext().makeString(string.toString().trim()); + } + } + + @CoreMethod(names = "chomp!", maxArgs = 0) + public abstract static class ChompBangNode extends CoreMethodNode { + + public ChompBangNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ChompBangNode(ChompBangNode prev) { + super(prev); + } + + @Specialization + public RubyString chompBang(RubyString string) { + string.replace(string.toString().trim()); + return string; + } + } + + @CoreMethod(names = "downcase", maxArgs = 0) + public abstract static class DowncaseNode extends CoreMethodNode { + + public DowncaseNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DowncaseNode(DowncaseNode prev) { + super(prev); + } + + @Specialization + public RubyString downcase(RubyString string) { + return string.getRubyClass().getContext().makeString(string.toString().toLowerCase()); + } + } + + @CoreMethod(names = "downcase!", maxArgs = 0) + public abstract static class DowncaseBangNode extends CoreMethodNode { + + public DowncaseBangNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public DowncaseBangNode(DowncaseBangNode prev) { + super(prev); + } + + @Specialization + public RubyString downcase(RubyString string) { + string.replace(string.toString().toLowerCase()); + return string; + } + } + + @CoreMethod(names = "empty?", maxArgs = 0) + public abstract static class EmptyNode extends CoreMethodNode { + + public EmptyNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EmptyNode(EmptyNode prev) { + super(prev); + } + + @Specialization + public boolean empty(RubyString string) { + return string.toString().isEmpty(); + } + } + + @CoreMethod(names = "end_with?", minArgs = 1, maxArgs = 1) + public abstract static class EndWithNode extends CoreMethodNode { + + public EndWithNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EndWithNode(EndWithNode prev) { + super(prev); + } + + @Specialization + public boolean endWith(RubyString string, RubyString b) { + return string.toString().endsWith(b.toString()); + } + } + + @CoreMethod(names = "gsub", minArgs = 2, maxArgs = 2) + public abstract static class GsubNode extends CoreMethodNode { + + public GsubNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public GsubNode(GsubNode prev) { + super(prev); + } + + @Specialization + public RubyString gsub(RubyString string, RubyString regexpString, RubyString replacement) { + final RubyRegexp regexp = new RubyRegexp(getContext().getCoreLibrary().getRegexpClass(), regexpString.toString()); + return gsub(string, regexp, replacement); + } + + @Specialization + public RubyString gsub(RubyString string, RubyRegexp regexp, RubyString replacement) { + return getContext().makeString(regexp.getPattern().matcher(string.toString()).replaceAll(replacement.toString())); + } + } + + @CoreMethod(names = "inspect", maxArgs = 0) + public abstract static class InpsectNode extends CoreMethodNode { + + public InpsectNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InpsectNode(InpsectNode prev) { + super(prev); + } + + @Specialization + public RubyString inspect(RubyString string) { + return getContext().makeString("\"" + string.toString().replace("\\", "\\\\").replace("\"", "\\\"") + "\""); + } + } + + @CoreMethod(names = "ljust", minArgs = 1, maxArgs = 2) + public abstract static class LjustNode extends CoreMethodNode { + + public LjustNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public LjustNode(LjustNode prev) { + super(prev); + } + + @Specialization + public RubyString ljust(RubyString string, int length, @SuppressWarnings("unused") UndefinedPlaceholder padding) { + return getContext().makeString(RubyString.ljust(string.toString(), length, " ")); + } + + @Specialization + public RubyString ljust(RubyString string, int length, RubyString padding) { + return getContext().makeString(RubyString.ljust(string.toString(), length, padding.toString())); + } + + } + + @CoreMethod(names = "size", maxArgs = 0) + public abstract static class SizeNode extends CoreMethodNode { + + public SizeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SizeNode(SizeNode prev) { + super(prev); + } + + @Specialization + public int size(RubyString string) { + return string.toString().length(); + } + } + + @CoreMethod(names = "match", minArgs = 1, maxArgs = 1) + public abstract static class MatchNode extends CoreMethodNode { + + public MatchNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public MatchNode(MatchNode prev) { + super(prev); + } + + @Specialization + public Object match(RubyString string, RubyString regexpString) { + final RubyRegexp regexp = new RubyRegexp(getContext().getCoreLibrary().getRegexpClass(), regexpString.toString()); + return regexp.match(string.toString()); + } + + @Specialization + public Object match(RubyString string, RubyRegexp regexp) { + return regexp.match(string.toString()); + } + } + + @CoreMethod(names = "rjust", minArgs = 1, maxArgs = 2) + public abstract static class RjustNode extends CoreMethodNode { + + public RjustNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public RjustNode(RjustNode prev) { + super(prev); + } + + @Specialization + public RubyString rjust(RubyString string, int length, @SuppressWarnings("unused") UndefinedPlaceholder padding) { + return getContext().makeString(RubyString.rjust(string.toString(), length, " ")); + } + + @Specialization + public RubyString rjust(RubyString string, int length, RubyString padding) { + return getContext().makeString(RubyString.rjust(string.toString(), length, padding.toString())); + } + + } + + @CoreMethod(names = "scan", minArgs = 1, maxArgs = 1) + public abstract static class ScanNode extends CoreMethodNode { + + public ScanNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ScanNode(ScanNode prev) { + super(prev); + } + + @Specialization + public RubyArray scan(RubyString string, RubyString regexp) { + return RubyString.scan(getContext(), string.toString(), Pattern.compile(regexp.toString())); + } + + @Specialization + public RubyArray scan(RubyString string, RubyRegexp regexp) { + return RubyString.scan(getContext(), string.toString(), regexp.getPattern()); + } + + } + + @CoreMethod(names = "split", minArgs = 1, maxArgs = 1) + public abstract static class SplitNode extends CoreMethodNode { + + public SplitNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SplitNode(SplitNode prev) { + super(prev); + } + + @Specialization + public RubyArray split(RubyString string, RubyString sep) { + final RubyContext context = getContext(); + + final String[] components = string.toString().split(Pattern.quote(sep.toString())); + + final Object[] objects = new Object[components.length]; + + for (int n = 0; n < objects.length; n++) { + objects[n] = context.makeString(components[n]); + } + + return RubyArray.specializedFromObjects(context.getCoreLibrary().getArrayClass(), objects); + } + + @Specialization + public RubyArray split(RubyString string, RubyRegexp sep) { + final RubyContext context = getContext(); + + final String[] components = string.toString().split(sep.getPattern().pattern()); + + final Object[] objects = new Object[components.length]; + + for (int n = 0; n < objects.length; n++) { + objects[n] = context.makeString(components[n]); + } + + return RubyArray.specializedFromObjects(context.getCoreLibrary().getArrayClass(), objects); + } + } + + @CoreMethod(names = "start_with?", minArgs = 1, maxArgs = 1) + public abstract static class StartWithNode extends CoreMethodNode { + + public StartWithNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public StartWithNode(StartWithNode prev) { + super(prev); + } + + @Specialization + public boolean endWith(RubyString string, RubyString b) { + return string.toString().startsWith(b.toString()); + } + } + + @CoreMethod(names = "to_f", maxArgs = 0) + public abstract static class ToFNode extends CoreMethodNode { + + public ToFNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToFNode(ToFNode prev) { + super(prev); + } + + @Specialization + public double toF(RubyString string) { + return Double.parseDouble(string.toString()); + } + } + + @CoreMethod(names = "to_i", maxArgs = 0) + public abstract static class ToINode extends CoreMethodNode { + + public ToINode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToINode(ToINode prev) { + super(prev); + } + + @Specialization + public Object toI(RubyString string) { + return string.toInteger(); + } + } + + @CoreMethod(names = "to_s", maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toF(RubyString string) { + return string; + } + } + + @CoreMethod(names = {"to_sym", "intern"}, maxArgs = 0) + public abstract static class ToSymNode extends CoreMethodNode { + + public ToSymNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSymNode(ToSymNode prev) { + super(prev); + } + + @Specialization + public RubySymbol toSym(RubyString string) { + return new RubySymbol(getContext().getCoreLibrary().getSymbolClass(), string.toString()); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/StructNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/StructNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "Struct") +public abstract class StructNodes { + + @CoreMethod(names = "initialize", needsBlock = true, appendCallNode = true, isSplatted = true) + public abstract static class InitalizeNode extends CoreMethodNode { + + public InitalizeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InitalizeNode(InitalizeNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder initialize(VirtualFrame frame, RubyClass struct, Object[] args, Object block, Node callSite) { + CompilerDirectives.transferToInterpreter(); + + final RubySymbol[] symbols = new RubySymbol[args.length]; + + for (int n = 0; n < args.length; n++) { + symbols[n] = (RubySymbol) args[n]; + } + + for (RubySymbol symbol : symbols) { + ModuleNodes.AttrAccessorNode.attrAccessor(getContext(), callSite.getSourceSection(), struct, symbol.toString()); + } + + if (!RubyNilClass.isNil(block)) { + ((RubyProc) block).callWithModifiedSelf(frame.pack(), struct); + } + + return NilPlaceholder.INSTANCE; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/SymbolNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/SymbolNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "Symbol") +public abstract class SymbolNodes { + + @CoreMethod(names = {"==", "==="}, minArgs = 1, maxArgs = 1) + public abstract static class EqualNode extends CoreMethodNode { + + public EqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EqualNode(EqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(@SuppressWarnings("unused") RubyString a, @SuppressWarnings("unused") NilPlaceholder b) { + return false; + } + + @Specialization + public boolean equal(RubySymbol a, RubySymbol b) { + return a.toString().equals(b.toString()); + } + + @Specialization + public boolean equal(RubySymbol a, RubyString b) { + return a.toString().equals(b.toString()); + } + + @Specialization + public boolean equal(RubySymbol a, int b) { + return a.toString().equals(Integer.toString(b)); + } + + } + + @CoreMethod(names = "empty?", maxArgs = 0) + public abstract static class EmptyNode extends CoreMethodNode { + + public EmptyNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EmptyNode(EmptyNode prev) { + super(prev); + } + + @Specialization + public boolean empty(RubySymbol symbol) { + return symbol.toString().isEmpty(); + } + + } + + @CoreMethod(names = "to_proc", maxArgs = 0) + public abstract static class ToProcNode extends CoreMethodNode { + + public ToProcNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToProcNode(ToProcNode prev) { + super(prev); + } + + @Specialization + public RubyProc toProc(RubySymbol symbol) { + // TODO(CS): this should be doing all kinds of caching + return symbol.toProc(); + } + } + + @CoreMethod(names = "to_sym", maxArgs = 0) + public abstract static class ToSymNode extends CoreMethodNode { + + public ToSymNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSymNode(ToSymNode prev) { + super(prev); + } + + @Specialization + public RubySymbol toSym(RubySymbol symbol) { + return symbol; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/SystemNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/SystemNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import java.io.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Represents an expression that is evaluated by running it as a system command via forking and + * execing, and then taking stdout as a string. + */ +@NodeInfo(shortName = "system") +public class SystemNode extends RubyNode { + + @Child protected RubyNode child; + + public SystemNode(RubyContext context, SourceSection sourceSection, RubyNode child) { + super(context, sourceSection); + this.child = adoptChild(child); + } + + @Override + public Object execute(VirtualFrame frame) { + final RubyContext context = getContext(); + + final String command = child.execute(frame).toString(); + + Process process; + + try { + // We need to run via bash to get the variable and other expansion we expect + process = Runtime.getRuntime().exec(new String[]{"bash", "-c", command}); + } catch (IOException e) { + throw new RuntimeException(e); + } + + final InputStream stdout = process.getInputStream(); + final BufferedReader reader = new BufferedReader(new InputStreamReader(stdout)); + + final StringBuilder resultBuilder = new StringBuilder(); + + String line; + + // TODO(cs): this isn't great for binary output + + try { + while ((line = reader.readLine()) != null) { + resultBuilder.append(line); + resultBuilder.append("\n"); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + return context.makeString(resultBuilder.toString()); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ThreadNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/ThreadNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "Thread") +public abstract class ThreadNodes { + + @CoreMethod(names = "initialize", needsBlock = true, maxArgs = 0) + public abstract static class InitializeNode extends CoreMethodNode { + + public InitializeNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public InitializeNode(InitializeNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder initialize(RubyThread thread, RubyProc block) { + thread.initialize(block); + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "join", maxArgs = 0) + public abstract static class JoinNode extends CoreMethodNode { + + public JoinNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public JoinNode(JoinNode prev) { + super(prev); + } + + @Specialization + public RubyThread join(RubyThread self) { + self.join(); + return self; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/TimeNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/TimeNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "Time") +public abstract class TimeNodes { + + @CoreMethod(names = "-", minArgs = 1, maxArgs = 1) + public abstract static class SubNode extends CoreMethodNode { + + public SubNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public SubNode(SubNode prev) { + super(prev); + } + + @Specialization + public double sub(RubyTime a, RubyTime b) { + return a.subtract(b); + } + + } + + @CoreMethod(names = "now", isModuleMethod = true, needsSelf = false, maxArgs = 0) + public abstract static class NowNode extends CoreMethodNode { + + public NowNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NowNode(NowNode prev) { + super(prev); + } + + @Specialization + public RubyTime now() { + return RubyTime.fromDate(getContext().getCoreLibrary().getTimeClass(), System.currentTimeMillis()); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/TrueClassNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/TrueClassNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@CoreClass(name = "TrueClass") +public abstract class TrueClassNodes { + + @CoreMethod(names = "!", needsSelf = false, maxArgs = 0) + public abstract static class NotNode extends CoreMethodNode { + + public NotNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public NotNode(NotNode prev) { + super(prev); + } + + @Specialization + public boolean not() { + return false; + } + + } + + @CoreMethod(names = {"==", "===", "=~"}, needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class EqualNode extends CoreMethodNode { + + public EqualNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EqualNode(EqualNode prev) { + super(prev); + } + + @Specialization + public boolean equal(boolean other) { + return other; + } + + @Specialization + public boolean equal(Object other) { + return other instanceof Boolean && ((boolean) other); + } + + } + + @CoreMethod(names = "^", needsSelf = false, minArgs = 1, maxArgs = 1) + public abstract static class XorNode extends CoreMethodNode { + + public XorNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public XorNode(XorNode prev) { + super(prev); + } + + @Specialization + public boolean xor(boolean other) { + return true ^ other; + } + + } + + @CoreMethod(names = "to_s", needsSelf = false, maxArgs = 0) + public abstract static class ToSNode extends CoreMethodNode { + + public ToSNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ToSNode(ToSNode prev) { + super(prev); + } + + @Specialization + public RubyString toS() { + return getContext().makeString("true"); + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/YieldingCoreMethodNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/core/YieldingCoreMethodNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.yield.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +public abstract class YieldingCoreMethodNode extends CoreMethodNode { + + @Child protected YieldDispatchNode dispatchNode; + + public YieldingCoreMethodNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + dispatchNode = adoptChild(new UninitializedYieldDispatchNode(context, getSourceSection())); + } + + public YieldingCoreMethodNode(YieldingCoreMethodNode prev) { + super(prev); + dispatchNode = adoptChild(prev.dispatchNode); + } + + public Object yield(VirtualFrame frame, RubyProc block, Object... arguments) { + return dispatchNode.dispatch(frame, block, arguments); + } + + public boolean yieldBoolean(VirtualFrame frame, RubyProc block, Object... arguments) { + return GeneralConversions.toBoolean(dispatchNode.dispatch(frame, block, arguments)); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/DebugNodes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/DebugNodes.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.debug; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.nodes.core.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +@CoreClass(name = "Debug") +public abstract class DebugNodes { + + @CoreMethod(names = "break", isModuleMethod = true, needsSelf = false, needsBlock = true, appendCallNode = true, minArgs = 0, maxArgs = 3) + public abstract static class BreakNode extends CoreMethodNode { + + public BreakNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public BreakNode(BreakNode prev) { + super(prev); + } + + @Specialization(order = 1) + public NilPlaceholder debugBreak(VirtualFrame frame, Node callNode, @SuppressWarnings("unused") UndefinedPlaceholder undefined0, @SuppressWarnings("unused") UndefinedPlaceholder undefined1, + @SuppressWarnings("unused") UndefinedPlaceholder block) { + final RubyContext context = getContext(); + if (context.getConfiguration().getDebug()) { + Node realCallNode = callNode; + while (realCallNode != null && !(realCallNode instanceof CallNode)) { + realCallNode = realCallNode.getParent(); + } + context.getDebugManager().haltedAt(realCallNode, frame.materialize()); + } + return NilPlaceholder.INSTANCE; + } + + @Specialization(order = 2) + public NilPlaceholder debugBreak(RubyString fileName, int line, @SuppressWarnings("unused") Node callNode, @SuppressWarnings("unused") UndefinedPlaceholder block) { + final RubyContext context = getContext(); + if (context.getConfiguration().getDebug()) { + final Source source = context.getSourceManager().get(fileName.toString()); + final SourceLineLocation lineLocation = new SourceLineLocation(source, line); + context.getDebugManager().setBreakpoint(lineLocation); + } + return NilPlaceholder.INSTANCE; + } + + @Specialization(order = 3) + public NilPlaceholder debugBreak(RubyString fileName, int line, @SuppressWarnings("unused") Node callNode, RubyProc block) { + final RubyContext context = getContext(); + if (context.getConfiguration().getDebug()) { + final Source source = context.getSourceManager().get(fileName.toString()); + final SourceLineLocation lineLocation = new SourceLineLocation(source, line); + context.getDebugManager().setLineProc(lineLocation, block); + } + return NilPlaceholder.INSTANCE; + } + + @Specialization(order = 4) + public NilPlaceholder debugBreak(RubySymbol methodName, RubySymbol localName, @SuppressWarnings("unused") Node callNode, @SuppressWarnings("unused") UndefinedPlaceholder block) { + final RubyContext context = getContext(); + if (context.getConfiguration().getDebug()) { + final RubyMethod method = context.getCoreLibrary().getMainObject().getLookupNode().lookupMethod(methodName.toString()); + context.getDebugManager().setLocalBreak(method.getUniqueIdentifier(), localName.toString()); + } + return NilPlaceholder.INSTANCE; + } + + @Specialization(order = 5) + public NilPlaceholder debugBreak(RubySymbol methodName, RubySymbol localName, @SuppressWarnings("unused") Node callNode, RubyProc block) { + final RubyContext context = getContext(); + if (context.getConfiguration().getDebug()) { + final RubyMethod method = context.getCoreLibrary().getMainObject().getLookupNode().lookupMethod(methodName.toString()); + context.getDebugManager().setLocalProc(method.getUniqueIdentifier(), localName.toString(), block); + } + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "continue", isModuleMethod = true, needsSelf = false, maxArgs = 0) + public abstract static class ContinueNode extends CoreMethodNode { + + public ContinueNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public ContinueNode(ContinueNode prev) { + super(prev); + } + + @Specialization + public Object debugContinue() { + if (getContext().getConfiguration().getDebug()) { + throw new BreakShellException(); + } + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "enabled?", isModuleMethod = true, needsSelf = false, maxArgs = 0) + public abstract static class EnabledNode extends CoreMethodNode { + + public EnabledNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public EnabledNode(ContinueNode prev) { + super(prev); + } + + @Specialization + public boolean enabled() { + return getContext().getConfiguration().getDebug(); + } + + } + + @CoreMethod(names = "where", isModuleMethod = true, needsSelf = false, appendCallNode = true, minArgs = 1, maxArgs = 1) + public abstract static class WhereNode extends CoreMethodNode { + + public WhereNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public WhereNode(WhereNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder where(Node callNode) { + final RubyContext context = getContext(); + if (context.getConfiguration().getDebug()) { + context.getConfiguration().getStandardOut().println(callNode.getSourceSection()); + } + return NilPlaceholder.INSTANCE; + } + + } + + @CoreMethod(names = "remove", isModuleMethod = true, needsSelf = false, needsBlock = true, minArgs = 2, maxArgs = 2) + public abstract static class RemoveNode extends CoreMethodNode { + + public RemoveNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + public RemoveNode(RemoveNode prev) { + super(prev); + } + + @Specialization + public NilPlaceholder debugRemove(RubyString fileName, int line) { + final RubyContext context = getContext(); + if (context.getConfiguration().getDebug()) { + final Source source = context.getSourceManager().get(fileName.toString()); + final SourceLineLocation lineLocation = new SourceLineLocation(source, line); + context.getDebugManager().removeBreakpoint(lineLocation); + } + return NilPlaceholder.INSTANCE; + } + + @Specialization + public NilPlaceholder debugRemove(RubySymbol methodName, RubySymbol localName) { + final RubyContext context = getContext(); + if (context.getConfiguration().getDebug()) { + final RubyMethod method = context.getCoreLibrary().getMainObject().getLookupNode().lookupMethod(methodName.toString()); + context.getDebugManager().removeLocalProbe(method.getUniqueIdentifier(), localName.toString()); + } + return NilPlaceholder.INSTANCE; + } + + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyProxyNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/debug/RubyProxyNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.debug; + +import java.math.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.nodes.instrument.*; +import com.oracle.truffle.api.nodes.instrument.InstrumentationProbeNode.ProbeChain; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * An instrumentation proxy node that forwards all Ruby execution calls through to + * a child node and returns results back to the parent, but which also sends notifications to an + * attached {@linkplain ProbeChain chain} of {@linkplain InstrumentationProbeNode probes}. + */ +public class RubyProxyNode extends RubyNode implements InstrumentationProxyNode { + + @Child private RubyNode child; + + private final ProbeChain probeChain; + + public RubyProxyNode(RubyContext context, RubyNode child) { + super(context, SourceSection.NULL); + this.child = adoptChild(child); + assert !(child instanceof RubyProxyNode); + this.probeChain = new ProbeChain(child.getSourceSection(), null); + } + + @Override + public RubyNode getNonProxyNode() { + return child; + } + + public RubyNode getChild() { + return child; + } + + public ProbeChain getProbeChain() { + return probeChain; + } + + @Override + public Object execute(VirtualFrame frame) { + probeChain.notifyEnter(child, frame); + + Object result; + + try { + result = child.execute(frame); + probeChain.notifyLeave(child, frame, result); + } catch (Exception e) { + probeChain.notifyLeaveExceptional(child, frame, e); + throw (e); + } + + return result; + } + + @Override + public RubyArray executeArray(VirtualFrame frame) throws UnexpectedResultException { + probeChain.notifyEnter(child, frame); + + RubyArray result; + + try { + result = child.executeArray(frame); + probeChain.notifyLeave(child, frame, result); + } catch (Exception e) { + probeChain.notifyLeaveExceptional(child, frame, e); + throw (e); + } + + return result; + } + + @Override + public BigInteger executeBignum(VirtualFrame frame) throws UnexpectedResultException { + probeChain.notifyEnter(child, frame); + + BigInteger result; + + try { + result = child.executeBignum(frame); + probeChain.notifyLeave(child, frame, result); + } catch (Exception e) { + probeChain.notifyLeaveExceptional(child, frame, e); + throw (e); + } + + return result; + } + + @Override + public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException { + probeChain.notifyEnter(child, frame); + + boolean result; + + try { + result = child.executeBoolean(frame); + probeChain.notifyLeave(child, frame, result); + } catch (Exception e) { + probeChain.notifyLeaveExceptional(child, frame, e); + throw (e); + } + + return result; + } + + @Override + public Object isDefined(VirtualFrame frame) { + return child.isDefined(frame); + } + + @Override + public int executeFixnum(VirtualFrame frame) throws UnexpectedResultException { + probeChain.notifyEnter(child, frame); + + int result; + + try { + result = child.executeFixnum(frame); + probeChain.notifyLeave(child, frame, result); + } catch (Exception e) { + probeChain.notifyLeaveExceptional(child, frame, e); + throw (e); + } + + return result; + } + + @Override + public double executeFloat(VirtualFrame frame) throws UnexpectedResultException { + probeChain.notifyEnter(child, frame); + + double result; + + try { + result = child.executeFloat(frame); + probeChain.notifyLeave(child, frame, result); + } catch (Exception e) { + probeChain.notifyLeaveExceptional(child, frame, e); + throw (e); + } + + return result; + } + + @Override + public RubyString executeString(VirtualFrame frame) throws UnexpectedResultException { + probeChain.notifyEnter(child, frame); + + RubyString result; + + try { + result = child.executeString(frame); + probeChain.notifyLeave(child, frame, result); + } catch (Exception e) { + probeChain.notifyLeaveExceptional(child, frame, e); + throw (e); + } + + return result; + } + + @Override + public void executeVoid(VirtualFrame frame) { + probeChain.notifyEnter(child, frame); + + try { + child.executeVoid(frame); + probeChain.notifyLeave(child, frame); + } catch (Exception e) { + probeChain.notifyLeaveExceptional(child, frame, e); + throw (e); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/BignumLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/BignumLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal; + +import java.math.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +@NodeInfo(shortName = "bignum") +public class BignumLiteralNode extends RubyNode { + + private final BigInteger value; + + public BignumLiteralNode(RubyContext context, SourceSection sourceSection, BigInteger value) { + super(context, sourceSection); + this.value = value; + } + + @Override + public BigInteger executeBignum(VirtualFrame frame) { + return value; + } + + @Override + public Object execute(VirtualFrame frame) { + return value; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/BooleanLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/BooleanLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +@NodeInfo(shortName = "boolean") +public class BooleanLiteralNode extends RubyNode { + + private final boolean value; + + public BooleanLiteralNode(RubyContext context, SourceSection sourceSection, boolean value) { + super(context, sourceSection); + this.value = value; + } + + @Override + public Object execute(VirtualFrame frame) { + return executeBoolean(frame); + } + + @Override + public boolean executeBoolean(VirtualFrame frame) { + return value; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/FixnumLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/FixnumLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +@NodeInfo(shortName = "fixnum") +public class FixnumLiteralNode extends RubyNode { + + private final int value; + + public FixnumLiteralNode(RubyContext context, SourceSection sourceSection, int value) { + super(context, sourceSection); + this.value = value; + } + + @Override + public Object execute(VirtualFrame frame) { + return executeFixnum(frame); + } + + @Override + public int executeFixnum(VirtualFrame frame) { + return value; + } + + // TODO(CS): remove this - shouldn't be fiddling with nodes from the outside + public int getValue() { + return value; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/FloatLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/FloatLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +@NodeInfo(shortName = "float") +public class FloatLiteralNode extends RubyNode { + + private final double value; + + public FloatLiteralNode(RubyContext context, SourceSection sourceSection, double value) { + super(context, sourceSection); + this.value = value; + } + + @Override + public Object execute(VirtualFrame frame) { + return executeFloat(frame); + } + + @Override + public double executeFloat(VirtualFrame frame) { + return value; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/HashLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/HashLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@NodeInfo(shortName = "hash") +public class HashLiteralNode extends RubyNode { + + @Children protected final RubyNode[] keys; + @Children protected final RubyNode[] values; + + public HashLiteralNode(SourceSection sourceSection, RubyNode[] keys, RubyNode[] values, RubyContext context) { + super(context, sourceSection); + assert keys.length == values.length; + this.keys = adoptChildren(keys); + this.values = adoptChildren(values); + } + + @ExplodeLoop + @Override + public Object execute(VirtualFrame frame) { + final RubyHash hash = new RubyHash(getContext().getCoreLibrary().getHashClass()); + + for (int n = 0; n < keys.length; n++) { + hash.put(keys[n].execute(frame), values[n].execute(frame)); + } + + return hash; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/NilNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/NilNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A node that does nothing and evaluates to Nil. A no-op. + */ +@NodeInfo(shortName = "nil") +public final class NilNode extends RubyNode { + + public NilNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + @Override + public NilPlaceholder executeNilPlaceholder(VirtualFrame frame) { + return NilPlaceholder.INSTANCE; + } + + @Override + public Object execute(VirtualFrame frame) { + return executeNilPlaceholder(frame); + } + + @Override + public void executeVoid(VirtualFrame frame) { + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/ObjectLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/ObjectLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal; + +import java.math.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@NodeInfo(shortName = "object") +public class ObjectLiteralNode extends RubyNode { + + private final Object object; + + public ObjectLiteralNode(RubyContext context, SourceSection sourceSection, Object object) { + super(context, sourceSection); + + assert RubyContext.shouldObjectBeVisible(object); + assert !(object instanceof Integer); + assert !(object instanceof Double); + assert !(object instanceof BigInteger); + assert !(object instanceof String); + assert !(object instanceof RubyString); + + this.object = object; + } + + @Override + public Object execute(VirtualFrame frame) { + return object; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/RangeLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/RangeLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.range.*; + +@NodeInfo(shortName = "range") +@NodeChildren({@NodeChild("begin"), @NodeChild("end")}) +public abstract class RangeLiteralNode extends RubyNode { + + private final boolean excludeEnd; + + public RangeLiteralNode(RubyContext context, SourceSection sourceSection, boolean excludeEnd) { + super(context, sourceSection); + this.excludeEnd = excludeEnd; + } + + public RangeLiteralNode(RangeLiteralNode prev) { + this(prev.getContext(), prev.getSourceSection(), prev.excludeEnd); + } + + @Specialization + public FixnumRange doFixnum(int begin, int end) { + return new FixnumRange(getContext().getCoreLibrary().getRangeClass(), begin, end, excludeEnd); + } + + @Generic + public Object doGeneric(Object begin, Object end) { + final RubyContext context = getContext(); + + if ((begin instanceof Integer) && (end instanceof Integer)) { + return doFixnum((int) begin, (int) end); + } else { + return new ObjectRange(context.getCoreLibrary().getRangeClass(), begin, end, excludeEnd); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/StringLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/StringLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +@NodeInfo(shortName = "string") +public class StringLiteralNode extends RubyNode { + + private final String string; + + public StringLiteralNode(RubyContext context, SourceSection sourceSection, String string) { + super(context, sourceSection); + + assert string != null; + + this.string = string; + } + + @Override + public Object execute(VirtualFrame frame) { + return getContext().makeString(string); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/array/ArrayLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/array/ArrayLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal.array; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +public abstract class ArrayLiteralNode extends RubyNode { + + @Children protected final RubyNode[] values; + + public ArrayLiteralNode(RubyContext context, SourceSection sourceSection, RubyNode[] values) { + super(context, sourceSection); + this.values = adoptChildren(values); + } + + protected RubyArray makeGeneric(VirtualFrame frame, Object[] alreadyExecuted) { + CompilerAsserts.neverPartOfCompilation(); + + replace(new ObjectArrayLiteralNode(getContext(), getSourceSection(), values)); + + final Object[] executedValues = new Object[values.length]; + + for (int n = 0; n < values.length; n++) { + if (n < alreadyExecuted.length) { + executedValues[n] = alreadyExecuted[n]; + } else { + executedValues[n] = values[n].execute(frame); + } + } + + return RubyArray.specializedFromObjects(getContext().getCoreLibrary().getArrayClass(), executedValues); + } + + @Override + public Object isDefined(VirtualFrame frame) { + return getContext().makeString("expression"); + } + + // TODO(CS): remove this - shouldn't be fiddling with nodes from the outside + public RubyNode[] getValues() { + return values; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/array/FixnumArrayLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/array/FixnumArrayLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal.array; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@NodeInfo(shortName = "fixnum-array-literal") +public class FixnumArrayLiteralNode extends ArrayLiteralNode { + + public FixnumArrayLiteralNode(RubyContext context, SourceSection sourceSection, RubyNode[] values) { + super(context, sourceSection, values); + } + + @ExplodeLoop + @Override + public RubyArray executeArray(VirtualFrame frame) { + final int[] executedValues = new int[values.length]; + + for (int n = 0; n < values.length; n++) { + try { + executedValues[n] = values[n].executeFixnum(frame); + } catch (UnexpectedResultException e) { + final Object[] executedObjects = new Object[n]; + + for (int i = 0; i < n; i++) { + executedObjects[i] = executedValues[i]; + } + + return makeGeneric(frame, executedObjects); + } + } + + return new RubyArray(getContext().getCoreLibrary().getArrayClass(), new FixnumArrayStore(executedValues)); + } + + @ExplodeLoop + @Override + public void executeVoid(VirtualFrame frame) { + for (int n = 0; n < values.length; n++) { + values[n].executeVoid(frame); + } + } + + @Override + public Object execute(VirtualFrame frame) { + return executeArray(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/array/ObjectArrayLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/array/ObjectArrayLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal.array; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@NodeInfo(shortName = "object-array-literal") +public class ObjectArrayLiteralNode extends ArrayLiteralNode { + + public ObjectArrayLiteralNode(RubyContext context, SourceSection sourceSection, RubyNode[] values) { + super(context, sourceSection, values); + } + + @ExplodeLoop + @Override + public RubyArray executeArray(VirtualFrame frame) { + final Object[] executedValues = new Object[values.length]; + + for (int n = 0; n < values.length; n++) { + executedValues[n] = values[n].execute(frame); + } + + return new RubyArray(getContext().getCoreLibrary().getArrayClass(), new ObjectArrayStore(executedValues)); + } + + @ExplodeLoop + @Override + public void executeVoid(VirtualFrame frame) { + for (int n = 0; n < values.length; n++) { + values[n].executeVoid(frame); + } + } + + @Override + public Object execute(VirtualFrame frame) { + return executeArray(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/array/UninitialisedArrayLiteralNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/literal/array/UninitialisedArrayLiteralNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.literal.array; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +@NodeInfo(shortName = "uninit-array-literal") +public class UninitialisedArrayLiteralNode extends ArrayLiteralNode { + + public UninitialisedArrayLiteralNode(RubyContext context, SourceSection sourceSection, RubyNode[] values) { + super(context, sourceSection, values); + } + + @ExplodeLoop + @Override + public Object execute(VirtualFrame frame) { + CompilerDirectives.transferToInterpreter(); + + final Object[] executedValues = new Object[values.length]; + + for (int n = 0; n < values.length; n++) { + executedValues[n] = values[n].execute(frame); + } + + final RubyArray array = RubyArray.specializedFromObjects(getContext().getCoreLibrary().getArrayClass(), executedValues); + final ArrayStore store = array.getArrayStore(); + + if (store instanceof FixnumArrayStore) { + replace(new FixnumArrayLiteralNode(getContext(), getSourceSection(), values)); + } else { + replace(new ObjectArrayLiteralNode(getContext(), getSourceSection(), values)); + } + + return array; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/AddMethodNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/AddMethodNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +@NodeInfo(shortName = "add-method") +public class AddMethodNode extends RubyNode { + + @Child protected RubyNode receiver; + @Child protected MethodDefinitionNode method; + + public AddMethodNode(RubyContext context, SourceSection section, RubyNode receiver, MethodDefinitionNode method) { + super(context, section); + this.receiver = adoptChild(receiver); + this.method = adoptChild(method); + } + + @Override + public Object execute(VirtualFrame frame) { + final Object receiverObject = receiver.execute(frame); + + final RubyMethod methodObject = (RubyMethod) method.execute(frame); + + final FrameSlot moduleFunctionFlagSlot = frame.getFrameDescriptor().findFrameSlot(RubyModule.MODULE_FUNCTION_FLAG_FRAME_SLOT_ID); + + boolean moduleFunctionFlag; + + if (moduleFunctionFlagSlot == null) { + moduleFunctionFlag = false; + } else { + Object moduleFunctionObject; + + try { + moduleFunctionObject = frame.getObject(moduleFunctionFlagSlot); + } catch (FrameSlotTypeException e) { + throw new RuntimeException(e); + } + + if (moduleFunctionObject instanceof Boolean) { + moduleFunctionFlag = (boolean) moduleFunctionObject; + } else { + moduleFunctionFlag = false; + } + } + + final RubyModule module = (RubyModule) receiverObject; + + final RubyMethod methodWithDeclaringModule = methodObject.withDeclaringModule(module); + + module.addMethod(methodWithDeclaringModule); + + if (moduleFunctionFlag) { + module.getSingletonClass().addMethod(methodWithDeclaringModule); + } + + return NilPlaceholder.INSTANCE; + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/AliasNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/AliasNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@NodeInfo(shortName = "alias") +public class AliasNode extends RubyNode { + + @Child protected RubyNode module; + final String newName; + final String oldName; + + public AliasNode(RubyContext context, SourceSection sourceSection, RubyNode module, String newName, String oldName) { + super(context, sourceSection); + this.module = adoptChild(module); + this.newName = newName; + this.oldName = oldName; + } + + @Override + public void executeVoid(VirtualFrame frame) { + final RubyModule moduleObject = (RubyModule) module.execute(frame); + moduleObject.alias(newName, oldName); + } + + @Override + public Object execute(VirtualFrame frame) { + executeVoid(frame); + return NilPlaceholder.INSTANCE; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/BlockDefinitionNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/BlockDefinitionNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * Define a block. That is, store the definition of a block and when executed produce the executable + * object that results. + */ +@NodeInfo(shortName = "block-def") +public class BlockDefinitionNode extends MethodDefinitionNode { + + public BlockDefinitionNode(RubyContext context, SourceSection sourceSection, String name, UniqueMethodIdentifier uniqueIdentifier, FrameDescriptor frameDescriptor, + boolean requiresDeclarationFrame, RubyRootNode pristineRootNode, CallTarget callTarget) { + super(context, sourceSection, name, uniqueIdentifier, frameDescriptor, requiresDeclarationFrame, pristineRootNode, callTarget); + } + + @Override + public Object execute(VirtualFrame frame) { + final RubyContext context = getContext(); + + MaterializedFrame declarationFrame; + + if (requiresDeclarationFrame) { + declarationFrame = frame.materialize(); + } else { + declarationFrame = null; + } + + final RubyArguments arguments = frame.getArguments(RubyArguments.class); + + final InlinableMethodImplementation methodImplementation = new InlinableMethodImplementation(callTarget, declarationFrame, frameDescriptor, pristineRootNode, true, false); + final RubyMethod method = new RubyMethod(getSourceSection(), null, uniqueIdentifier, null, name, Visibility.PUBLIC, false, methodImplementation); + + return new RubyProc(context.getCoreLibrary().getProcClass(), RubyProc.Type.PROC, arguments.getSelf(), arguments.getBlock(), method); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/CatchNextNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/CatchNextNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; + +/** + * Catch a {@code next} jump at the root of a method. + */ +public class CatchNextNode extends RubyNode { + + @Child protected RubyNode body; + + private final BranchProfile nextProfile = new BranchProfile(); + + public CatchNextNode(RubyContext context, SourceSection sourceSection, RubyNode body) { + super(context, sourceSection); + this.body = adoptChild(body); + } + + @Override + public Object execute(VirtualFrame frame) { + try { + return body.execute(frame); + } catch (NextException e) { + nextProfile.enter(); + return NilPlaceholder.INSTANCE; + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/CatchReturnAsErrorNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/CatchReturnAsErrorNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; + +/** + * Catch a {@code return} jump at the root of a method, and report it as an error. + */ +public class CatchReturnAsErrorNode extends RubyNode { + + @Child protected RubyNode body; + + public CatchReturnAsErrorNode(RubyContext context, SourceSection sourceSection, RubyNode body) { + super(context, sourceSection); + this.body = adoptChild(body); + } + + @Override + public Object execute(VirtualFrame frame) { + try { + return body.execute(frame); + } catch (ReturnException e) { + CompilerDirectives.transferToInterpreter(); + throw new RaiseException(getContext().getCoreLibrary().unexpectedReturn()); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/CatchReturnNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/CatchReturnNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; + +/** + * Catch a {@code return} jump at the root of a method. + */ +public class CatchReturnNode extends RubyNode { + + @Child protected RubyNode body; + private final long returnID; + + private final BranchProfile returnProfile = new BranchProfile(); + private final BranchProfile returnToOtherMethodProfile = new BranchProfile(); + + public CatchReturnNode(RubyContext context, SourceSection sourceSection, RubyNode body, long returnID) { + super(context, sourceSection); + this.body = adoptChild(body); + this.returnID = returnID; + } + + public CatchReturnNode(CatchReturnNode prev) { + super(prev); + returnID = prev.returnID; + } + + @Override + public Object execute(VirtualFrame frame) { + try { + return body.execute(frame); + } catch (ReturnException e) { + returnProfile.enter(); + + if (e.getReturnID() == returnID) { + return e.getValue(); + } else { + returnToOtherMethodProfile.enter(); + throw e; + } + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/MethodDefinitionNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/MethodDefinitionNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * Define a method. That is, store the definition of a method and when executed produce the + * executable object that results. + */ +@NodeInfo(shortName = "method-def") +public class MethodDefinitionNode extends RubyNode { + + protected final String name; + protected final UniqueMethodIdentifier uniqueIdentifier; + + protected final FrameDescriptor frameDescriptor; + protected final RubyRootNode pristineRootNode; + + protected final CallTarget callTarget; + + protected final boolean requiresDeclarationFrame; + + public MethodDefinitionNode(RubyContext context, SourceSection sourceSection, String name, UniqueMethodIdentifier uniqueIdentifier, FrameDescriptor frameDescriptor, + boolean requiresDeclarationFrame, RubyRootNode pristineRootNode, CallTarget callTarget) { + super(context, sourceSection); + this.name = name; + this.uniqueIdentifier = uniqueIdentifier; + this.frameDescriptor = frameDescriptor; + this.requiresDeclarationFrame = requiresDeclarationFrame; + this.pristineRootNode = pristineRootNode; + this.callTarget = callTarget; + } + + public RubyMethod executeMethod(VirtualFrame frame) { + CompilerDirectives.transferToInterpreter(); + + MaterializedFrame declarationFrame; + + if (requiresDeclarationFrame) { + declarationFrame = frame.materialize(); + } else { + declarationFrame = null; + } + + final FrameSlot visibilitySlot = frame.getFrameDescriptor().findFrameSlot(RubyModule.VISIBILITY_FRAME_SLOT_ID); + + Visibility visibility; + + if (visibilitySlot == null) { + visibility = Visibility.PUBLIC; + } else { + Object visibilityObject; + + try { + visibilityObject = frame.getObject(visibilitySlot); + } catch (FrameSlotTypeException e) { + throw new RuntimeException(e); + } + + if (visibilityObject instanceof Visibility) { + visibility = (Visibility) visibilityObject; + } else { + visibility = Visibility.PUBLIC; + } + } + + final InlinableMethodImplementation methodImplementation = new InlinableMethodImplementation(callTarget, declarationFrame, frameDescriptor, pristineRootNode, false, false); + return new RubyMethod(getSourceSection(), null, uniqueIdentifier, null, name, visibility, false, methodImplementation); + } + + @Override + public Object execute(VirtualFrame frame) { + return executeMethod(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/ShellResultNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/ShellResultNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Produce a {@link ShellResult} object from a return value and the resulting frame. + */ +public class ShellResultNode extends RubyNode { + + @Child protected RubyNode body; + + public ShellResultNode(RubyContext context, SourceSection sourceSection, RubyNode body) { + super(context, sourceSection); + this.body = adoptChild(body); + } + + @Override + public Object execute(VirtualFrame frame) { + return new ShellResult(body.execute(frame), frame.materialize()); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/BlockDestructureSwitchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/BlockDestructureSwitchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.arguments; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * Switches between loading arguments as normal and doing a destructure. See + * testBlockArgumentsDestructure and MethodTranslator. + */ +@NodeInfo(shortName = "block-destructure-switch") +public class BlockDestructureSwitchNode extends RubyNode { + + @Child protected RubyNode loadIndividualArguments; + @Child protected RubyNode destructureArguments; + @Child protected RubyNode body; + + public BlockDestructureSwitchNode(RubyContext context, SourceSection sourceSection, RubyNode loadIndividualArguments, RubyNode destructureArguments, RubyNode body) { + super(context, sourceSection); + this.loadIndividualArguments = adoptChild(loadIndividualArguments); + this.destructureArguments = adoptChild(destructureArguments); + this.body = adoptChild(body); + } + + @Override + public Object execute(VirtualFrame frame) { + final RubyArguments arguments = frame.getArguments(RubyArguments.class); + + if (arguments.getArguments().length == 1 && arguments.getArguments()[0] instanceof RubyArray) { + destructureArguments.executeVoid(frame); + } else { + loadIndividualArguments.executeVoid(frame); + } + + return body.execute(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/CheckArityNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/CheckArityNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.arguments; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * Check arguments meet the arity of the method. + */ +@NodeInfo(shortName = "check-arity") +public class CheckArityNode extends RubyNode { + + private final Arity arity; + + public CheckArityNode(RubyContext context, SourceSection sourceSection, Arity arity) { + super(context, sourceSection); + this.arity = arity; + } + + @Override + public void executeVoid(VirtualFrame frame) { + final RubyArguments arguments = frame.getArguments(RubyArguments.class); + arity.checkArguments(getContext(), arguments.getArguments()); + } + + @Override + public Object execute(VirtualFrame frame) { + executeVoid(frame); + return NilPlaceholder.INSTANCE; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadAllArgumentsNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadAllArgumentsNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.arguments; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +public class ReadAllArgumentsNode extends RubyNode { + + public ReadAllArgumentsNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + @Override + public Object[] executeObjectArray(VirtualFrame frame) { + return frame.getArguments(RubyArguments.class).getArguments(); + } + + @Override + public Object execute(VirtualFrame frame) { + return executeObjectArray(frame); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadBlockArgumentNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadBlockArgumentNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.arguments; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Read the block as a {@code Proc}. + */ +@NodeInfo(shortName = "read-block-argument") +public class ReadBlockArgumentNode extends RubyNode { + + private final boolean undefinedIfNotPresent; + + public ReadBlockArgumentNode(RubyContext context, SourceSection sourceSection, boolean undefinedIfNotPresent) { + super(context, sourceSection); + this.undefinedIfNotPresent = undefinedIfNotPresent; + } + + @Override + public Object execute(VirtualFrame frame) { + final RubyArguments arguments = frame.getArguments(RubyArguments.class); + final RubyProc block = arguments.getBlock(); + + if (block == null) { + if (undefinedIfNotPresent) { + return UndefinedPlaceholder.INSTANCE; + } else { + return NilPlaceholder.INSTANCE; + } + } else { + return block; + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadDestructureArgumentNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadDestructureArgumentNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.arguments; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * Assuming argument 0 is an array, read an element from that array. + */ +@NodeInfo(shortName = "read-destructure-argument") +public class ReadDestructureArgumentNode extends RubyNode { + + private final int index; + + public ReadDestructureArgumentNode(RubyContext context, SourceSection sourceSection, int index) { + super(context, sourceSection); + this.index = index; + } + + @Override + public Object execute(VirtualFrame frame) { + final RubyArray array = (RubyArray) frame.getArguments(RubyArguments.class).getArguments()[0]; + return array.get(index); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadOptionalArgumentNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadOptionalArgumentNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.arguments; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Read an optional argument. + */ +@NodeInfo(shortName = "read-optional-argument") +public class ReadOptionalArgumentNode extends RubyNode { + + private final int index; + private final int minimum; + @Child protected RubyNode defaultValue; + + private final BranchProfile defaultValueProfile = new BranchProfile(); + + public ReadOptionalArgumentNode(RubyContext context, SourceSection sourceSection, int index, int minimum, RubyNode defaultValue) { + super(context, sourceSection); + this.index = index; + this.minimum = minimum; + this.defaultValue = adoptChild(defaultValue); + } + + @Override + public Object execute(VirtualFrame frame) { + final Object[] arguments = frame.getArguments(RubyArguments.class).getArguments(); + + if (arguments.length < minimum) { + defaultValueProfile.enter(); + return defaultValue.execute(frame); + } else { + assert index < arguments.length; + return arguments[index]; + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadPostArgumentNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadPostArgumentNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.arguments; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Read a post-optional argument. + */ +@NodeInfo(shortName = "read-post-optional-argument") +public class ReadPostArgumentNode extends RubyNode { + + private final int indexFromEnd; + + public ReadPostArgumentNode(RubyContext context, SourceSection sourceSection, int indexFromEnd) { + super(context, sourceSection); + this.indexFromEnd = indexFromEnd; + } + + @Override + public Object execute(VirtualFrame frame) { + final Object[] arguments = frame.getArguments(RubyArguments.class).getArguments(); + final int effectiveIndex = arguments.length - 1 - indexFromEnd; + assert effectiveIndex < arguments.length; + return arguments[effectiveIndex]; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadPreArgumentNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadPreArgumentNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.arguments; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Read pre-optional argument. + */ +@NodeInfo(shortName = "read-pre-optional-argument") +public class ReadPreArgumentNode extends RubyNode { + + private final int index; + private final boolean undefinedIfNotPresent; + + private final BranchProfile notPresentProfile = new BranchProfile(); + + public ReadPreArgumentNode(RubyContext context, SourceSection sourceSection, int index, boolean undefinedIfNotPresent) { + super(context, sourceSection); + this.index = index; + this.undefinedIfNotPresent = undefinedIfNotPresent; + } + + @Override + public Object execute(VirtualFrame frame) { + final Object[] arguments = frame.getArguments(RubyArguments.class).getArguments(); + + if (undefinedIfNotPresent) { + if (index >= arguments.length) { + notPresentProfile.enter(); + return UndefinedPlaceholder.INSTANCE; + } + } else { + assert index < arguments.length; + } + + return arguments[index]; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadRestArgumentNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/arguments/ReadRestArgumentNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.arguments; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * Read the rest of arguments after a certain point into an array. + */ +@NodeInfo(shortName = "read-rest-of-arguments") +public class ReadRestArgumentNode extends RubyNode { + + private final int index; + + public ReadRestArgumentNode(RubyContext context, SourceSection sourceSection, int index) { + super(context, sourceSection); + this.index = index; + } + + @Override + public Object execute(VirtualFrame frame) { + final RubyArguments rubyArguments = frame.getArguments(RubyArguments.class); + + final Object[] arguments = rubyArguments.getArguments(); + + final RubyClass arrayClass = getContext().getCoreLibrary().getArrayClass(); + + if (arguments.length <= index) { + return new RubyArray(arrayClass); + } else if (index == 0) { + return new RubyArray(arrayClass, new ObjectArrayStore(arguments)); + } else { + return new RubyArray(arrayClass, new ObjectArrayStore(Arrays.copyOfRange(arguments, index, arguments.length))); + } + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/FlipFlopStateNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/FlipFlopStateNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.locals; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +public abstract class FlipFlopStateNode extends Node { + + public FlipFlopStateNode(SourceSection sourceSection) { + super(sourceSection); + } + + public abstract boolean getState(VirtualFrame frame); + + public abstract void setState(VirtualFrame frame, boolean state); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/FrameSlotNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/FrameSlotNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.locals; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +public abstract class FrameSlotNode extends RubyNode { + + protected final FrameSlot frameSlot; + + protected FrameSlotNode(RubyContext context, SourceSection sourceSection, FrameSlot frameSlot) { + super(context, sourceSection); + this.frameSlot = frameSlot; + } + + public final FrameSlot getFrameSlot() { + return frameSlot; + } + + @Override + public FrameSlotNode copy() { + return (FrameSlotNode) super.copy(); + } + + protected final void setBoolean(Frame frame, boolean value) { + frame.setBoolean(frameSlot, value); + } + + protected final void setFixnum(Frame frame, int value) { + frame.setInt(frameSlot, value); + } + + protected final void setFloat(Frame frame, double value) { + frame.setDouble(frameSlot, value); + } + + protected final void setObject(Frame frame, Object value) { + frame.setObject(frameSlot, value); + } + + protected final boolean getBoolean(Frame frame) throws FrameSlotTypeException { + return frame.getBoolean(frameSlot); + } + + protected final int getFixnum(Frame frame) throws FrameSlotTypeException { + return frame.getInt(frameSlot); + } + + protected final double getFloat(Frame frame) throws FrameSlotTypeException { + return frame.getDouble(frameSlot); + } + + protected final Object getObject(Frame frame) { + try { + return frame.getObject(frameSlot); + } catch (FrameSlotTypeException e) { + throw new IllegalStateException(); + } + } + + protected final boolean isBooleanKind() { + return isKind(FrameSlotKind.Boolean); + } + + protected final boolean isFixnumKind() { + return isKind(FrameSlotKind.Int); + } + + protected final boolean isFloatKind() { + return isKind(FrameSlotKind.Double); + } + + protected final boolean isObjectKind() { + if (frameSlot.getKind() != FrameSlotKind.Object) { + CompilerDirectives.transferToInterpreter(); + frameSlot.setKind(FrameSlotKind.Object); + } + return true; + } + + private boolean isKind(FrameSlotKind kind) { + return frameSlot.getKind() == kind || initialSetKind(kind); + } + + private boolean initialSetKind(FrameSlotKind kind) { + if (frameSlot.getKind() == FrameSlotKind.Illegal) { + CompilerDirectives.transferToInterpreter(); + frameSlot.setKind(kind); + return true; + } + return false; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/InitFlipFlopSlotNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/InitFlipFlopSlotNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.locals; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +public class InitFlipFlopSlotNode extends RubyNode { + + private final FrameSlot frameSlot; + + public InitFlipFlopSlotNode(RubyContext context, SourceSection sourceSection, FrameSlot frameSlot) { + super(context, sourceSection); + this.frameSlot = frameSlot; + } + + @Override + public void executeVoid(VirtualFrame frame) { + frame.setBoolean(frameSlot, false); + } + + @Override + public Object execute(VirtualFrame frame) { + executeVoid(frame); + return null; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/LevelFlipFlopStateNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/LevelFlipFlopStateNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.locals; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; + +public class LevelFlipFlopStateNode extends FlipFlopStateNode { + + private final int level; + private final FrameSlot frameSlot; + + public LevelFlipFlopStateNode(SourceSection sourceSection, int level, FrameSlot frameSlot) { + super(sourceSection); + this.level = level; + this.frameSlot = frameSlot; + } + + @Override + public boolean getState(VirtualFrame frame) { + final MaterializedFrame levelFrame = RubyArguments.getDeclarationFrame(frame, level); + + try { + return levelFrame.getBoolean(frameSlot); + } catch (FrameSlotTypeException e) { + throw new IllegalStateException(); + } + } + + @Override + public void setState(VirtualFrame frame, boolean state) { + final MaterializedFrame levelFrame = RubyArguments.getDeclarationFrame(frame, level); + levelFrame.setBoolean(frameSlot, state); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/LocalFlipFlopStateNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/LocalFlipFlopStateNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.locals; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; + +public class LocalFlipFlopStateNode extends FlipFlopStateNode { + + private final FrameSlot frameSlot; + + public LocalFlipFlopStateNode(SourceSection sourceSection, FrameSlot frameSlot) { + super(sourceSection); + this.frameSlot = frameSlot; + } + + @Override + public boolean getState(VirtualFrame frame) { + try { + return frame.getBoolean(frameSlot); + } catch (FrameSlotTypeException e) { + throw new IllegalStateException(); + } + } + + @Override + public void setState(VirtualFrame frame, boolean state) { + frame.setBoolean(frameSlot, state); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/ReadLevelVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/ReadLevelVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.locals; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +public abstract class ReadLevelVariableNode extends FrameSlotNode implements ReadNode { + + private final int varLevel; + + public ReadLevelVariableNode(RubyContext context, SourceSection sourceSection, FrameSlot slot, int level) { + super(context, sourceSection, slot); + this.varLevel = level; + } + + public ReadLevelVariableNode(ReadLevelVariableNode prev) { + this(prev.getContext(), prev.getSourceSection(), prev.frameSlot, prev.varLevel); + } + + @Specialization(rewriteOn = {FrameSlotTypeException.class}) + public boolean doBoolean(VirtualFrame frame) throws FrameSlotTypeException { + MaterializedFrame levelFrame = RubyArguments.getDeclarationFrame(frame, varLevel); + return getBoolean(levelFrame); + } + + @Specialization(rewriteOn = {FrameSlotTypeException.class}) + public int doFixnum(VirtualFrame frame) throws FrameSlotTypeException { + MaterializedFrame levelFrame = RubyArguments.getDeclarationFrame(frame, varLevel); + return getFixnum(levelFrame); + } + + @Specialization(rewriteOn = {FrameSlotTypeException.class}) + public double doFloat(VirtualFrame frame) throws FrameSlotTypeException { + MaterializedFrame levelFrame = RubyArguments.getDeclarationFrame(frame, varLevel); + return getFloat(levelFrame); + } + + @Specialization + public Object doObject(VirtualFrame frame) { + MaterializedFrame levelFrame = RubyArguments.getDeclarationFrame(frame, varLevel); + return getObject(levelFrame); + } + + public int getVarLevel() { + return varLevel; + } + + @Override + public RubyNode makeWriteNode(RubyNode rhs) { + return WriteLevelVariableNodeFactory.create(getContext(), getSourceSection(), frameSlot, varLevel, rhs); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/ReadLocalVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/ReadLocalVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.locals; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +public abstract class ReadLocalVariableNode extends FrameSlotNode implements ReadNode { + + public ReadLocalVariableNode(RubyContext context, SourceSection sourceSection, FrameSlot slot) { + super(context, sourceSection, slot); + } + + public ReadLocalVariableNode(ReadLocalVariableNode prev) { + this(prev.getContext(), prev.getSourceSection(), prev.frameSlot); + } + + @Specialization(rewriteOn = {FrameSlotTypeException.class}) + public boolean doBoolean(VirtualFrame frame) throws FrameSlotTypeException { + return getBoolean(frame); + } + + @Specialization(rewriteOn = {FrameSlotTypeException.class}) + public int doFixnum(VirtualFrame frame) throws FrameSlotTypeException { + return getFixnum(frame); + } + + @Specialization(rewriteOn = {FrameSlotTypeException.class}) + public double doFloat(VirtualFrame frame) throws FrameSlotTypeException { + return getFloat(frame); + } + + @Specialization + public Object doObject(VirtualFrame frame) { + return getObject(frame); + } + + @Override + public RubyNode makeWriteNode(RubyNode rhs) { + return WriteLocalVariableNodeFactory.create(getContext(), getSourceSection(), frameSlot, rhs); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/WriteLevelVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/WriteLevelVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.locals; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +@NodeChild(value = "rhs", type = RubyNode.class) +public abstract class WriteLevelVariableNode extends FrameSlotNode implements WriteNode { + + private final int varLevel; + + public WriteLevelVariableNode(RubyContext context, SourceSection sourceSection, FrameSlot frameSlot, int level) { + super(context, sourceSection, frameSlot); + this.varLevel = level; + } + + protected WriteLevelVariableNode(WriteLevelVariableNode prev) { + this(prev.getContext(), prev.getSourceSection(), prev.frameSlot, prev.varLevel); + } + + @Specialization(guards = "isBooleanKind") + public boolean doBoolean(VirtualFrame frame, boolean value) { + MaterializedFrame levelFrame = RubyArguments.getDeclarationFrame(frame, varLevel); + setBoolean(levelFrame, value); + return value; + } + + @Specialization(guards = "isFixnumKind") + public int doFixnum(VirtualFrame frame, int value) { + MaterializedFrame levelFrame = RubyArguments.getDeclarationFrame(frame, varLevel); + setFixnum(levelFrame, value); + return value; + } + + @Specialization(guards = "isFloatKind") + public double doFloat(VirtualFrame frame, double value) { + MaterializedFrame levelFrame = RubyArguments.getDeclarationFrame(frame, varLevel); + setFloat(levelFrame, value); + return value; + } + + @Specialization(guards = "isObjectKind") + public Object doObject(VirtualFrame frame, Object value) { + MaterializedFrame levelFrame = RubyArguments.getDeclarationFrame(frame, varLevel); + setObject(levelFrame, value); + return value; + } + + @Override + public RubyNode makeReadNode() { + return ReadLevelVariableNodeFactory.create(getContext(), getSourceSection(), frameSlot, varLevel); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/WriteLocalVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/methods/locals/WriteLocalVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.methods.locals; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.dsl.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +@NodeChild(value = "rhs", type = RubyNode.class) +public abstract class WriteLocalVariableNode extends FrameSlotNode implements WriteNode { + + public WriteLocalVariableNode(RubyContext context, SourceSection sourceSection, FrameSlot frameSlot) { + super(context, sourceSection, frameSlot); + } + + protected WriteLocalVariableNode(WriteLocalVariableNode prev) { + this(prev.getContext(), prev.getSourceSection(), prev.frameSlot); + } + + @Specialization(guards = "isBooleanKind") + public boolean doFixnum(VirtualFrame frame, boolean value) { + setBoolean(frame, value); + return value; + } + + @Specialization(guards = "isFixnumKind") + public int doFixnum(VirtualFrame frame, int value) { + setFixnum(frame, value); + return value; + } + + @Specialization(guards = "isFloatKind") + public double doFloat(VirtualFrame frame, double value) { + setFloat(frame, value); + return value; + } + + @Specialization(guards = "isObjectKind") + public Object doObject(VirtualFrame frame, Object value) { + setObject(frame, value); + return value; + } + + @Override + public RubyNode makeReadNode() { + return ReadLocalVariableNodeFactory.create(getContext(), getSourceSection(), frameSlot); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/ClassNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/ClassNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Reads the class of an object. + */ +@NodeInfo(shortName = "class") +public class ClassNode extends RubyNode { + + @Child protected RubyNode child; + + public ClassNode(RubyContext context, SourceSection sourceSection, RubyNode child) { + super(context, sourceSection); + this.child = adoptChild(child); + } + + @Override + public Object execute(VirtualFrame frame) { + return ((RubyBasicObject) child.execute(frame)).getRubyClass(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/DefineOrGetClassNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/DefineOrGetClassNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Define a new class, or get the existing one of the same name. + */ +public class DefineOrGetClassNode extends RubyNode { + + private final String name; + @Child protected RubyNode moduleDefinedIn; + @Child protected RubyNode superClass; + + public DefineOrGetClassNode(RubyContext context, SourceSection sourceSection, String name, RubyNode moduleDefinedIn, RubyNode superClass) { + super(context, sourceSection); + this.name = name; + this.moduleDefinedIn = adoptChild(moduleDefinedIn); + this.superClass = adoptChild(superClass); + } + + @Override + public Object execute(VirtualFrame frame) { + CompilerAsserts.neverPartOfCompilation(); + + final RubyContext context = getContext(); + + final RubyModule moduleDefinedInObject = (RubyModule) moduleDefinedIn.execute(frame); + + // Look for a current definition of the class, or create a new one + + final Object constantValue = moduleDefinedInObject.lookupConstant(name); + + RubyClass definingClass; + + if (constantValue == null) { + final Object self = frame.getArguments(RubyArguments.class).getSelf(); + + RubyModule parentModule; + + if (self instanceof RubyModule) { + parentModule = (RubyModule) self; + } else { + // Because it's top level, and so self is the magic main object + parentModule = null; + } + + final RubyClass superClassObject = (RubyClass) superClass.execute(frame); + + if (superClassObject instanceof RubyException.RubyExceptionClass) { + definingClass = new RubyException.RubyExceptionClass(superClassObject, name); + } else { + definingClass = new RubyClass(parentModule, superClassObject, name); + } + + moduleDefinedInObject.setConstant(name, definingClass); + moduleDefinedInObject.getSingletonClass().setConstant(name, definingClass); + + definingClass.getRubyClass().include(moduleDefinedInObject); + } else { + if (constantValue instanceof RubyClass) { + definingClass = (RubyClass) constantValue; + } else { + throw new RaiseException(context.getCoreLibrary().typeErrorIsNotA(constantValue.toString(), "class")); + } + } + + return definingClass; + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/DefineOrGetModuleNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/DefineOrGetModuleNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Define a new module, or get the existing one of the same name. + */ +public class DefineOrGetModuleNode extends RubyNode { + + private final String name; + @Child protected RubyNode moduleDefinedIn; + + public DefineOrGetModuleNode(RubyContext context, SourceSection sourceSection, String name, RubyNode moduleDefinedIn) { + super(context, sourceSection); + this.name = name; + this.moduleDefinedIn = adoptChild(moduleDefinedIn); + } + + @Override + public Object execute(VirtualFrame frame) { + CompilerAsserts.neverPartOfCompilation(); + + final RubyContext context = getContext(); + + final RubyModule moduleDefinedInObject = (RubyModule) moduleDefinedIn.execute(frame); + + // Look for a current definition of the module, or create a new one + + final Object constantValue = moduleDefinedInObject.lookupConstant(name); + + RubyModule definingModule; + + if (constantValue == null) { + final Object self = frame.getArguments(RubyArguments.class).getSelf(); + + RubyModule parentModule; + + if (self instanceof RubyModule) { + parentModule = (RubyModule) self; + } else { + // Because it's top level, and so self is the magic main object + parentModule = null; + } + + definingModule = new RubyModule(context.getCoreLibrary().getModuleClass(), parentModule, name); + moduleDefinedInObject.setConstant(name, definingModule); + moduleDefinedInObject.getSingletonClass().setConstant(name, definingModule); + } else { + if (constantValue instanceof RubyModule) { + definingModule = (RubyModule) constantValue; + } else { + throw new RaiseException(context.getCoreLibrary().typeErrorIsNotA(constantValue.toString(), "module")); + } + } + + return definingModule; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/OpenModuleNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/OpenModuleNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.methods.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Open a module and execute a method in it - probably to define new methods. + */ +public class OpenModuleNode extends RubyNode { + + @Child protected RubyNode definingModule; + @Child protected MethodDefinitionNode definitionMethod; + + public OpenModuleNode(RubyContext context, SourceSection sourceSection, RubyNode definingModule, MethodDefinitionNode definitionMethod) { + super(context, sourceSection); + this.definingModule = adoptChild(definingModule); + this.definitionMethod = adoptChild(definitionMethod); + } + + @Override + public Object execute(VirtualFrame frame) { + CompilerAsserts.neverPartOfCompilation(); + + // Call the definition method with the module as self - there's no return value + + final RubyModule module = (RubyModule) definingModule.execute(frame); + definitionMethod.executeMethod(frame).call(frame.pack(), module, null); + + return NilPlaceholder.INSTANCE; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/ReadClassVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/ReadClassVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@NodeInfo(shortName = "read-class-variable") +public class ReadClassVariableNode extends RubyNode { + + protected final String name; + @Child protected RubyNode module; + + public ReadClassVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode module) { + super(context, sourceSection); + this.name = name; + this.module = adoptChild(module); + } + + @Override + public Object execute(VirtualFrame frame) { + final RubyObject object = (RubyObject) module.execute(frame); + + RubyModule moduleObject; + + // TODO(CS): this cannot be right + + if (object instanceof RubyModule) { + moduleObject = (RubyModule) object; + } else { + moduleObject = object.getRubyClass(); + } + + final Object value = moduleObject.lookupClassVariable(name); + + if (value == null) { + // TODO(CS): is this right? + return NilPlaceholder.INSTANCE; + } + + return value; + } + + @Override + public Object isDefined(VirtualFrame frame) { + final RubyContext context = getContext(); + + final RubyObject object = (RubyObject) module.execute(frame); + + RubyModule moduleObject; + + if (object instanceof RubyModule) { + moduleObject = (RubyModule) object; + } else { + moduleObject = object.getRubyClass(); + } + + final Object value = moduleObject.lookupClassVariable(name); + + if (value == null) { + return NilPlaceholder.INSTANCE; + } else { + return context.makeString("class variable"); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/SelfNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/SelfNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +@NodeInfo(shortName = "self") +public class SelfNode extends RubyNode { + + public SelfNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + @Override + public Object execute(VirtualFrame frame) { + final Object self = frame.getArguments(RubyArguments.class).getSelf(); + assert RubyContext.shouldObjectBeVisible(self); + return self; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/SingletonClassNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/SingletonClassNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects; + +import java.math.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Reads the singleton (meta, eigen) class of an object. + */ +@NodeInfo(shortName = "singleton") +public class SingletonClassNode extends RubyNode { + + @Child protected RubyNode child; + + public SingletonClassNode(RubyContext context, SourceSection sourceSection, RubyNode child) { + super(context, sourceSection); + this.child = adoptChild(child); + } + + @Override + public Object execute(VirtualFrame frame) { + final Object childResult = child.execute(frame); + + final RubyContext context = getContext(); + + if (childResult instanceof NilPlaceholder) { + return context.getCoreLibrary().getNilClass(); + } else if (childResult instanceof BigInteger) { + // TODO(CS): this is problematic - do Bignums have singletons or not? + return context.getCoreLibrary().box(childResult).getSingletonClass(); + } else { + return ((RubyBasicObject) childResult).getSingletonClass(); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/WriteClassVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/WriteClassVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +@NodeInfo(shortName = "write-class-variable") +public class WriteClassVariableNode extends RubyNode { + + private final String name; + @Child protected RubyNode module; + @Child protected RubyNode rhs; + + public WriteClassVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode module, RubyNode rhs) { + super(context, sourceSection); + this.name = name; + this.module = adoptChild(module); + this.rhs = adoptChild(rhs); + } + + @Override + public Object execute(VirtualFrame frame) { + // TODO(CS): can module ever not evaluate to a RubyModule? + + final RubyModule moduleObject = (RubyModule) module.execute(frame); + + final Object rhsValue = rhs.execute(frame); + + moduleObject.setClassVariable(name, rhsValue); + + return rhsValue; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadFixnumInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadFixnumInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@NodeInfo(shortName = "@Fixnum") +public class ReadFixnumInstanceVariableNode extends ReadSpecializedInstanceVariableNode { + + private final FixnumStorageLocation storageLocation; + + public ReadFixnumInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, ObjectLayout objectLayout, FixnumStorageLocation storageLocation) { + super(context, sourceSection, name, receiver, objectLayout); + this.storageLocation = storageLocation; + } + + @Override + public int executeFixnum(VirtualFrame frame) throws UnexpectedResultException { + final RubyBasicObject receiverObject = (RubyBasicObject) receiver.execute(frame); + + final ObjectLayout receiverLayout = receiverObject.getObjectLayout(); + + final boolean condition = receiverLayout == objectLayout; + + if (condition) { + assert receiverLayout != null; + return storageLocation.readFixnum(receiverObject, condition); + } else { + CompilerDirectives.transferToInterpreter(); + replace(respecialize(receiverObject)); + throw new UnexpectedResultException(receiverObject.getInstanceVariable(name)); + } + } + + @Override + public Object execute(VirtualFrame frame) { + try { + return executeFixnum(frame); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadFloatInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadFloatInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@NodeInfo(shortName = "@Float") +public class ReadFloatInstanceVariableNode extends ReadSpecializedInstanceVariableNode { + + private final FloatStorageLocation storageLocation; + + public ReadFloatInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, ObjectLayout objectLayout, FloatStorageLocation storageLocation) { + super(context, sourceSection, name, receiver, objectLayout); + this.storageLocation = storageLocation; + } + + @Override + public double executeFloat(VirtualFrame frame) throws UnexpectedResultException { + final RubyBasicObject receiverObject = (RubyBasicObject) receiver.execute(frame); + + final ObjectLayout receiverLayout = receiverObject.getObjectLayout(); + + final boolean condition = receiverLayout == objectLayout; + + if (condition) { + assert receiverLayout != null; + return storageLocation.readFloat(receiverObject, condition); + } else { + CompilerDirectives.transferToInterpreter(); + replace(respecialize(receiverObject)); + throw new UnexpectedResultException(receiverObject.getInstanceVariable(name)); + } + } + + @Override + public Object execute(VirtualFrame frame) { + try { + return executeFloat(frame); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +public abstract class ReadInstanceVariableNode extends RubyNode implements ReadNode { + + protected final String name; + @Child protected RubyNode receiver; + + public ReadInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver) { + super(context, sourceSection); + this.name = name; + this.receiver = adoptChild(receiver); + } + + public ReadInstanceVariableNode respecialize(RubyBasicObject receiverObject) { + final StorageLocation storageLocation = receiverObject.getUpdatedObjectLayout().findStorageLocation(name); + + if (storageLocation == null) { + return new ReadMissingInstanceVariableNode(getSourceSection(), name, receiver, receiverObject.getObjectLayout(), getContext()); + } + + if (storageLocation instanceof FixnumStorageLocation) { + return new ReadFixnumInstanceVariableNode(getContext(), getSourceSection(), name, receiver, storageLocation.getObjectLayout(), (FixnumStorageLocation) storageLocation); + } else if (storageLocation instanceof FloatStorageLocation) { + return new ReadFloatInstanceVariableNode(getContext(), getSourceSection(), name, receiver, storageLocation.getObjectLayout(), (FloatStorageLocation) storageLocation); + } else { + return new ReadObjectInstanceVariableNode(getContext(), getSourceSection(), name, receiver, storageLocation.getObjectLayout(), (ObjectStorageLocation) storageLocation); + } + } + + public WriteInstanceVariableNode makeWriteNode(RubyNode rhs) { + return new UninitializedWriteInstanceVariableNode(getContext(), getEncapsulatingSourceSection(), name, (RubyNode) receiver.copy(), rhs); + } + + public String getName() { + return name; + } + + public RubyNode getReceiver() { + return receiver; + } + + @Override + public Object isDefined(VirtualFrame frame) { + final RubyContext context = getContext(); + + try { + final Object receiverObject = receiver.execute(frame); + final RubyBasicObject receiverRubyObject = context.getCoreLibrary().box(receiverObject); + + final ObjectLayout layout = receiverRubyObject.getObjectLayout(); + final StorageLocation storageLocation = layout.findStorageLocation(name); + + if (storageLocation.isSet(receiverRubyObject)) { + return context.makeString("instance-variable"); + } else { + return NilPlaceholder.INSTANCE; + } + } catch (Exception e) { + return NilPlaceholder.INSTANCE; + } + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadMissingInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadMissingInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@NodeInfo(shortName = "@missing") +public class ReadMissingInstanceVariableNode extends ReadSpecializedInstanceVariableNode { + + public ReadMissingInstanceVariableNode(SourceSection sourceSection, String name, RubyNode receiver, ObjectLayout objectLayout, RubyContext context) { + super(context, sourceSection, name, receiver, objectLayout); + } + + @Override + public Object execute(VirtualFrame frame) { + final RubyBasicObject receiverObject = (RubyBasicObject) receiver.execute(frame); + + if (!receiverObject.getObjectLayout().contains(objectLayout)) { + CompilerDirectives.transferToInterpreter(); + replace(respecialize(receiverObject)); + return receiverObject.getInstanceVariable(name); + } + + return NilPlaceholder.INSTANCE; + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadObjectInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadObjectInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@NodeInfo(shortName = "@Object") +public class ReadObjectInstanceVariableNode extends ReadSpecializedInstanceVariableNode { + + private final ObjectStorageLocation storageLocation; + + public ReadObjectInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, ObjectLayout objectLayout, ObjectStorageLocation storageLocation) { + super(context, sourceSection, name, receiver, objectLayout); + this.storageLocation = storageLocation; + } + + @Override + public Object execute(VirtualFrame frame) { + final RubyBasicObject receiverObject = (RubyBasicObject) receiver.execute(frame); + + final ObjectLayout receiverLayout = receiverObject.getObjectLayout(); + + final boolean condition = receiverLayout == objectLayout; + + if (condition) { + assert receiverLayout != null; + return storageLocation.read(receiverObject, condition); + } else { + CompilerDirectives.transferToInterpreter(); + replace(respecialize(receiverObject)); + return receiverObject.getInstanceVariable(name); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadSpecializedInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/ReadSpecializedInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +public abstract class ReadSpecializedInstanceVariableNode extends ReadInstanceVariableNode { + + protected final ObjectLayout objectLayout; + + public ReadSpecializedInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, ObjectLayout objectLayout) { + super(context, sourceSection, name, receiver); + this.objectLayout = objectLayout; + } + + protected void respecialize() { + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/UninitializedReadInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/UninitializedReadInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@NodeInfo(shortName = "@uninit") +public class UninitializedReadInstanceVariableNode extends ReadInstanceVariableNode { + + public UninitializedReadInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver) { + super(context, sourceSection, name, receiver); + } + + @Override + public Object execute(VirtualFrame frame) { + CompilerDirectives.transferToInterpreter(); + final RubyBasicObject receiverObject = (RubyBasicObject) receiver.execute(frame); + replace(respecialize(receiverObject)); + return receiverObject.getInstanceVariable(name); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/UninitializedWriteInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/UninitializedWriteInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@NodeInfo(shortName = "@uninit=") +public class UninitializedWriteInstanceVariableNode extends WriteInstanceVariableNode { + + public UninitializedWriteInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, RubyNode rhs) { + super(context, sourceSection, name, receiver, rhs); + } + + @Override + public Object execute(VirtualFrame frame) { + CompilerDirectives.transferToInterpreter(); + final RubyBasicObject receiverObject = (RubyBasicObject) receiver.execute(frame); + final Object value = rhs.execute(frame); + receiverObject.setInstanceVariable(name, value); + replace(respecialize(receiverObject)); + return value; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/WriteFixnumInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/WriteFixnumInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@NodeInfo(shortName = "@Fixnum=") +public class WriteFixnumInstanceVariableNode extends WriteSpecializedInstanceVariableNode { + + private final FixnumStorageLocation storageLocation; + + public WriteFixnumInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, RubyNode rhs, ObjectLayout objectLayout, + FixnumStorageLocation storageLocation) { + super(context, sourceSection, name, receiver, rhs, objectLayout); + this.storageLocation = storageLocation; + } + + @Override + public int executeFixnum(VirtualFrame frame) throws UnexpectedResultException { + final RubyBasicObject receiverObject = (RubyBasicObject) receiver.execute(frame); + + int value; + + try { + value = rhs.executeFixnum(frame); + } catch (UnexpectedResultException e) { + receiverObject.setInstanceVariable(name, e.getResult()); + replace(respecialize(receiverObject)); + throw e; + } + + final ObjectLayout receiverLayout = receiverObject.getObjectLayout(); + + if (receiverLayout != objectLayout) { + CompilerDirectives.transferToInterpreter(); + receiverObject.setInstanceVariable(name, value); + replace(respecialize(receiverObject)); + return value; + } + + assert receiverLayout != null; + + storageLocation.writeFixnum(receiverObject, value); + return value; + } + + @Override + public Object execute(VirtualFrame frame) { + try { + return executeFixnum(frame); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/WriteFloatInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/WriteFloatInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@NodeInfo(shortName = "@Float=") +public class WriteFloatInstanceVariableNode extends WriteSpecializedInstanceVariableNode { + + private final FloatStorageLocation storageLocation; + + public WriteFloatInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, RubyNode rhs, ObjectLayout objectLayout, + FloatStorageLocation storageLocation) { + super(context, sourceSection, name, receiver, rhs, objectLayout); + this.storageLocation = storageLocation; + } + + @Override + public double executeFloat(VirtualFrame frame) throws UnexpectedResultException { + final RubyBasicObject receiverObject = (RubyBasicObject) receiver.execute(frame); + + double value; + + try { + value = rhs.executeFloat(frame); + } catch (UnexpectedResultException e) { + receiverObject.setInstanceVariable(name, e.getResult()); + replace(respecialize(receiverObject)); + throw e; + } + + final ObjectLayout receiverLayout = receiverObject.getObjectLayout(); + + if (receiverLayout != objectLayout) { + CompilerDirectives.transferToInterpreter(); + receiverObject.setInstanceVariable(name, value); + replace(respecialize(receiverObject)); + return value; + } + + assert receiverLayout != null; + + storageLocation.writeFloat(receiverObject, value); + return value; + } + + @Override + public Object execute(VirtualFrame frame) { + try { + return executeFloat(frame); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/WriteInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/WriteInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +public abstract class WriteInstanceVariableNode extends RubyNode implements WriteNode { + + protected final String name; + @Child protected RubyNode receiver; + @Child protected RubyNode rhs; + + public WriteInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, RubyNode rhs) { + super(context, sourceSection); + this.name = name; + this.receiver = adoptChild(receiver); + this.rhs = adoptChild(rhs); + } + + public WriteInstanceVariableNode respecialize(RubyBasicObject receiverObject) { + StorageLocation storageLocation = receiverObject.getObjectLayout().findStorageLocation(name); + + if (storageLocation == null) { + throw new RuntimeException("Storage location should be found at this point"); + } + + if (storageLocation instanceof FixnumStorageLocation) { + return new WriteFixnumInstanceVariableNode(getContext(), getSourceSection(), name, receiver, rhs, storageLocation.getObjectLayout(), (FixnumStorageLocation) storageLocation); + } else if (storageLocation instanceof FloatStorageLocation) { + return new WriteFloatInstanceVariableNode(getContext(), getSourceSection(), name, receiver, rhs, storageLocation.getObjectLayout(), (FloatStorageLocation) storageLocation); + } else { + return new WriteObjectInstanceVariableNode(getContext(), getSourceSection(), name, receiver, rhs, storageLocation.getObjectLayout(), (ObjectStorageLocation) storageLocation); + } + } + + @Override + public ReadInstanceVariableNode makeReadNode() { + return new UninitializedReadInstanceVariableNode(getContext(), getEncapsulatingSourceSection(), name, (RubyNode) receiver.copy()); + } + + public String getName() { + return name; + } + + public RubyNode getReceiver() { + return receiver; + } + + public RubyNode getRHS() { + return rhs; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/WriteObjectInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/WriteObjectInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +@NodeInfo(shortName = "@Object=") +public class WriteObjectInstanceVariableNode extends WriteSpecializedInstanceVariableNode { + + private final ObjectStorageLocation storageLocation; + + public WriteObjectInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, RubyNode rhs, ObjectLayout objectLayout, + ObjectStorageLocation storageLocation) { + super(context, sourceSection, name, receiver, rhs, objectLayout); + this.storageLocation = storageLocation; + } + + @Override + public Object execute(VirtualFrame frame) { + final RubyBasicObject receiverObject = (RubyBasicObject) receiver.execute(frame); + final Object value = rhs.execute(frame); + + final ObjectLayout receiverLayout = receiverObject.getObjectLayout(); + + if (receiverLayout != objectLayout) { + CompilerDirectives.transferToInterpreter(); + receiverObject.setInstanceVariable(name, value); + replace(respecialize(receiverObject)); + return value; + } + + assert receiverLayout != null; + + storageLocation.write(receiverObject, value); + return value; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/WriteSpecializedInstanceVariableNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/objects/instancevariables/WriteSpecializedInstanceVariableNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.objects.instancevariables; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +public abstract class WriteSpecializedInstanceVariableNode extends WriteInstanceVariableNode { + + protected final ObjectLayout objectLayout; + + public WriteSpecializedInstanceVariableNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, RubyNode rhs, ObjectLayout objectLayout) { + super(context, sourceSection, name, receiver, rhs); + this.objectLayout = objectLayout; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/yield/GeneralYieldDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/yield/GeneralYieldDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.yield; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * A yield dispatch node that just calls {@link RubyProc#call} as normal. + */ +public class GeneralYieldDispatchNode extends YieldDispatchNode { + + public GeneralYieldDispatchNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + @Override + public Object dispatch(VirtualFrame frame, RubyProc block, Object[] argumentsObjects) { + return block.call(frame.pack(), argumentsObjects); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/yield/InlinedYieldDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/yield/InlinedYieldDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.yield; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Dispatch to a known method which has been inlined. + */ +public class InlinedYieldDispatchNode extends YieldDispatchNode { + + private final RubyRootNode pristineRootNode; + + private final FrameDescriptor frameDescriptor; + private final RubyRootNode rootNode; + + public InlinedYieldDispatchNode(RubyContext context, SourceSection sourceSection, InlinableMethodImplementation method) { + super(context, sourceSection); + pristineRootNode = method.getPristineRootNode(); + frameDescriptor = method.getFrameDescriptor(); + rootNode = method.getCloneOfPristineRootNode(); + } + + @Override + public Object dispatch(VirtualFrame frame, RubyProc block, Object[] argumentsObjects) { + /* + * We're inlining based on the root node used, checking that root node still matches, and + * then taking the materialized frame from the block we actually got. We can't look for + * RubyMethod or RubyProc, because they're allocated for each call passing a block. + */ + + if (!(block.getMethod().getImplementation() instanceof InlinableMethodImplementation) || + ((InlinableMethodImplementation) block.getMethod().getImplementation()).getPristineRootNode() != pristineRootNode) { + CompilerDirectives.transferToInterpreter(); + + // TODO(CS): at the moment we just go back to uninit, which may cause loops + + final UninitializedYieldDispatchNode dispatch = new UninitializedYieldDispatchNode(getContext(), getSourceSection()); + replace(dispatch); + return dispatch.dispatch(frame, block, argumentsObjects); + } + + final InlinableMethodImplementation implementation = (InlinableMethodImplementation) block.getMethod().getImplementation(); + + final RubyArguments arguments = new RubyArguments(implementation.getDeclarationFrame(), block.getSelf(), block.getBlock(), argumentsObjects); + final VirtualFrame inlinedFrame = Truffle.getRuntime().createVirtualFrame(frame.pack(), arguments, frameDescriptor); + return rootNode.execute(inlinedFrame); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/yield/UninitializedYieldDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/yield/UninitializedYieldDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.yield; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * An uninitialized node in the yield dispatch chain. + */ +public class UninitializedYieldDispatchNode extends YieldDispatchNode { + + public UninitializedYieldDispatchNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + @Override + public Object dispatch(VirtualFrame frame, RubyProc block, Object[] argumentsObjects) { + CompilerDirectives.transferToInterpreter(); + + final MethodImplementation implementation = block.getMethod().getImplementation(); + + if (implementation instanceof InlinableMethodImplementation && InlineHeuristic.shouldInlineYield((InlinableMethodImplementation) implementation)) { + final InlinedYieldDispatchNode dispatch = new InlinedYieldDispatchNode(getContext(), getSourceSection(), (InlinableMethodImplementation) implementation); + replace(dispatch); + return dispatch.dispatch(frame, block, argumentsObjects); + } else { + final GeneralYieldDispatchNode dispatch = new GeneralYieldDispatchNode(getContext(), getSourceSection()); + replace(dispatch); + return dispatch.dispatch(frame, block, argumentsObjects); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/yield/YieldDispatchNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/yield/YieldDispatchNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.yield; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * A node in the yield dispatch chain. + */ +public abstract class YieldDispatchNode extends Node { + + private final RubyContext context; + + public YieldDispatchNode(RubyContext context, SourceSection sourceSection) { + super(sourceSection); + + assert context != null; + assert sourceSection != null; + + this.context = context; + } + + public abstract Object dispatch(VirtualFrame frame, RubyProc block, Object[] argumentsObjects); + + public RubyContext getContext() { + return context; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/yield/YieldNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.nodes/src/com/oracle/truffle/ruby/nodes/yield/YieldNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.nodes.yield; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Yield to the current block. + */ +@NodeInfo(shortName = "yield") +public class YieldNode extends RubyNode { + + @Children protected final RubyNode[] arguments; + @Child protected YieldDispatchNode dispatch; + + public YieldNode(RubyContext context, SourceSection sourceSection, RubyNode[] arguments) { + super(context, sourceSection); + this.arguments = adoptChildren(arguments); + dispatch = adoptChild(new UninitializedYieldDispatchNode(getContext(), getSourceSection())); + } + + @ExplodeLoop + @Override + public final Object execute(VirtualFrame frame) { + final Object[] argumentsObjects = new Object[arguments.length]; + + for (int i = 0; i < arguments.length; i++) { + argumentsObjects[i] = arguments[i].execute(frame); + } + + final RubyProc block = frame.getArguments(RubyArguments.class).getBlock(); + + if (block == null) { + CompilerDirectives.transferToInterpreter(); + // TODO(CS): convert to the proper Ruby exception + throw new RuntimeException("No block to yield to"); + } + + return dispatch.dispatch(frame, block, argumentsObjects); + } + + @Override + public Object isDefined(VirtualFrame frame) { + final RubyArguments args = frame.getArguments(RubyArguments.class); + + if (args.getBlock() == null) { + return NilPlaceholder.INSTANCE; + } else { + return getContext().makeString("yield"); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/DeadNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/DeadNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.parser; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Dead nodes are removed wherever they are found during translation. They fill in for some missing + * nodes when we're processing the AST. + */ +public class DeadNode extends RubyNode { + + public DeadNode(RubyContext context, SourceSection sourceSection) { + super(context, sourceSection); + } + + @Override + public Object execute(VirtualFrame frame) { + throw new UnsupportedOperationException("Dead nodes should have been pruned before execution starts"); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/JRubyParser.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/JRubyParser.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.parser; + +import java.io.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.control.*; +import com.oracle.truffle.ruby.nodes.literal.*; +import com.oracle.truffle.ruby.nodes.methods.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.debug.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +public class JRubyParser implements RubyParser { + + private long nextReturnID = 0; + + @Override + public RubyParserResult parse(RubyContext context, Source source, ParserContext parserContext, MaterializedFrame parentFrame) { + // Set up the JRuby parser + + final org.jrubyparser.Parser parser = new org.jrubyparser.Parser(); + + org.jrubyparser.CompatVersion parserVersion = null; + + switch (context.getConfiguration().getRubyVersion()) { + case RUBY_18: + parserVersion = org.jrubyparser.CompatVersion.RUBY1_8; + break; + case RUBY_19: + parserVersion = org.jrubyparser.CompatVersion.RUBY1_9; + break; + case RUBY_20: + parserVersion = org.jrubyparser.CompatVersion.RUBY2_0; + break; + case RUBY_21: + parserVersion = org.jrubyparser.CompatVersion.RUBY2_0; + break; + } + + // TODO(cs) should this get a new unique method identifier or not? + final TranslatorEnvironment environment = new TranslatorEnvironment(context, environmentForFrame(context, parentFrame), this, allocateReturnID(), true, true, new UniqueMethodIdentifier()); + + // All parsing contexts have a visibility slot at their top level + + environment.addMethodDeclarationSlots(); + + final org.jrubyparser.LocalStaticScope staticScope = new org.jrubyparser.LocalStaticScope(null); + + if (parentFrame != null) { + /* + * Note that jruby-parser will be mistaken about how deep the existing variables are, + * but that doesn't matter as we look them up ourselves after being told their in some + * parent scope. + */ + + MaterializedFrame frame = parentFrame; + + while (frame != null) { + for (FrameSlot slot : frame.getFrameDescriptor().getSlots()) { + if (slot.getIdentifier() instanceof String) { + final String name = (String) slot.getIdentifier(); + if (staticScope.exists(name) == -1) { + staticScope.assign(null, name, null); + } + } + } + + frame = frame.getArguments(RubyArguments.class).getDeclarationFrame(); + } + } + + final org.jrubyparser.parser.ParserConfiguration parserConfiguration = new org.jrubyparser.parser.ParserConfiguration(0, parserVersion, staticScope); + + // Parse to the JRuby AST + + org.jrubyparser.ast.RootNode node; + + try { + node = (org.jrubyparser.ast.RootNode) parser.parse(source.getName(), new StringReader(source.getCode()), parserConfiguration); + } catch (UnsupportedOperationException | org.jrubyparser.lexer.SyntaxException e) { + String message = e.getMessage(); + + if (message == null) { + message = "(no message)"; + } + + throw new RaiseException(new RubyException(context.getCoreLibrary().getSyntaxErrorClass(), message)); + } + + if (context.getConfiguration().getPrintParseTree()) { + System.err.println(node); + } + + // Translate to Ruby Truffle nodes + + final Translator translator; + + if (parserContext == RubyParser.ParserContext.MODULE) { + translator = new ModuleTranslator(context, null, environment, source); + } else { + translator = new Translator(context, null, environment, source); + } + + RubyNode truffleNode; + + final RubyDebugManager debugManager = context.getDebugManager(); + try { + if (debugManager != null) { + debugManager.notifyStartLoading(source); + } + + if (node.getBody() == null) { + truffleNode = new NilNode(context, null); + } else { + truffleNode = (RubyNode) node.getBody().accept(translator); + } + + // Load flip-flop states + + if (environment.getFlipFlopStates().size() > 0) { + truffleNode = new SequenceNode(context, truffleNode.getSourceSection(), translator.initFlipFlopStates(truffleNode.getSourceSection()), truffleNode); + } + + // Catch next + + truffleNode = new CatchNextNode(context, truffleNode.getSourceSection(), truffleNode); + + // Catch return + + truffleNode = new CatchReturnAsErrorNode(context, truffleNode.getSourceSection(), truffleNode); + + // Shell result + + if (parserContext == RubyParser.ParserContext.SHELL) { + truffleNode = new ShellResultNode(context, truffleNode.getSourceSection(), truffleNode); + } + + // Root Node + + String indicativeName; + + switch (parserContext) { + case TOP_LEVEL: + indicativeName = "(main)"; + break; + case SHELL: + indicativeName = "(shell)"; + break; + case MODULE: + indicativeName = "(module)"; + break; + default: + throw new UnsupportedOperationException(); + } + + final RootNode root = new RubyRootNode(truffleNode.getSourceSection(), indicativeName, truffleNode); + + // Return the root and the frame descriptor + + return new RubyParserResult(root, environment.getFrameDescriptor()); + } finally { + if (debugManager != null) { + debugManager.notifyFinishedLoading(source); + } + } + } + + public long allocateReturnID() { + if (nextReturnID == Long.MAX_VALUE) { + throw new RuntimeException("Return IDs exhausted"); + } + + final long allocated = nextReturnID; + nextReturnID++; + return allocated; + } + + private TranslatorEnvironment environmentForFrame(RubyContext context, MaterializedFrame frame) { + if (frame == null) { + return null; + } else { + final MaterializedFrame parent = frame.getArguments(RubyArguments.class).getDeclarationFrame(); + return new TranslatorEnvironment(context, environmentForFrame(context, parent), frame.getFrameDescriptor(), this, allocateReturnID(), true, true, new UniqueMethodIdentifier()); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/MethodTranslator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/MethodTranslator.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.parser; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.nodes.control.*; +import com.oracle.truffle.ruby.nodes.literal.*; +import com.oracle.truffle.ruby.nodes.methods.*; +import com.oracle.truffle.ruby.nodes.methods.arguments.*; +import com.oracle.truffle.ruby.nodes.methods.locals.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +class MethodTranslator extends Translator { + + private boolean isBlock; + + public MethodTranslator(RubyContext context, Translator parent, TranslatorEnvironment environment, boolean isBlock, Source source) { + super(context, parent, environment, source); + this.isBlock = isBlock; + } + + public MethodDefinitionNode compileFunctionNode(SourceSection sourceSection, String methodName, org.jrubyparser.ast.ArgsNode argsNode, org.jrubyparser.ast.Node bodyNode) { + environment.setMethodName(methodName); + + final Arity arity = findParameters(argsNode); + + RubyNode body; + + if (bodyNode != null) { + body = (RubyNode) bodyNode.accept(this); + } else { + body = new NilNode(context, sourceSection); + } + + body = loadArgumentsIntoLocals(arity, body); + + if (environment.getFlipFlopStates().size() > 0) { + body = new SequenceNode(context, sourceSection, initFlipFlopStates(sourceSection), body); + } + + if (isBlock) { + body = new CatchNextNode(context, sourceSection, body); + } else { + body = new CatchReturnNode(context, sourceSection, body, environment.getReturnID()); + body = new CatchNextNode(context, sourceSection, body); + } + + final RubyRootNode pristineRootNode = new RubyRootNode(sourceSection, methodName, body); + + final CallTarget callTarget = Truffle.getRuntime().createCallTarget(NodeUtil.cloneNode(pristineRootNode), environment.getFrameDescriptor()); + + if (isBlock) { + return new BlockDefinitionNode(context, sourceSection, methodName, environment.getUniqueMethodIdentifier(), environment.getFrameDescriptor(), environment.needsDeclarationFrame(), + pristineRootNode, callTarget); + } else { + return new MethodDefinitionNode(context, sourceSection, methodName, environment.getUniqueMethodIdentifier(), environment.getFrameDescriptor(), environment.needsDeclarationFrame(), + pristineRootNode, callTarget); + } + } + + private RubyNode loadArgumentsIntoLocals(Arity arity, RubyNode body) { + final SourceSection sourceSection = body.getEncapsulatingSourceSection(); + + final List loadIndividualArgumentsNodes = new ArrayList<>(); + + if (!isBlock) { + loadIndividualArgumentsNodes.add(new CheckArityNode(context, sourceSection, arity)); + } + + final int preCount = environment.getPreParameters().size(); + final int postCount = environment.getPostParameters().size(); + + for (int n = 0; n < environment.getPreParameters().size(); n++) { + final FrameSlot param = environment.getPreParameters().get(n); + + // ReadPre reads from the start of the arguments array + + final ReadPreArgumentNode readArgumentNode = new ReadPreArgumentNode(context, sourceSection, n, false); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); + + loadIndividualArgumentsNodes.add(writeLocal); + } + + for (int n = 0; n < environment.getOptionalParameters().size(); n++) { + final FrameSlot param = environment.getOptionalParameters().get(n); + final RubyNode defaultValue = environment.getOptionalParametersDefaultValues().get(param); + + /* + * ReadOptional reads from the start of the arguments array, as long as it is long + * enough, else uses the default value (which may use locals with arguments just loaded, + * either from pre or preceding optionals). + */ + + final ReadOptionalArgumentNode readArgumentNode = new ReadOptionalArgumentNode(context, body.getEncapsulatingSourceSection(), preCount + n, preCount + postCount + n + 1, + (RubyNode) defaultValue.copy()); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); + + loadIndividualArgumentsNodes.add(writeLocal); + } + + for (int n = 0; n < environment.getPostParameters().size(); n++) { + final FrameSlot param = environment.getPostParameters().get(n); + + // ReadPost reads from the end of the arguments array + + final ReadPostArgumentNode readArgumentNode = new ReadPostArgumentNode(context, sourceSection, postCount - n - 1); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); + + loadIndividualArgumentsNodes.add(writeLocal); + } + + if (environment.getRestParameter() != null) { + /* + * TODO(cs): this assumes there are no optionals and therefore also no posts, which may + * not be a valid assumption. + */ + + if (postCount != 0) { + context.implementationMessage("post arguments as well as a rest argument - they will conflict"); + } + + final ReadRestArgumentNode readArgumentNode = new ReadRestArgumentNode(context, sourceSection, preCount); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, environment.getRestParameter(), readArgumentNode); + + loadIndividualArgumentsNodes.add(writeLocal); + } + + if (environment.getBlockParameter() != null) { + final FrameSlot param = environment.getBlockParameter(); + + final ReadBlockArgumentNode readArgumentNode = new ReadBlockArgumentNode(context, sourceSection, false); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); + + loadIndividualArgumentsNodes.add(writeLocal); + } + + final RubyNode loadIndividualArguments = new SequenceNode(context, sourceSection, loadIndividualArgumentsNodes.toArray(new RubyNode[loadIndividualArgumentsNodes.size()])); + + final RubyNode noSwitch = new SequenceNode(context, body.getSourceSection(), loadIndividualArguments, body); + + if (!isBlock) { + return noSwitch; + } + + /* + * See the test testBlockArgumentsDestructure for a motivation for this. See + * BlockDestructureSwitchNode for how it works. + */ + + if (preCount + postCount == 1 && environment.getOptionalParameters().size() == 0) { + return noSwitch; + } + + final List destructureLoadArgumentsNodes = new ArrayList<>(); + + for (int n = 0; n < environment.getPreParameters().size(); n++) { + final FrameSlot param = environment.getPreParameters().get(n); + + final ReadDestructureArgumentNode readArgumentNode = new ReadDestructureArgumentNode(context, sourceSection, n); + + final WriteLocalVariableNode writeLocal = WriteLocalVariableNodeFactory.create(context, sourceSection, param, readArgumentNode); + + destructureLoadArgumentsNodes.add(writeLocal); + } + + final RubyNode destructureLoadArguments = new SequenceNode(context, body.getSourceSection(), destructureLoadArgumentsNodes.toArray(new RubyNode[destructureLoadArgumentsNodes.size()])); + + return new BlockDestructureSwitchNode(context, body.getEncapsulatingSourceSection(), loadIndividualArguments, destructureLoadArguments, body); + + } + + private Arity findParameters(org.jrubyparser.ast.ArgsNode args) { + if (args == null) { + return Arity.NO_ARGS; + } + + final SourceSection sourceSection = translate(args.getPosition()); + + if (args.getPre() != null) { + for (org.jrubyparser.ast.Node arg : args.getPre().childNodes()) { + if (arg instanceof org.jrubyparser.ast.ArgumentNode) { + final org.jrubyparser.ast.ArgumentNode argNode = (org.jrubyparser.ast.ArgumentNode) arg; + environment.getPreParameters().add(environment.declareVar(argNode.getName())); + } else if (arg instanceof org.jrubyparser.ast.MultipleAsgnNode) { + /* + * TODO(cs): I don't know how to handle this yet, so I just do my best to get + * the names out and define them so the rest of the parser succeeds. + */ + + context.implementationMessage("only extracting names from multiple assignment in arguments"); + + final org.jrubyparser.ast.MultipleAsgnNode multAsgn = (org.jrubyparser.ast.MultipleAsgnNode) arg; + + final List names = new ArrayList<>(); + getNamesFromMultipleAssignment(multAsgn, names); + + for (String name : names) { + environment.getPreParameters().add(environment.declareVar(name)); + } + } + } + } + + // The JRuby parser expresses optional arguments as a block of local assignments + + /* + * Note that default values for optional params can refer to the actual value of previous + * args, so be careful with the order of args here and in loadArgumentsIntoLocals. + */ + + if (args.getOptional() != null) { + for (org.jrubyparser.ast.Node arg : args.getOptional().childNodes()) { + final org.jrubyparser.ast.OptArgNode optArgNode = (org.jrubyparser.ast.OptArgNode) arg; + + String name; + org.jrubyparser.ast.Node valueNode; + + if (optArgNode.getValue() instanceof org.jrubyparser.ast.LocalAsgnNode) { + final org.jrubyparser.ast.LocalAsgnNode optLocalAsgn = (org.jrubyparser.ast.LocalAsgnNode) optArgNode.getValue(); + name = optLocalAsgn.getName(); + valueNode = optLocalAsgn.getValue(); + } else if (optArgNode.getValue() instanceof org.jrubyparser.ast.DAsgnNode) { + final org.jrubyparser.ast.DAsgnNode optLocalAsgn = (org.jrubyparser.ast.DAsgnNode) optArgNode.getValue(); + name = optLocalAsgn.getName(); + valueNode = optLocalAsgn.getValue(); + } else { + throw new UnsupportedOperationException(optArgNode.getValue().getClass().getName()); + } + + RubyNode paramDefaultValue; + + if (valueNode == null) { + paramDefaultValue = new NilNode(context, sourceSection); + } else { + paramDefaultValue = (RubyNode) valueNode.accept(this); + } + + final FrameSlot frameSlot = environment.declareVar(name); + environment.getOptionalParameters().add(frameSlot); + environment.getOptionalParametersDefaultValues().put(frameSlot, paramDefaultValue); + } + } + + if (args.getPost() != null) { + for (org.jrubyparser.ast.Node arg : args.getPost().childNodes()) { + final org.jrubyparser.ast.ArgumentNode argNode = (org.jrubyparser.ast.ArgumentNode) arg; + environment.getPostParameters().add(environment.declareVar(argNode.getName())); + } + } + + if (args.getRest() != null) { + final org.jrubyparser.ast.RestArgNode rest = (org.jrubyparser.ast.RestArgNode) args.getRest(); + environment.setRestParameter(environment.declareVar(rest.getName())); + } + + if (args.getBlock() != null) { + final org.jrubyparser.ast.BlockArgNode blockArgNode = args.getBlock(); + final FrameSlot frameSlot = environment.declareVar(blockArgNode.getName()); + environment.setBlockParameter(frameSlot); + } + + final int minimum = environment.getPreParameters().size() + environment.getPostParameters().size(); + + int maximum = minimum + environment.getOptionalParameters().size(); + + if (args.getRest() != null) { + maximum = Arity.NO_MAXIMUM; + } + + return new Arity(minimum, maximum); + } + + private void getNamesFromMultipleAssignment(org.jrubyparser.ast.MultipleAsgnNode multAsgn, List names) { + for (org.jrubyparser.ast.Node a : multAsgn.getPre().childNodes()) { + if (a instanceof org.jrubyparser.ast.DAsgnNode) { + names.add(((org.jrubyparser.ast.DAsgnNode) a).getName()); + } else if (a instanceof org.jrubyparser.ast.MultipleAsgnNode) { + getNamesFromMultipleAssignment((org.jrubyparser.ast.MultipleAsgnNode) a, names); + } else if (a instanceof org.jrubyparser.ast.LocalAsgnNode) { + names.add(((org.jrubyparser.ast.LocalAsgnNode) a).getName()); + } else { + throw new RuntimeException(a.getClass().getName()); + } + } + } + + @Override + public Object visitSuperNode(org.jrubyparser.ast.SuperNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final ArgumentsAndBlockTranslation argumentsAndBlock = translateArgumentsAndBlock(sourceSection, node.getIter(), node.getArgs(), null); + + final String name = environment.getMethodName(); + + return new GeneralSuperCallNode(context, sourceSection, name, argumentsAndBlock.getBlock(), argumentsAndBlock.getArguments(), argumentsAndBlock.isSplatted()); + } + + @Override + public Object visitZSuperNode(org.jrubyparser.ast.ZSuperNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final ArgumentsAndBlockTranslation argumentsAndBlock = translateArgumentsAndBlock(sourceSection, node.getIter(), null, null); + + final String name = environment.getMethodName(); + + return new GeneralSuperCallNode(context, sourceSection, name, argumentsAndBlock.getBlock(), argumentsAndBlock.getArguments(), argumentsAndBlock.isSplatted()); + } + + @Override + protected FlipFlopStateNode createFlipFlopState(SourceSection sourceSection, int depth) { + if (isBlock) { + environment.setNeedsDeclarationFrame(); + return parent.createFlipFlopState(sourceSection, depth + 1); + } else { + return super.createFlipFlopState(sourceSection, depth); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/ModuleTranslator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/ModuleTranslator.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.parser; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.constants.*; +import com.oracle.truffle.ruby.nodes.control.*; +import com.oracle.truffle.ruby.nodes.literal.*; +import com.oracle.truffle.ruby.nodes.methods.*; +import com.oracle.truffle.ruby.nodes.objects.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * Translates module and class nodes. + *

+ * In Ruby, a module or class definition is somewhat like a method. It has a local scope and a value + * for self, which is the module or class object that is being defined. Therefore for a module or + * class definition we translate into a special method. We run that method with self set to be the + * newly allocated module or class. We then have to treat at least method and constant definitions + * differently. + */ +class ModuleTranslator extends Translator { + + public ModuleTranslator(RubyContext context, Translator parent, TranslatorEnvironment environment, Source source) { + super(context, parent, environment, source); + } + + public MethodDefinitionNode compileClassNode(org.jrubyparser.SourcePosition sourcePosition, String name, org.jrubyparser.ast.Node bodyNode) { + final SourceSection sourceSection = translate(sourcePosition); + + environment.addMethodDeclarationSlots(); + + final String methodName = "(" + name + "-def" + ")"; + environment.setMethodName(methodName); + + RubyNode body; + + if (bodyNode != null) { + body = (RubyNode) bodyNode.accept(this); + } else { + body = new NilNode(context, sourceSection); + } + + if (environment.getFlipFlopStates().size() > 0) { + body = new SequenceNode(context, sourceSection, initFlipFlopStates(sourceSection), body); + } + + body = new CatchReturnNode(context, sourceSection, body, environment.getReturnID()); + + final RubyRootNode pristineRootNode = new RubyRootNode(sourceSection, methodName, body); + + final CallTarget callTarget = Truffle.getRuntime().createCallTarget(NodeUtil.cloneNode(pristineRootNode), environment.getFrameDescriptor()); + + return new MethodDefinitionNode(context, sourceSection, methodName, environment.getUniqueMethodIdentifier(), environment.getFrameDescriptor(), environment.needsDeclarationFrame(), + pristineRootNode, callTarget); + } + + @Override + public Object visitConstDeclNode(org.jrubyparser.ast.ConstDeclNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final SelfNode selfNode = new SelfNode(context, sourceSection); + + return new WriteConstantNode(context, sourceSection, node.getName(), selfNode, (RubyNode) node.getValue().accept(this)); + } + + @Override + public Object visitConstNode(org.jrubyparser.ast.ConstNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final SelfNode selfNode = new SelfNode(context, sourceSection); + + return new UninitializedReadConstantNode(context, sourceSection, node.getName(), selfNode); + } + + @Override + public Object visitDefnNode(org.jrubyparser.ast.DefnNode node) { + /* + * The top-level translator puts methods into Object. We put ours into the self, which is + * the class being defined. + */ + + final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getParser().allocateReturnID(), true, true, + new UniqueMethodIdentifier()); + final MethodTranslator methodCompiler = new MethodTranslator(context, this, newEnvironment, false, source); + final MethodDefinitionNode functionExprNode = methodCompiler.compileFunctionNode(translate(node.getPosition()), node.getName(), node.getArgs(), node.getBody()); + + final SourceSection sourceSection = translate(node.getPosition()); + return new AddMethodNode(context, sourceSection, new SelfNode(context, sourceSection), functionExprNode); + } + + @Override + public Object visitClassVarAsgnNode(org.jrubyparser.ast.ClassVarAsgnNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final RubyNode receiver = new SelfNode(context, sourceSection); + + final RubyNode rhs = (RubyNode) node.getValue().accept(this); + + return new WriteClassVariableNode(context, sourceSection, node.getName(), receiver, rhs); + } + + @Override + public Object visitClassVarNode(org.jrubyparser.ast.ClassVarNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + return new ReadClassVariableNode(context, sourceSection, node.getName(), new SelfNode(context, sourceSection)); + } + + @Override + public Object visitAliasNode(org.jrubyparser.ast.AliasNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final org.jrubyparser.ast.LiteralNode oldName = (org.jrubyparser.ast.LiteralNode) node.getOldName(); + final org.jrubyparser.ast.LiteralNode newName = (org.jrubyparser.ast.LiteralNode) node.getNewName(); + + return new AliasNode(context, sourceSection, new SelfNode(context, sourceSection), newName.getName(), oldName.getName()); + } + + @Override + protected RubyNode getModuleToDefineModulesIn(SourceSection sourceSection) { + return new SelfNode(context, sourceSection); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/RubyFrameTypeConversion.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/RubyFrameTypeConversion.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.parser; + +import com.oracle.truffle.api.impl.*; +import com.oracle.truffle.ruby.runtime.*; + +public final class RubyFrameTypeConversion extends DefaultFrameTypeConversion { + + private static final RubyFrameTypeConversion INSTANCE = new RubyFrameTypeConversion(); + + private RubyFrameTypeConversion() { + } + + @Override + public Object getDefaultValue() { + return NilPlaceholder.INSTANCE; + } + + public static RubyFrameTypeConversion getInstance() { + return INSTANCE; + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/Translator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/Translator.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,2093 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.parser; + +import java.math.*; +import java.util.*; +import java.util.regex.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.impl.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.call.*; +import com.oracle.truffle.ruby.nodes.cast.*; +import com.oracle.truffle.ruby.nodes.constants.*; +import com.oracle.truffle.ruby.nodes.control.*; +import com.oracle.truffle.ruby.nodes.core.*; +import com.oracle.truffle.ruby.nodes.debug.*; +import com.oracle.truffle.ruby.nodes.literal.*; +import com.oracle.truffle.ruby.nodes.literal.array.*; +import com.oracle.truffle.ruby.nodes.methods.*; +import com.oracle.truffle.ruby.nodes.methods.locals.*; +import com.oracle.truffle.ruby.nodes.objects.*; +import com.oracle.truffle.ruby.nodes.objects.instancevariables.*; +import com.oracle.truffle.ruby.nodes.yield.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.range.*; +import com.oracle.truffle.ruby.runtime.debug.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * A JRuby parser node visitor which translates JRuby AST nodes into our Ruby nodes, implementing a + * Ruby parser. Therefore there is some namespace contention here! We make all references to JRuby + * explicit. This is the only place though - it doesn't leak out elsewhere. + */ +public class Translator implements org.jrubyparser.NodeVisitor { + + protected final Translator parent; + + protected final RubyContext context; + protected final TranslatorEnvironment environment; + protected final Source source; + + private boolean translatingForStatement = false; + + private static final Map nodeDefinedNames = new HashMap<>(); + + static { + nodeDefinedNames.put(org.jrubyparser.ast.SelfNode.class, "self"); + nodeDefinedNames.put(org.jrubyparser.ast.NilNode.class, "nil"); + nodeDefinedNames.put(org.jrubyparser.ast.TrueNode.class, "true"); + nodeDefinedNames.put(org.jrubyparser.ast.FalseNode.class, "false"); + nodeDefinedNames.put(org.jrubyparser.ast.LocalAsgnNode.class, "assignment"); + nodeDefinedNames.put(org.jrubyparser.ast.DAsgnNode.class, "assignment"); + nodeDefinedNames.put(org.jrubyparser.ast.GlobalAsgnNode.class, "assignment"); + nodeDefinedNames.put(org.jrubyparser.ast.InstAsgnNode.class, "assignment"); + nodeDefinedNames.put(org.jrubyparser.ast.ClassVarAsgnNode.class, "assignment"); + nodeDefinedNames.put(org.jrubyparser.ast.OpAsgnAndNode.class, "assignment"); + nodeDefinedNames.put(org.jrubyparser.ast.OpAsgnOrNode.class, "assignment"); + nodeDefinedNames.put(org.jrubyparser.ast.OpAsgnNode.class, "assignment"); + nodeDefinedNames.put(org.jrubyparser.ast.OpElementAsgnNode.class, "assignment"); + nodeDefinedNames.put(org.jrubyparser.ast.MultipleAsgnNode.class, "assignment"); + nodeDefinedNames.put(org.jrubyparser.ast.GlobalVarNode.class, "global-variable"); + nodeDefinedNames.put(org.jrubyparser.ast.StrNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.DStrNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.FixnumNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.BignumNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.FloatNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.RegexpNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.DRegexpNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.ArrayNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.HashNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.SymbolNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.DotNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.NotNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.AndNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.OrNode.class, "expression"); + nodeDefinedNames.put(org.jrubyparser.ast.LocalVarNode.class, "local-variable"); + nodeDefinedNames.put(org.jrubyparser.ast.DVarNode.class, "local-variable"); + } + + /** + * Global variables which in common usage have frame local semantics. + */ + public static final Set FRAME_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$_")); + + public Translator(RubyContext context, Translator parent, TranslatorEnvironment environment, Source source) { + this.context = context; + this.parent = parent; + this.environment = environment; + this.source = source; + } + + @Override + public Object visitAliasNode(org.jrubyparser.ast.AliasNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final org.jrubyparser.ast.LiteralNode oldName = (org.jrubyparser.ast.LiteralNode) node.getOldName(); + final org.jrubyparser.ast.LiteralNode newName = (org.jrubyparser.ast.LiteralNode) node.getNewName(); + + final ClassNode classNode = new ClassNode(context, sourceSection, new SelfNode(context, sourceSection)); + + return new AliasNode(context, sourceSection, classNode, newName.getName(), oldName.getName()); + } + + @Override + public Object visitAndNode(org.jrubyparser.ast.AndNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + RubyNode x; + + if (node.getFirst() == null) { + x = new NilNode(context, sourceSection); + } else { + x = (RubyNode) node.getFirst().accept(this); + } + + RubyNode y; + + if (node.getSecond() == null) { + y = new NilNode(context, sourceSection); + } else { + y = (RubyNode) node.getSecond().accept(this); + } + + return AndNodeFactory.create(context, sourceSection, x, y); + } + + @Override + public Object visitArgsCatNode(org.jrubyparser.ast.ArgsCatNode node) { + final List nodes = new ArrayList<>(); + collectArgsCatNodes(nodes, node); + + final List translatedNodes = new ArrayList<>(); + + for (org.jrubyparser.ast.Node catNode : nodes) { + translatedNodes.add((RubyNode) catNode.accept(this)); + } + + return new ArrayConcatNode(context, translate(node.getPosition()), translatedNodes.toArray(new RubyNode[translatedNodes.size()])); + } + + // ArgsCatNodes can be nested - this collects them into a flat list of children + private void collectArgsCatNodes(List nodes, org.jrubyparser.ast.ArgsCatNode node) { + if (node.getFirst() instanceof org.jrubyparser.ast.ArgsCatNode) { + collectArgsCatNodes(nodes, (org.jrubyparser.ast.ArgsCatNode) node.getFirst()); + } else { + nodes.add(node.getFirst()); + } + + if (node.getSecond() instanceof org.jrubyparser.ast.ArgsCatNode) { + collectArgsCatNodes(nodes, (org.jrubyparser.ast.ArgsCatNode) node.getSecond()); + } else { + nodes.add(node.getSecond()); + } + } + + @Override + public Object visitArgsNode(org.jrubyparser.ast.ArgsNode node) { + return unimplemented(node); + } + + @Override + public Object visitArgsPushNode(org.jrubyparser.ast.ArgsPushNode node) { + return new ArrayPushNode(context, translate(node.getPosition()), (RubyNode) node.getFirstNode().accept(this), (RubyNode) node.getSecondNode().accept(this)); + } + + @Override + public Object visitArrayNode(org.jrubyparser.ast.ArrayNode node) { + final List values = node.childNodes(); + + final RubyNode[] translatedValues = new RubyNode[values.size()]; + + for (int n = 0; n < values.size(); n++) { + translatedValues[n] = (RubyNode) values.get(n).accept(this); + } + + return new UninitialisedArrayLiteralNode(context, translate(node.getPosition()), translatedValues); + } + + @Override + public Object visitAttrAssignNode(org.jrubyparser.ast.AttrAssignNode node) { + return visitAttrAssignNodeExtraArgument(node, null); + } + + /** + * See translateDummyAssignment to understand what this is for. + */ + public RubyNode visitAttrAssignNodeExtraArgument(org.jrubyparser.ast.AttrAssignNode node, RubyNode extraArgument) { + final org.jrubyparser.ast.CallNode callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), node.getReceiver(), node.getName(), node.getArgs()); + return visitCallNodeExtraArgument(callNode, extraArgument); + } + + @Override + public Object visitBackRefNode(org.jrubyparser.ast.BackRefNode node) { + return unimplemented(node); + } + + @Override + public Object visitBeginNode(org.jrubyparser.ast.BeginNode node) { + return node.getBody().accept(this); + } + + @Override + public Object visitBignumNode(org.jrubyparser.ast.BignumNode node) { + return new BignumLiteralNode(context, translate(node.getPosition()), node.getValue()); + } + + @Override + public Object visitBlockArg18Node(org.jrubyparser.ast.BlockArg18Node node) { + return unimplemented(node); + } + + @Override + public Object visitBlockArgNode(org.jrubyparser.ast.BlockArgNode node) { + return unimplemented(node); + } + + @Override + public Object visitBlockNode(org.jrubyparser.ast.BlockNode node) { + final List children = node.childNodes(); + + final List translatedChildren = new ArrayList<>(); + + for (int n = 0; n < children.size(); n++) { + final RubyNode translatedChild = (RubyNode) children.get(n).accept(this); + + if (!(translatedChild instanceof DeadNode)) { + translatedChildren.add(translatedChild); + } + } + + if (translatedChildren.size() == 1) { + return translatedChildren.get(0); + } else { + return new SequenceNode(context, translate(node.getPosition()), translatedChildren.toArray(new RubyNode[translatedChildren.size()])); + } + } + + @Override + public Object visitBlockPassNode(org.jrubyparser.ast.BlockPassNode node) { + return unimplemented(node); + } + + @Override + public Object visitBreakNode(org.jrubyparser.ast.BreakNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + RubyNode resultNode; + + if (node.getValueNode() == null) { + resultNode = new NilNode(context, sourceSection); + } else { + resultNode = (RubyNode) node.getValueNode().accept(this); + } + + return new BreakNode(context, sourceSection, resultNode); + } + + @Override + public Object visitCallNode(org.jrubyparser.ast.CallNode node) { + return visitCallNodeExtraArgument(node, null); + } + + /** + * See translateDummyAssignment to understand what this is for. + */ + public RubyNode visitCallNodeExtraArgument(org.jrubyparser.ast.CallNode node, RubyNode extraArgument) { + final SourceSection sourceSection = translate(node.getPosition()); + + final RubyNode receiverTranslated = (RubyNode) node.getReceiver().accept(this); + + org.jrubyparser.ast.Node args = node.getArgs(); + org.jrubyparser.ast.Node block = node.getIter(); + + if (block == null && args instanceof org.jrubyparser.ast.IterNode) { + final org.jrubyparser.ast.Node temp = args; + args = block; + block = temp; + } + + final ArgumentsAndBlockTranslation argumentsAndBlock = translateArgumentsAndBlock(sourceSection, block, args, extraArgument); + + return new CallNode(context, sourceSection, node.getName(), receiverTranslated, argumentsAndBlock.getBlock(), argumentsAndBlock.isSplatted(), argumentsAndBlock.getArguments()); + } + + protected class ArgumentsAndBlockTranslation { + + private final RubyNode block; + private final RubyNode[] arguments; + private final boolean isSplatted; + + public ArgumentsAndBlockTranslation(RubyNode block, RubyNode[] arguments, boolean isSplatted) { + super(); + this.block = block; + this.arguments = arguments; + this.isSplatted = isSplatted; + } + + public RubyNode getBlock() { + return block; + } + + public RubyNode[] getArguments() { + return arguments; + } + + public boolean isSplatted() { + return isSplatted; + } + + } + + protected ArgumentsAndBlockTranslation translateArgumentsAndBlock(SourceSection sourceSection, org.jrubyparser.ast.Node iterNode, org.jrubyparser.ast.Node argsNode, RubyNode extraArgument) { + assert !(argsNode instanceof org.jrubyparser.ast.IterNode); + + final List arguments = new ArrayList<>(); + org.jrubyparser.ast.Node blockPassNode = null; + + boolean isSplatted = false; + + if (argsNode instanceof org.jrubyparser.ast.ListNode) { + arguments.addAll(((org.jrubyparser.ast.ListNode) argsNode).childNodes()); + } else if (argsNode instanceof org.jrubyparser.ast.BlockPassNode) { + final org.jrubyparser.ast.BlockPassNode blockPass = (org.jrubyparser.ast.BlockPassNode) argsNode; + + final org.jrubyparser.ast.Node blockPassArgs = blockPass.getArgs(); + + if (blockPassArgs instanceof org.jrubyparser.ast.ListNode) { + arguments.addAll(((org.jrubyparser.ast.ListNode) blockPassArgs).childNodes()); + } else if (blockPassArgs instanceof org.jrubyparser.ast.ArgsCatNode) { + arguments.add(blockPassArgs); + } else if (blockPassArgs != null) { + throw new UnsupportedOperationException("Don't know how to block pass " + blockPassArgs); + } + + blockPassNode = blockPass.getBody(); + } else if (argsNode instanceof org.jrubyparser.ast.SplatNode) { + isSplatted = true; + arguments.add(argsNode); + } else if (argsNode instanceof org.jrubyparser.ast.ArgsCatNode) { + isSplatted = true; + arguments.add(argsNode); + } else if (argsNode != null) { + isSplatted = true; + arguments.add(argsNode); + } + + RubyNode blockTranslated; + + if (blockPassNode != null && iterNode != null) { + throw new UnsupportedOperationException("Don't know how to pass both an block and a block-pass argument"); + } else if (iterNode != null) { + blockTranslated = (BlockDefinitionNode) iterNode.accept(this); + } else if (blockPassNode != null) { + blockTranslated = ProcCastNodeFactory.create(context, sourceSection, (RubyNode) blockPassNode.accept(this)); + } else { + blockTranslated = null; + } + + final List argumentsTranslated = new ArrayList<>(); + + for (org.jrubyparser.ast.Node argument : arguments) { + argumentsTranslated.add((RubyNode) argument.accept(this)); + } + + if (extraArgument != null) { + argumentsTranslated.add(extraArgument); + } + + final RubyNode[] argumentsTranslatedArray = argumentsTranslated.toArray(new RubyNode[argumentsTranslated.size()]); + + return new ArgumentsAndBlockTranslation(blockTranslated, argumentsTranslatedArray, isSplatted); + } + + @Override + public Object visitCaseNode(org.jrubyparser.ast.CaseNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + RubyNode elseNode; + + if (node.getElse() != null) { + elseNode = (RubyNode) node.getElse().accept(this); + } else { + elseNode = new NilNode(context, sourceSection); + } + + /* + * There are two sorts of case - one compares a list of expressions against a value, the + * other just checks a list of expressions for truth. + */ + + if (node.getCase() != null) { + // Evaluate the case expression and store it in a local + + final String tempName = environment.allocateLocalTemp(); + + final RubyNode readTemp = environment.findLocalVarNode(tempName, sourceSection); + + final RubyNode assignTemp = ((ReadNode) readTemp).makeWriteNode((RubyNode) node.getCase().accept(this)); + + /* + * Build an if expression from the whens and else. Work backwards because the first if + * contains all the others in its else clause. + */ + + for (int n = node.getCases().size() - 1; n >= 0; n--) { + final org.jrubyparser.ast.WhenNode when = (org.jrubyparser.ast.WhenNode) node.getCases().get(n); + + // Make a condition from the one or more expressions combined in an or expression + + final List expressions; + + if (when.getExpression() instanceof org.jrubyparser.ast.ListNode) { + expressions = ((org.jrubyparser.ast.ListNode) when.getExpression()).childNodes(); + } else { + expressions = Arrays.asList(when.getExpression()); + } + + final List comparisons = new ArrayList<>(); + + for (org.jrubyparser.ast.Node expressionNode : expressions) { + final RubyNode rubyExpression = (RubyNode) expressionNode.accept(this); + + final CallNode comparison = new CallNode(context, sourceSection, "===", rubyExpression, null, false, new RubyNode[]{environment.findLocalVarNode(tempName, sourceSection)}); + + comparisons.add(comparison); + } + + RubyNode conditionNode = comparisons.get(comparisons.size() - 1); + + // As with the if nodes, we work backwards to make it left associative + + for (int i = comparisons.size() - 2; i >= 0; i--) { + conditionNode = OrNodeFactory.create(context, sourceSection, comparisons.get(i), conditionNode); + } + + // Create the if node + + final BooleanCastNode conditionCastNode = BooleanCastNodeFactory.create(context, sourceSection, conditionNode); + + RubyNode thenNode; + + if (when.getBody() == null) { + thenNode = new NilNode(context, sourceSection); + } else { + thenNode = (RubyNode) when.getBody().accept(this); + } + + final IfNode ifNode = new IfNode(context, sourceSection, conditionCastNode, thenNode, elseNode); + + // This if becomes the else for the next if + + elseNode = ifNode; + } + + final RubyNode ifNode = elseNode; + + // A top-level block assigns the temp then runs the if + + return new SequenceNode(context, sourceSection, assignTemp, ifNode); + } else { + for (int n = node.getCases().size() - 1; n >= 0; n--) { + final org.jrubyparser.ast.WhenNode when = (org.jrubyparser.ast.WhenNode) node.getCases().get(n); + + // Make a condition from the one or more expressions combined in an or expression + + final List expressions; + + if (when.getExpression() instanceof org.jrubyparser.ast.ListNode) { + expressions = ((org.jrubyparser.ast.ListNode) when.getExpression()).childNodes(); + } else { + expressions = Arrays.asList(when.getExpression()); + } + + final List tests = new ArrayList<>(); + + for (org.jrubyparser.ast.Node expressionNode : expressions) { + final RubyNode rubyExpression = (RubyNode) expressionNode.accept(this); + tests.add(rubyExpression); + } + + RubyNode conditionNode = tests.get(tests.size() - 1); + + // As with the if nodes, we work backwards to make it left associative + + for (int i = tests.size() - 2; i >= 0; i--) { + conditionNode = OrNodeFactory.create(context, sourceSection, tests.get(i), conditionNode); + } + + // Create the if node + + final BooleanCastNode conditionCastNode = BooleanCastNodeFactory.create(context, sourceSection, conditionNode); + + final RubyNode thenNode = (RubyNode) when.getBody().accept(this); + + final IfNode ifNode = new IfNode(context, sourceSection, conditionCastNode, thenNode, elseNode); + + // This if becomes the else for the next if + + elseNode = ifNode; + } + + return elseNode; + } + } + + @Override + public Object visitClassNode(org.jrubyparser.ast.ClassNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final String name = node.getCPath().getName(); + + final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getParser().allocateReturnID(), true, true, + new UniqueMethodIdentifier()); + final ModuleTranslator classTranslator = new ModuleTranslator(context, this, newEnvironment, source); + + final MethodDefinitionNode definitionMethod = classTranslator.compileClassNode(node.getPosition(), node.getCPath().getName(), node.getBody()); + + /* + * See my note in visitDefnNode about where the class gets defined - the same applies here. + */ + + RubyNode superClass; + + if (node.getSuper() != null) { + superClass = (RubyNode) node.getSuper().accept(this); + } else { + superClass = new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getObjectClass()); + } + + final DefineOrGetClassNode defineOrGetClass = new DefineOrGetClassNode(context, sourceSection, name, getModuleToDefineModulesIn(sourceSection), superClass); + + return new OpenModuleNode(context, sourceSection, defineOrGetClass, definitionMethod); + } + + protected RubyNode getModuleToDefineModulesIn(SourceSection sourceSection) { + return new ClassNode(context, sourceSection, new SelfNode(context, sourceSection)); + } + + @Override + public Object visitClassVarAsgnNode(org.jrubyparser.ast.ClassVarAsgnNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final RubyNode receiver = new ClassNode(context, sourceSection, new SelfNode(context, sourceSection)); + + final RubyNode rhs = (RubyNode) node.getValue().accept(this); + + return new WriteClassVariableNode(context, sourceSection, node.getName(), receiver, rhs); + } + + @Override + public Object visitClassVarDeclNode(org.jrubyparser.ast.ClassVarDeclNode node) { + return unimplemented(node); + } + + @Override + public Object visitClassVarNode(org.jrubyparser.ast.ClassVarNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + return new ReadClassVariableNode(context, sourceSection, node.getName(), new SelfNode(context, sourceSection)); + } + + @Override + public Object visitColon2Node(org.jrubyparser.ast.Colon2Node node) { + final RubyNode lhs = (RubyNode) node.getLeftNode().accept(this); + + return new UninitializedReadConstantNode(context, translate(node.getPosition()), node.getName(), lhs); + } + + @Override + public Object visitColon3Node(org.jrubyparser.ast.Colon3Node node) { + // Colon3 means the root namespace, as in ::Foo + + final SourceSection sourceSection = translate(node.getPosition()); + + final ObjectLiteralNode root = new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getMainObject()); + + return new UninitializedReadConstantNode(context, sourceSection, node.getName(), root); + } + + @Override + public Object visitConstDeclNode(org.jrubyparser.ast.ConstDeclNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final ClassNode classNode = new ClassNode(context, sourceSection, new SelfNode(context, sourceSection)); + + return new WriteConstantNode(context, sourceSection, node.getName(), classNode, (RubyNode) node.getValue().accept(this)); + } + + @Override + public Object visitConstNode(org.jrubyparser.ast.ConstNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + return new UninitializedReadConstantNode(context, sourceSection, node.getName(), new SelfNode(context, sourceSection)); + } + + @Override + public Object visitDAsgnNode(org.jrubyparser.ast.DAsgnNode node) { + return new org.jrubyparser.ast.LocalAsgnNode(node.getPosition(), node.getName(), node.getDepth(), node.getValue()).accept(this); + } + + @Override + public Object visitDRegxNode(org.jrubyparser.ast.DRegexpNode node) { + SourceSection sourceSection = translate(node.getPosition()); + + final RubyNode stringNode = translateInterpolatedString(sourceSection, node.childNodes()); + + return StringToRegexpNodeFactory.create(context, sourceSection, stringNode); + } + + @Override + public Object visitDStrNode(org.jrubyparser.ast.DStrNode node) { + return translateInterpolatedString(translate(node.getPosition()), node.childNodes()); + } + + @Override + public Object visitDSymbolNode(org.jrubyparser.ast.DSymbolNode node) { + SourceSection sourceSection = translate(node.getPosition()); + + final RubyNode stringNode = translateInterpolatedString(sourceSection, node.childNodes()); + + return StringToSymbolNodeFactory.create(context, sourceSection, stringNode); + } + + private RubyNode translateInterpolatedString(SourceSection sourceSection, List childNodes) { + final List children = new ArrayList<>(); + + for (org.jrubyparser.ast.Node child : childNodes) { + children.add((RubyNode) child.accept(this)); + } + + return new InterpolatedStringNode(context, sourceSection, children.toArray(new RubyNode[children.size()])); + } + + @Override + public Object visitDVarNode(org.jrubyparser.ast.DVarNode node) { + RubyNode readNode = environment.findLocalVarNode(node.getName(), translate(node.getPosition())); + + if (readNode == null) { + context.implementationMessage("can't find variable %s at %s, using noop", node.getName(), node.getPosition()); + readNode = new NilNode(context, translate(node.getPosition())); + } + + return readNode; + } + + @Override + public Object visitDXStrNode(org.jrubyparser.ast.DXStrNode node) { + SourceSection sourceSection = translate(node.getPosition()); + + final RubyNode string = translateInterpolatedString(sourceSection, node.childNodes()); + + return new SystemNode(context, sourceSection, string); + } + + @Override + public Object visitDefinedNode(org.jrubyparser.ast.DefinedNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + org.jrubyparser.ast.Node expressionNode = node.getExpression(); + + while (expressionNode instanceof org.jrubyparser.ast.NewlineNode) { + expressionNode = ((org.jrubyparser.ast.NewlineNode) expressionNode).getNextNode(); + } + + final String name = nodeDefinedNames.get(expressionNode.getClass()); + + if (name != null) { + final StringLiteralNode literal = new StringLiteralNode(context, sourceSection, name); + return literal; + } + + return new DefinedNode(context, sourceSection, (RubyNode) node.getExpression().accept(this)); + } + + @Override + public Object visitDefnNode(org.jrubyparser.ast.DefnNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + final ClassNode classNode = new ClassNode(context, sourceSection, new SelfNode(context, sourceSection)); + return translateMethodDefinition(sourceSection, classNode, node.getName(), node.getArgs(), node.getBody()); + } + + @Override + public Object visitDefsNode(org.jrubyparser.ast.DefsNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final RubyNode objectNode = (RubyNode) node.getReceiver().accept(this); + + final SingletonClassNode singletonClassNode = new SingletonClassNode(context, sourceSection, objectNode); + + return translateMethodDefinition(sourceSection, singletonClassNode, node.getName(), node.getArgs(), node.getBody()); + } + + private RubyNode translateMethodDefinition(SourceSection sourceSection, RubyNode classNode, String methodName, org.jrubyparser.ast.ArgsNode argsNode, org.jrubyparser.ast.Node bodyNode) { + final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getParser().allocateReturnID(), true, true, + new UniqueMethodIdentifier()); + + // ownScopeForAssignments is the same for the defined method as the current one. + + final MethodTranslator methodCompiler = new MethodTranslator(context, this, newEnvironment, false, source); + + final MethodDefinitionNode functionExprNode = methodCompiler.compileFunctionNode(sourceSection, methodName, argsNode, bodyNode); + + /* + * In the top-level, methods are defined in the class of the main object. This is + * counter-intuitive - I would have expected them to be defined in the singleton class. + * Apparently this is a design decision to make top-level methods sort of global. + * + * http://stackoverflow.com/questions/1761148/where-are-methods-defined-at-the-ruby-top-level + */ + + return new AddMethodNode(context, sourceSection, classNode, functionExprNode); + } + + @Override + public Object visitDotNode(org.jrubyparser.ast.DotNode node) { + final RubyNode begin = (RubyNode) node.getBegin().accept(this); + final RubyNode end = (RubyNode) node.getEnd().accept(this); + SourceSection sourceSection = translate(node.getPosition()); + + if (begin instanceof FixnumLiteralNode && end instanceof FixnumLiteralNode) { + final int beginValue = ((FixnumLiteralNode) begin).getValue(); + final int endValue = ((FixnumLiteralNode) end).getValue(); + + return new ObjectLiteralNode(context, sourceSection, new FixnumRange(context.getCoreLibrary().getRangeClass(), beginValue, endValue, node.isExclusive())); + } + // See RangeNode for why there is a node specifically for creating this one type + return RangeLiteralNodeFactory.create(context, sourceSection, node.isExclusive(), begin, end); + } + + @Override + public Object visitEncodingNode(org.jrubyparser.ast.EncodingNode node) { + return unimplemented(node); + } + + @Override + public Object visitEnsureNode(org.jrubyparser.ast.EnsureNode node) { + final RubyNode tryPart = (RubyNode) node.getBody().accept(this); + final RubyNode ensurePart = (RubyNode) node.getEnsure().accept(this); + return new EnsureNode(context, translate(node.getPosition()), tryPart, ensurePart); + } + + @Override + public Object visitEvStrNode(org.jrubyparser.ast.EvStrNode node) { + return node.getBody().accept(this); + } + + @Override + public Object visitFCallNode(org.jrubyparser.ast.FCallNode node) { + final org.jrubyparser.ast.Node receiver = new org.jrubyparser.ast.SelfNode(node.getPosition()); + final org.jrubyparser.ast.Node callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), receiver, node.getName(), node.getArgs(), node.getIter()); + + return callNode.accept(this); + } + + @Override + public Object visitFalseNode(org.jrubyparser.ast.FalseNode node) { + return new BooleanLiteralNode(context, translate(node.getPosition()), false); + } + + @Override + public Object visitFixnumNode(org.jrubyparser.ast.FixnumNode node) { + final long value = node.getValue(); + + if (value >= RubyFixnum.MIN_VALUE && value <= RubyFixnum.MAX_VALUE) { + return new FixnumLiteralNode(context, translate(node.getPosition()), (int) value); + } + return new BignumLiteralNode(context, translate(node.getPosition()), BigInteger.valueOf(value)); + } + + @Override + public Object visitFlipNode(org.jrubyparser.ast.FlipNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final RubyNode begin = (RubyNode) node.getBegin().accept(this); + final RubyNode end = (RubyNode) node.getEnd().accept(this); + + final BooleanCastNode beginCast = BooleanCastNodeFactory.create(context, sourceSection, begin); + final BooleanCastNode endCast = BooleanCastNodeFactory.create(context, sourceSection, end); + final FlipFlopStateNode stateNode = createFlipFlopState(sourceSection, 0); + + return new FlipFlopNode(context, sourceSection, beginCast, endCast, stateNode, node.isExclusive()); + } + + protected FlipFlopStateNode createFlipFlopState(SourceSection sourceSection, int depth) { + final FrameSlot frameSlot = environment.declareVar(environment.allocateLocalTemp()); + environment.getFlipFlopStates().add(frameSlot); + + if (depth == 0) { + return new LocalFlipFlopStateNode(sourceSection, frameSlot); + } else { + return new LevelFlipFlopStateNode(sourceSection, depth, frameSlot); + } + } + + @Override + public Object visitFloatNode(org.jrubyparser.ast.FloatNode node) { + return new FloatLiteralNode(context, translate(node.getPosition()), node.getValue()); + } + + @Override + public Object visitForNode(org.jrubyparser.ast.ForNode node) { + /** + * A Ruby for-loop, such as: + * + *

+         * for x in y
+         *     z = x
+         *     puts z
+         * end
+         * 
+ * + * naively desugars to: + * + *
+         * y.each do |x|
+         *     z = x
+         *     puts z
+         * end
+         * 
+ * + * The main difference is that z is always going to be local to the scope outside the block, + * so it's a bit more like: + * + *
+         * z = nil unless z is already defined
+         * y.each do |x|
+         *    z = x
+         *    puts x
+         * end
+         * 
+ * + * Which forces z to be defined in the correct scope. The parser already correctly calls z a + * local, but then that causes us a problem as if we're going to translate to a block we + * need a formal parameter - not a local variable. My solution to this is to add a + * temporary: + * + *
+         * z = nil unless z is already defined
+         * y.each do |temp|
+         *    x = temp
+         *    z = x
+         *    puts x
+         * end
+         * 
+ * + * We also need that temp because the expression assigned in the for could be index + * assignment, multiple assignment, or whatever: + * + *
+         * for x[0] in y
+         *     z = x[0]
+         *     puts z
+         * end
+         * 
+ * + * http://blog.grayproductions.net/articles/the_evils_of_the_for_loop + * http://stackoverflow.com/questions/3294509/for-vs-each-in-ruby + * + * The other complication is that normal locals should be defined in the enclosing scope, + * unlike a normal block. We do that by setting a flag on this translator object when we + * visit the new iter, translatingForStatement, which we recognise when visiting an iter + * node. + * + * Finally, note that JRuby's terminology is strange here. Normally 'iter' is a different + * term for a block. Here, JRuby calls the object being iterated over the 'iter'. + */ + + final String temp = environment.allocateLocalTemp(); + + final org.jrubyparser.ast.Node receiver = node.getIter(); + + /* + * The x in for x in ... is like the nodes in multiple assignment - it has a dummy RHS which + * we need to replace with our temp. Just like in multiple assignment this is really awkward + * with the JRuby AST. + */ + + final org.jrubyparser.ast.LocalVarNode readTemp = new org.jrubyparser.ast.LocalVarNode(node.getPosition(), 0, temp); + final org.jrubyparser.ast.Node forVar = node.getVar(); + final org.jrubyparser.ast.Node assignTemp = setRHS(forVar, readTemp); + + final org.jrubyparser.ast.BlockNode bodyWithTempAssign = new org.jrubyparser.ast.BlockNode(node.getPosition()); + bodyWithTempAssign.add(assignTemp); + bodyWithTempAssign.add(node.getBody()); + + final org.jrubyparser.ast.ArgumentNode blockVar = new org.jrubyparser.ast.ArgumentNode(node.getPosition(), temp); + final org.jrubyparser.ast.ListNode blockArgsPre = new org.jrubyparser.ast.ListNode(node.getPosition(), blockVar); + final org.jrubyparser.ast.ArgsNode blockArgs = new org.jrubyparser.ast.ArgsNode(node.getPosition(), blockArgsPre, null, null, null, null, null, null); + final org.jrubyparser.ast.IterNode block = new org.jrubyparser.ast.IterNode(node.getPosition(), blockArgs, node.getScope(), bodyWithTempAssign); + + final org.jrubyparser.ast.CallNode callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), receiver, "each", null, block); + + translatingForStatement = true; + final RubyNode translated = (RubyNode) callNode.accept(this); + translatingForStatement = false; + + return translated; + } + + private static org.jrubyparser.ast.Node setRHS(org.jrubyparser.ast.Node node, org.jrubyparser.ast.Node rhs) { + if (node instanceof org.jrubyparser.ast.LocalAsgnNode) { + final org.jrubyparser.ast.LocalAsgnNode localAsgnNode = (org.jrubyparser.ast.LocalAsgnNode) node; + return new org.jrubyparser.ast.LocalAsgnNode(node.getPosition(), localAsgnNode.getName(), 0, rhs); + } else if (node instanceof org.jrubyparser.ast.DAsgnNode) { + final org.jrubyparser.ast.DAsgnNode dAsgnNode = (org.jrubyparser.ast.DAsgnNode) node; + return new org.jrubyparser.ast.DAsgnNode(node.getPosition(), dAsgnNode.getName(), 0, rhs); + } else if (node instanceof org.jrubyparser.ast.MultipleAsgnNode) { + final org.jrubyparser.ast.MultipleAsgnNode multAsgnNode = (org.jrubyparser.ast.MultipleAsgnNode) node; + return new org.jrubyparser.ast.MultipleAsgnNode(node.getPosition(), multAsgnNode.getPre(), multAsgnNode.getRest(), multAsgnNode.getPost()); + } else if (node instanceof org.jrubyparser.ast.InstAsgnNode) { + final org.jrubyparser.ast.InstAsgnNode instAsgnNode = (org.jrubyparser.ast.InstAsgnNode) node; + return new org.jrubyparser.ast.InstAsgnNode(node.getPosition(), instAsgnNode.getName(), rhs); + } else if (node instanceof org.jrubyparser.ast.ClassVarAsgnNode) { + final org.jrubyparser.ast.ClassVarAsgnNode instAsgnNode = (org.jrubyparser.ast.ClassVarAsgnNode) node; + return new org.jrubyparser.ast.ClassVarAsgnNode(node.getPosition(), instAsgnNode.getName(), rhs); + } else if (node instanceof org.jrubyparser.ast.ConstDeclNode) { + final org.jrubyparser.ast.ConstDeclNode constDeclNode = (org.jrubyparser.ast.ConstDeclNode) node; + return new org.jrubyparser.ast.ConstDeclNode(node.getPosition(), constDeclNode.getName(), (org.jrubyparser.ast.INameNode) constDeclNode.getConstNode(), rhs); + } else { + throw new UnsupportedOperationException("Don't know how to set the RHS of a " + node.getClass().getName()); + } + } + + @Override + public Object visitGlobalAsgnNode(org.jrubyparser.ast.GlobalAsgnNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final String name = "$" + node.getName(); + final RubyNode rhs = (RubyNode) node.getValue().accept(this); + + if (FRAME_LOCAL_GLOBAL_VARIABLES.contains(name)) { + context.implementationMessage("Assigning to frame local global variables not implemented at %s", node.getPosition()); + + return rhs; + } else { + final ObjectLiteralNode globalVariablesObjectNode = new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getGlobalVariablesObject()); + + return new UninitializedWriteInstanceVariableNode(context, sourceSection, name, globalVariablesObjectNode, rhs); + } + } + + @Override + public Object visitGlobalVarNode(org.jrubyparser.ast.GlobalVarNode node) { + final String name = "$" + node.getName(); + final SourceSection sourceSection = translate(node.getPosition()); + + if (FRAME_LOCAL_GLOBAL_VARIABLES.contains(name)) { + // Assignment is implicit for many of these, so we need to declare when we use + + environment.declareVar(name); + + final RubyNode readNode = environment.findLocalVarNode(name, sourceSection); + + return readNode; + } else { + final ObjectLiteralNode globalVariablesObjectNode = new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getGlobalVariablesObject()); + + return new UninitializedReadInstanceVariableNode(context, sourceSection, name, globalVariablesObjectNode); + } + } + + @Override + public Object visitHashNode(org.jrubyparser.ast.HashNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final List keys = new ArrayList<>(); + final List values = new ArrayList<>(); + + final org.jrubyparser.ast.ListNode entries = node.getListNode(); + + assert entries.size() % 2 == 0; + + for (int n = 0; n < entries.size(); n += 2) { + if (entries.get(n) == null) { + final NilNode nilNode = new NilNode(context, sourceSection); + keys.add(nilNode); + } else { + keys.add((RubyNode) entries.get(n).accept(this)); + } + + if (entries.get(n + 1) == null) { + final NilNode nilNode = new NilNode(context, sourceSection); + values.add(nilNode); + } else { + values.add((RubyNode) entries.get(n + 1).accept(this)); + } + } + + return new HashLiteralNode(translate(node.getPosition()), keys.toArray(new RubyNode[keys.size()]), values.toArray(new RubyNode[values.size()]), context); + } + + @Override + public Object visitIfNode(org.jrubyparser.ast.IfNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + org.jrubyparser.ast.Node thenBody = node.getThenBody(); + + if (thenBody == null) { + thenBody = new org.jrubyparser.ast.NilNode(node.getPosition()); + } + + org.jrubyparser.ast.Node elseBody = node.getElseBody(); + + if (elseBody == null) { + elseBody = new org.jrubyparser.ast.NilNode(node.getPosition()); + } + + RubyNode condition; + + if (node.getCondition() == null) { + condition = new NilNode(context, sourceSection); + } else { + condition = (RubyNode) node.getCondition().accept(this); + } + + final BooleanCastNode conditionCast = BooleanCastNodeFactory.create(context, sourceSection, condition); + + final RubyNode thenBodyTranslated = (RubyNode) thenBody.accept(this); + final RubyNode elseBodyTranslated = (RubyNode) elseBody.accept(this); + + return new IfNode(context, sourceSection, conditionCast, thenBodyTranslated, elseBodyTranslated); + } + + @Override + public Object visitInstAsgnNode(org.jrubyparser.ast.InstAsgnNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + final String nameWithoutSigil = node.getName(); + + final RubyNode receiver = new SelfNode(context, sourceSection); + + RubyNode rhs; + + if (node.getValue() == null) { + rhs = new DeadNode(context, sourceSection); + } else { + rhs = (RubyNode) node.getValue().accept(this); + } + + return new UninitializedWriteInstanceVariableNode(context, sourceSection, nameWithoutSigil, receiver, rhs); + } + + @Override + public Object visitInstVarNode(org.jrubyparser.ast.InstVarNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + final String nameWithoutSigil = node.getName(); + + final RubyNode receiver = new SelfNode(context, sourceSection); + + return new UninitializedReadInstanceVariableNode(context, sourceSection, nameWithoutSigil, receiver); + } + + @Override + public Object visitIterNode(org.jrubyparser.ast.IterNode node) { + /* + * In a block we do NOT allocate a new return ID - returns will return from the method, not + * the block (in the general case, see Proc and the difference between Proc and Lambda for + * specifics). + */ + + final boolean hasOwnScope = !translatingForStatement; + + // Unset this flag for any for any blocks within the for statement's body + + translatingForStatement = false; + + final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getReturnID(), hasOwnScope, false, + new UniqueMethodIdentifier()); + final MethodTranslator methodCompiler = new MethodTranslator(context, this, newEnvironment, true, source); + + org.jrubyparser.ast.ArgsNode argsNode; + + if (node.getVar() instanceof org.jrubyparser.ast.ArgsNode) { + argsNode = (org.jrubyparser.ast.ArgsNode) node.getVar(); + } else if (node.getVar() instanceof org.jrubyparser.ast.DAsgnNode) { + final org.jrubyparser.ast.ArgumentNode arg = new org.jrubyparser.ast.ArgumentNode(node.getPosition(), ((org.jrubyparser.ast.DAsgnNode) node.getVar()).getName()); + final org.jrubyparser.ast.ListNode preArgs = new org.jrubyparser.ast.ArrayNode(node.getPosition(), arg); + argsNode = new org.jrubyparser.ast.ArgsNode(node.getPosition(), preArgs, null, null, null, null, null, null); + } else if (node.getVar() == null) { + argsNode = null; + } else { + throw new UnsupportedOperationException(); + } + + return methodCompiler.compileFunctionNode(translate(node.getPosition()), "(block)", argsNode, node.getBody()); + } + + @Override + public Object visitLiteralNode(org.jrubyparser.ast.LiteralNode node) { + return unimplemented(node); + } + + @Override + public Object visitLocalAsgnNode(org.jrubyparser.ast.LocalAsgnNode node) { + + final SourceSection sourceSection = translate(node.getPosition()); + + if (environment.getNeverAssignInParentScope()) { + environment.declareVar(node.getName()); + } + + RubyNode lhs = environment.findLocalVarNode(node.getName(), sourceSection); + + if (lhs == null) { + if (environment.hasOwnScopeForAssignments()) { + environment.declareVar(node.getName()); + } + + TranslatorEnvironment environmentToDeclareIn = environment; + + while (!environmentToDeclareIn.hasOwnScopeForAssignments()) { + environmentToDeclareIn = environmentToDeclareIn.getParent(); + } + + environmentToDeclareIn.declareVar(node.getName()); + lhs = environment.findLocalVarNode(node.getName(), sourceSection); + + if (lhs == null) { + throw new RuntimeException("shoudln't be here"); + } + } + + RubyNode rhs; + + if (node.getValue() == null) { + rhs = new DeadNode(context, sourceSection); + } else { + rhs = (RubyNode) node.getValue().accept(this); + } + + RubyNode translated = ((ReadNode) lhs).makeWriteNode(rhs); + + if (context.getConfiguration().getDebug()) { + final UniqueMethodIdentifier methodIdentifier = environment.findMethodForLocalVar(node.getName()); + + RubyProxyNode proxy; + if (translated instanceof RubyProxyNode) { + proxy = (RubyProxyNode) translated; + } else { + proxy = new RubyProxyNode(context, translated); + } + context.getDebugManager().registerLocalDebugProxy(methodIdentifier, node.getName(), proxy.getProbeChain()); + + translated = proxy; + } + + return translated; + } + + @Override + public Object visitLocalVarNode(org.jrubyparser.ast.LocalVarNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final String name = node.getName(); + + RubyNode readNode = environment.findLocalVarNode(name, sourceSection); + + if (readNode == null) { + context.implementationMessage("Local variable found by parser but not by translator - " + name + " at " + node.getPosition()); + readNode = environment.findLocalVarNode(environment.allocateLocalTemp(), sourceSection); + } + + return readNode; + } + + @Override + public Object visitMatch2Node(org.jrubyparser.ast.Match2Node node) { + final org.jrubyparser.ast.Node argsNode = buildArrayNode(node.getPosition(), node.getValue()); + final org.jrubyparser.ast.Node callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), node.getReceiver(), "=~", argsNode, null); + return callNode.accept(this); + } + + @Override + public Object visitMatch3Node(org.jrubyparser.ast.Match3Node node) { + final org.jrubyparser.ast.Node argsNode = buildArrayNode(node.getPosition(), node.getValue()); + final org.jrubyparser.ast.Node callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), node.getReceiver(), "=~", argsNode, null); + return callNode.accept(this); + } + + @Override + public Object visitMatchNode(org.jrubyparser.ast.MatchNode node) { + return unimplemented(node); + } + + @Override + public Object visitModuleNode(org.jrubyparser.ast.ModuleNode node) { + // See visitClassNode + + final SourceSection sourceSection = translate(node.getPosition()); + + final String name = node.getCPath().getName(); + + final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getParser().allocateReturnID(), true, true, + new UniqueMethodIdentifier()); + final ModuleTranslator classTranslator = new ModuleTranslator(context, this, newEnvironment, source); + + final MethodDefinitionNode definitionMethod = classTranslator.compileClassNode(node.getPosition(), node.getCPath().getName(), node.getBody()); + + final DefineOrGetModuleNode defineModuleNode = new DefineOrGetModuleNode(context, sourceSection, name, getModuleToDefineModulesIn(sourceSection)); + + return new OpenModuleNode(context, sourceSection, defineModuleNode, definitionMethod); + } + + @Override + public Object visitMultipleAsgnNode(org.jrubyparser.ast.MultipleAsgnNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final org.jrubyparser.ast.ArrayNode preArray = (org.jrubyparser.ast.ArrayNode) node.getPre(); + final org.jrubyparser.ast.Node rhs = node.getValue(); + + RubyNode rhsTranslated; + + if (rhs == null) { + context.implementationMessage("warning: no RHS for multiple assignment - using noop"); + rhsTranslated = new NilNode(context, sourceSection); + } else { + rhsTranslated = (RubyNode) rhs.accept(this); + } + + /* + * One very common case is to do + * + * a, b = c, d + */ + + if (preArray != null && node.getPost() == null && node.getRest() == null && rhsTranslated instanceof UninitialisedArrayLiteralNode && + ((UninitialisedArrayLiteralNode) rhsTranslated).getValues().length == preArray.size()) { + /* + * We can deal with this common case be rewriting as + * + * temp1 = c; temp2 = d; a = temp1; b = temp2 + * + * We can't just do + * + * a = c; b = d + * + * As we don't know if d depends on the original value of a. + * + * We also need to return an array [c, d], but we make that result elidable so it isn't + * executed if it isn't actually demanded. + */ + + final RubyNode[] rhsValues = ((UninitialisedArrayLiteralNode) rhsTranslated).getValues(); + final int assignedValuesCount = preArray.size(); + + final RubyNode[] sequence = new RubyNode[assignedValuesCount * 2]; + + final RubyNode[] tempValues = new RubyNode[assignedValuesCount]; + + for (int n = 0; n < assignedValuesCount; n++) { + final String tempName = environment.allocateLocalTemp(); + final RubyNode readTemp = environment.findLocalVarNode(tempName, sourceSection); + final RubyNode assignTemp = ((ReadNode) readTemp).makeWriteNode(rhsValues[n]); + final RubyNode assignFinalValue = translateDummyAssignment(preArray.get(n), readTemp); + + sequence[n] = assignTemp; + sequence[assignedValuesCount + n] = assignFinalValue; + + tempValues[n] = readTemp; + } + + final RubyNode blockNode = new SequenceNode(context, sourceSection, sequence); + + final UninitialisedArrayLiteralNode arrayNode = new UninitialisedArrayLiteralNode(context, sourceSection, tempValues); + + final ElidableResultNode elidableResult = new ElidableResultNode(context, sourceSection, blockNode, arrayNode); + + return elidableResult; + } else if (preArray != null) { + /* + * The other simple case is + * + * a, b, c = x + * + * If x is an array, then it's + * + * a[0] = x[0] etc + * + * If x isn't an array then it's + * + * a, b, c = [x, nil, nil] + * + * Which I believe is the same effect as + * + * a, b, c, = *x + * + * So we insert the splat cast node, even though it isn't there. + */ + + /* + * Create a temp for the array. + */ + + final String tempName = environment.allocateLocalTemp(); + + /* + * Create a sequence of instructions, with the first being the literal array assigned to + * the temp. + */ + + final List sequence = new ArrayList<>(); + + final RubyNode splatCastNode = SplatCastNodeFactory.create(context, sourceSection, rhsTranslated); + + final RubyNode writeTemp = ((ReadNode) environment.findLocalVarNode(tempName, sourceSection)).makeWriteNode(splatCastNode); + + sequence.add(writeTemp); + + /* + * Then index the temp array for each assignment on the LHS. + */ + + for (int n = 0; n < preArray.size(); n++) { + final ArrayIndexNode assignedValue = ArrayIndexNodeFactory.create(context, sourceSection, n, environment.findLocalVarNode(tempName, sourceSection)); + + sequence.add(translateDummyAssignment(preArray.get(n), assignedValue)); + } + + if (node.getRest() != null) { + final ArrayRestNode assignedValue = new ArrayRestNode(context, sourceSection, preArray.size(), environment.findLocalVarNode(tempName, sourceSection)); + + sequence.add(translateDummyAssignment(node.getRest(), assignedValue)); + } + + return new SequenceNode(context, sourceSection, sequence.toArray(new RubyNode[sequence.size()])); + } else if (node.getPre() == null && node.getPost() == null && node.getRest() instanceof org.jrubyparser.ast.StarNode) { + return rhsTranslated; + } else if (node.getPre() == null && node.getPost() == null && node.getRest() != null && rhs != null && !(rhs instanceof org.jrubyparser.ast.ArrayNode)) { + /* + * *a = b + * + * >= 1.8, this seems to be the same as: + * + * a = *b + */ + + final RubyNode restTranslated = ((RubyNode) node.getRest().accept(this)).getNonProxyNode(); + + /* + * Sometimes rest is a corrupt write with no RHS, like in other multiple assignments, + * and sometimes it is already a read. + */ + + ReadNode restRead; + + if (restTranslated instanceof ReadNode) { + restRead = (ReadNode) restTranslated; + } else if (restTranslated instanceof WriteNode) { + restRead = (ReadNode) ((WriteNode) restTranslated).makeReadNode(); + } else { + throw new RuntimeException("Unknown form of multiple assignment " + node + " at " + node.getPosition()); + } + + final SplatCastNode rhsSplatCast = SplatCastNodeFactory.create(context, sourceSection, rhsTranslated); + + return restRead.makeWriteNode(rhsSplatCast); + } else if (node.getPre() == null && node.getPost() == null && node.getRest() != null && rhs != null && rhs instanceof org.jrubyparser.ast.ArrayNode) { + /* + * *a = [b, c] + * + * This seems to be the same as: + * + * a = [b, c] + */ + + final RubyNode restTranslated = ((RubyNode) node.getRest().accept(this)).getNonProxyNode(); + + /* + * Sometimes rest is a corrupt write with no RHS, like in other multiple assignments, + * and sometimes it is already a read. + */ + + ReadNode restRead; + + if (restTranslated instanceof ReadNode) { + restRead = (ReadNode) restTranslated; + } else if (restTranslated instanceof WriteNode) { + restRead = (ReadNode) ((WriteNode) restTranslated).makeReadNode(); + } else { + throw new RuntimeException("Unknown form of multiple assignment " + node + " at " + node.getPosition()); + } + + return restRead.makeWriteNode(rhsTranslated); + } else { + throw new RuntimeException("Unknown form of multiple assignment " + node + " at " + node.getPosition()); + } + } + + private RubyNode translateDummyAssignment(org.jrubyparser.ast.Node dummyAssignment, RubyNode rhs) { + final SourceSection sourceSection = translate(dummyAssignment.getPosition()); + + /* + * This is tricky. To represent the RHS of a multiple assignment they use corrupt assignment + * values, in some cases with no value to be assigned, and in other cases with a dummy + * value. We can't visit them normally, as they're corrupt. We can't just modify them to + * have our RHS, as that's a node in our AST, not theirs. We can't use a dummy value in + * their AST because I can't add new visitors to this interface. + */ + + RubyNode translated; + + if (dummyAssignment instanceof org.jrubyparser.ast.LocalAsgnNode) { + /* + * They have a dummy NilImplicitNode as the RHS. Translate, convert to read, convert to + * write which allows us to set the RHS. + */ + + final WriteNode dummyTranslated = (WriteNode) ((RubyNode) dummyAssignment.accept(this)).getNonProxyNode(); + translated = ((ReadNode) dummyTranslated.makeReadNode()).makeWriteNode(rhs); + } else if (dummyAssignment instanceof org.jrubyparser.ast.InstAsgnNode) { + /* + * Same as before, just a different type of assignment. + */ + + final WriteInstanceVariableNode dummyTranslated = (WriteInstanceVariableNode) dummyAssignment.accept(this); + translated = dummyTranslated.makeReadNode().makeWriteNode(rhs); + } else if (dummyAssignment instanceof org.jrubyparser.ast.AttrAssignNode) { + /* + * They've given us an AttrAssignNode with the final argument, the assigned value, + * missing. If we translate that we'll get foo.[]=(index), so missing the value. To + * solve we have a special version of the visitCallNode that allows us to pass another + * already translated argument, visitCallNodeExtraArgument. However, we initially have + * an AttrAssignNode, so we also need a special version of that. + */ + + final org.jrubyparser.ast.AttrAssignNode dummyAttrAssignment = (org.jrubyparser.ast.AttrAssignNode) dummyAssignment; + translated = visitAttrAssignNodeExtraArgument(dummyAttrAssignment, rhs); + } else if (dummyAssignment instanceof org.jrubyparser.ast.DAsgnNode) { + final RubyNode dummyTranslated = (RubyNode) dummyAssignment.accept(this); + + if (dummyTranslated.getNonProxyNode() instanceof WriteLevelVariableNode) { + translated = ((ReadNode) ((WriteLevelVariableNode) dummyTranslated.getNonProxyNode()).makeReadNode()).makeWriteNode(rhs); + } else { + translated = ((ReadNode) ((WriteLocalVariableNode) dummyTranslated.getNonProxyNode()).makeReadNode()).makeWriteNode(rhs); + } + } else { + translated = ((ReadNode) environment.findLocalVarNode(environment.allocateLocalTemp(), sourceSection)).makeWriteNode(rhs); + } + + return translated; + } + + @Override + public Object visitNewlineNode(org.jrubyparser.ast.NewlineNode node) { + RubyNode translated = (RubyNode) node.getNextNode().accept(this); + + if (context.getConfiguration().getDebug()) { + + RubyProxyNode proxy; + SourceSection sourceSection; + if (translated instanceof RubyProxyNode) { + proxy = (RubyProxyNode) translated; + sourceSection = proxy.getChild().getSourceSection(); + } else { + proxy = new RubyProxyNode(context, translated); + sourceSection = translated.getSourceSection(); + } + context.getDebugManager().registerProbeChain(sourceSection, proxy.getProbeChain()); + translated = proxy; + } + + if (context.getConfiguration().getTrace()) { + RubyProxyNode proxy; + if (translated instanceof RubyProxyNode) { + proxy = (RubyProxyNode) translated; + } else { + proxy = new RubyProxyNode(context, translated); + } + proxy.getProbeChain().appendProbe(new RubyTraceProbe(context)); + + translated = proxy; + } + + return translated; + } + + @Override + public Object visitNextNode(org.jrubyparser.ast.NextNode node) { + return new NextNode(context, translate(node.getPosition())); + } + + @Override + public Object visitNilNode(org.jrubyparser.ast.NilNode node) { + return new NilNode(context, translate(node.getPosition())); + } + + @Override + public Object visitNotNode(org.jrubyparser.ast.NotNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final BooleanCastNode booleanCastNode = BooleanCastNodeFactory.create(context, sourceSection, (RubyNode) node.getCondition().accept(this)); + + return new NotNode(context, sourceSection, booleanCastNode); + } + + @Override + public Object visitNthRefNode(org.jrubyparser.ast.NthRefNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final String name = "$" + node.getMatchNumber(); + + RubyNode readLocal = environment.findLocalVarNode(name, sourceSection); + + if (readLocal == null) { + environment.declareVar(name); + readLocal = environment.findLocalVarNode(name, sourceSection); + } + + return readLocal; + } + + @Override + public Object visitOpAsgnAndNode(org.jrubyparser.ast.OpAsgnAndNode node) { + final org.jrubyparser.ast.Node lhs = node.getFirst(); + final org.jrubyparser.ast.Node rhs = node.getSecond(); + + return AndNodeFactory.create(context, translate(node.getPosition()), (RubyNode) lhs.accept(this), (RubyNode) rhs.accept(this)); + } + + @Override + public Object visitOpAsgnNode(org.jrubyparser.ast.OpAsgnNode node) { + /* + * We're going to de-sugar a.foo += c into a.foo = a.foo + c. Note that we can't evaluate a + * more than once, so we put it into a temporary, and we're doing something more like: + * + * temp = a; temp.foo = temp.foo + c + */ + + final String temp = environment.allocateLocalTemp(); + final org.jrubyparser.ast.Node writeReceiverToTemp = new org.jrubyparser.ast.LocalAsgnNode(node.getPosition(), temp, 0, node.getReceiver()); + final org.jrubyparser.ast.Node readReceiverFromTemp = new org.jrubyparser.ast.LocalVarNode(node.getPosition(), 0, temp); + + final org.jrubyparser.ast.Node readMethod = new org.jrubyparser.ast.CallNode(node.getPosition(), readReceiverFromTemp, node.getVariableName(), null); + final org.jrubyparser.ast.Node operation = new org.jrubyparser.ast.CallNode(node.getPosition(), readMethod, node.getOperatorName(), buildArrayNode(node.getPosition(), node.getValue())); + final org.jrubyparser.ast.Node writeMethod = new org.jrubyparser.ast.CallNode(node.getPosition(), readReceiverFromTemp, node.getVariableName() + "=", buildArrayNode(node.getPosition(), + operation)); + + final org.jrubyparser.ast.BlockNode block = new org.jrubyparser.ast.BlockNode(node.getPosition()); + block.add(writeReceiverToTemp); + block.add(writeMethod); + + return block.accept(this); + } + + @Override + public Object visitOpAsgnOrNode(org.jrubyparser.ast.OpAsgnOrNode node) { + /* + * De-sugar x ||= y into x || x = y. No repeated evaluations there so it's easy. It's also + * basically how jruby-parser represents it already. We'll do it directly, rather than via + * another JRuby AST node. + */ + + final org.jrubyparser.ast.Node lhs = node.getFirst(); + final org.jrubyparser.ast.Node rhs = node.getSecond(); + + return OrNodeFactory.create(context, translate(node.getPosition()), (RubyNode) lhs.accept(this), (RubyNode) rhs.accept(this)); + } + + @Override + public Object visitOpElementAsgnNode(org.jrubyparser.ast.OpElementAsgnNode node) { + /* + * We're going to de-sugar a[b] += c into a[b] = a[b] + c. See discussion in + * visitOpAsgnNode. + */ + + org.jrubyparser.ast.Node index; + + if (node.getArgs() == null) { + index = null; + } else { + index = node.getArgs().childNodes().get(0); + } + + final org.jrubyparser.ast.Node operand = node.getValue(); + + final String temp = environment.allocateLocalTemp(); + final org.jrubyparser.ast.Node writeArrayToTemp = new org.jrubyparser.ast.LocalAsgnNode(node.getPosition(), temp, 0, node.getReceiver()); + final org.jrubyparser.ast.Node readArrayFromTemp = new org.jrubyparser.ast.LocalVarNode(node.getPosition(), 0, temp); + + final org.jrubyparser.ast.Node arrayRead = new org.jrubyparser.ast.CallNode(node.getPosition(), readArrayFromTemp, "[]", buildArrayNode(node.getPosition(), index)); + + final String op = node.getOperatorName(); + + org.jrubyparser.ast.Node operation = null; + + if (op.equals("||")) { + operation = new org.jrubyparser.ast.OrNode(node.getPosition(), arrayRead, operand); + } else if (op.equals("&&")) { + operation = new org.jrubyparser.ast.AndNode(node.getPosition(), arrayRead, operand); + } else { + operation = new org.jrubyparser.ast.CallNode(node.getPosition(), arrayRead, node.getOperatorName(), buildArrayNode(node.getPosition(), operand)); + } + + final org.jrubyparser.ast.Node arrayWrite = new org.jrubyparser.ast.CallNode(node.getPosition(), readArrayFromTemp, "[]=", buildArrayNode(node.getPosition(), index, operation)); + + final org.jrubyparser.ast.BlockNode block = new org.jrubyparser.ast.BlockNode(node.getPosition()); + block.add(writeArrayToTemp); + block.add(arrayWrite); + + return block.accept(this); + } + + private static org.jrubyparser.ast.ArrayNode buildArrayNode(org.jrubyparser.SourcePosition sourcePosition, org.jrubyparser.ast.Node first, org.jrubyparser.ast.Node... rest) { + if (first == null) { + return new org.jrubyparser.ast.ArrayNode(sourcePosition); + } + + final org.jrubyparser.ast.ArrayNode array = new org.jrubyparser.ast.ArrayNode(sourcePosition, first); + + for (org.jrubyparser.ast.Node node : rest) { + array.add(node); + } + + return array; + } + + @Override + public Object visitOrNode(org.jrubyparser.ast.OrNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + RubyNode x; + + if (node.getFirst() == null) { + x = new NilNode(context, sourceSection); + } else { + x = (RubyNode) node.getFirst().accept(this); + } + + RubyNode y; + + if (node.getSecond() == null) { + y = new NilNode(context, sourceSection); + } else { + y = (RubyNode) node.getSecond().accept(this); + } + + return OrNodeFactory.create(context, sourceSection, x, y); + } + + @Override + public Object visitPostExeNode(org.jrubyparser.ast.PostExeNode node) { + return unimplemented(node); + } + + @Override + public Object visitPreExeNode(org.jrubyparser.ast.PreExeNode node) { + return unimplemented(node); + } + + @Override + public Object visitRedoNode(org.jrubyparser.ast.RedoNode node) { + return new RedoNode(context, translate(node.getPosition())); + } + + @Override + public Object visitRegexpNode(org.jrubyparser.ast.RegexpNode node) { + RubyRegexp regexp; + + try { + final String patternText = node.getValue(); + + int flags = Pattern.MULTILINE | Pattern.UNIX_LINES; + + final org.jrubyparser.RegexpOptions options = node.getOptions(); + + if (options.isIgnorecase()) { + flags |= Pattern.CASE_INSENSITIVE; + } + + if (options.isMultiline()) { + // TODO(cs): isn't this the default? + flags |= Pattern.MULTILINE; + } + + final Pattern pattern = Pattern.compile(patternText, flags); + + regexp = new RubyRegexp(context.getCoreLibrary().getRegexpClass(), pattern); + } catch (PatternSyntaxException e) { + context.implementationMessage("failed to parse Ruby regexp " + node.getValue() + " as Java regexp - replacing with ."); + regexp = new RubyRegexp(context.getCoreLibrary().getRegexpClass(), "."); + } + + final ObjectLiteralNode literalNode = new ObjectLiteralNode(context, translate(node.getPosition()), regexp); + return literalNode; + } + + @Override + public Object visitRescueBodyNode(org.jrubyparser.ast.RescueBodyNode node) { + return unimplemented(node); + } + + @Override + public Object visitRescueNode(org.jrubyparser.ast.RescueNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + RubyNode tryPart; + + if (node.getBody() != null) { + tryPart = (RubyNode) node.getBody().accept(this); + } else { + tryPart = new NilNode(context, sourceSection); + } + + final List rescueNodes = new ArrayList<>(); + + org.jrubyparser.ast.RescueBodyNode rescueBody = node.getRescue(); + + while (rescueBody != null) { + if (rescueBody.getExceptions() != null) { + if (rescueBody.getExceptions() instanceof org.jrubyparser.ast.ArrayNode) { + final List exceptionNodes = ((org.jrubyparser.ast.ArrayNode) rescueBody.getExceptions()).childNodes(); + + final RubyNode[] handlingClasses = new RubyNode[exceptionNodes.size()]; + + for (int n = 0; n < handlingClasses.length; n++) { + handlingClasses[n] = (RubyNode) exceptionNodes.get(n).accept(this); + } + + RubyNode translatedBody; + + if (rescueBody.getBody() == null) { + translatedBody = new NilNode(context, sourceSection); + } else { + translatedBody = (RubyNode) rescueBody.getBody().accept(this); + } + + final RescueClassesNode rescueNode = new RescueClassesNode(context, sourceSection, handlingClasses, translatedBody); + rescueNodes.add(rescueNode); + } else if (rescueBody.getExceptions() instanceof org.jrubyparser.ast.SplatNode) { + final org.jrubyparser.ast.SplatNode splat = (org.jrubyparser.ast.SplatNode) rescueBody.getExceptions(); + + RubyNode splatTranslated; + + if (splat.getValue() == null) { + splatTranslated = new NilNode(context, sourceSection); + } else { + splatTranslated = (RubyNode) splat.getValue().accept(this); + } + + RubyNode bodyTranslated; + + if (rescueBody.getBody() == null) { + bodyTranslated = new NilNode(context, sourceSection); + } else { + bodyTranslated = (RubyNode) rescueBody.getBody().accept(this); + } + + final RescueSplatNode rescueNode = new RescueSplatNode(context, sourceSection, splatTranslated, bodyTranslated); + rescueNodes.add(rescueNode); + } else { + unimplemented(node); + } + } else { + RubyNode bodyNode; + + if (rescueBody.getBody() == null) { + bodyNode = new NilNode(context, sourceSection); + } else { + bodyNode = (RubyNode) rescueBody.getBody().accept(this); + } + + final RescueAnyNode rescueNode = new RescueAnyNode(context, sourceSection, bodyNode); + rescueNodes.add(rescueNode); + } + + rescueBody = rescueBody.getOptRescue(); + } + + RubyNode elsePart; + + if (node.getElse() != null) { + elsePart = (RubyNode) node.getElse().accept(this); + } else { + elsePart = new NilNode(context, sourceSection); + } + + return new TryNode(context, sourceSection, tryPart, rescueNodes.toArray(new RescueNode[rescueNodes.size()]), elsePart); + } + + @Override + public Object visitRestArgNode(org.jrubyparser.ast.RestArgNode node) { + return unimplemented(node); + } + + @Override + public Object visitRetryNode(org.jrubyparser.ast.RetryNode node) { + return new RetryNode(context, translate(node.getPosition())); + } + + @Override + public Object visitReturnNode(org.jrubyparser.ast.ReturnNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + RubyNode translatedChild; + + if (node.getValue() == null) { + translatedChild = new NilNode(context, sourceSection); + } else { + translatedChild = (RubyNode) node.getValue().accept(this); + } + + return new ReturnNode(context, sourceSection, environment.getReturnID(), translatedChild); + } + + @Override + public Object visitRootNode(org.jrubyparser.ast.RootNode node) { + return unimplemented(node); + } + + @Override + public Object visitSClassNode(org.jrubyparser.ast.SClassNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getParser().allocateReturnID(), true, true, + new UniqueMethodIdentifier()); + final ModuleTranslator classTranslator = new ModuleTranslator(context, this, newEnvironment, source); + + final MethodDefinitionNode definitionMethod = classTranslator.compileClassNode(node.getPosition(), "singleton", node.getBody()); + + final RubyNode receiverNode = (RubyNode) node.getReceiver().accept(this); + + final SingletonClassNode singletonClassNode = new SingletonClassNode(context, sourceSection, receiverNode); + + return new OpenModuleNode(context, sourceSection, singletonClassNode, definitionMethod); + } + + @Override + public Object visitSValueNode(org.jrubyparser.ast.SValueNode node) { + return node.getValue().accept(this); + } + + @Override + public Object visitSelfNode(org.jrubyparser.ast.SelfNode node) { + return new SelfNode(context, translate(node.getPosition())); + } + + @Override + public Object visitSplatNode(org.jrubyparser.ast.SplatNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + RubyNode value; + + if (node.getValue() == null) { + value = new NilNode(context, sourceSection); + } else { + value = (RubyNode) node.getValue().accept(this); + } + + return SplatCastNodeFactory.create(context, sourceSection, value); + } + + @Override + public Object visitStrNode(org.jrubyparser.ast.StrNode node) { + return new StringLiteralNode(context, translate(node.getPosition()), node.getValue()); + } + + @Override + public Object visitSuperNode(org.jrubyparser.ast.SuperNode node) { + return unimplemented(node); + } + + @Override + public Object visitSymbolNode(org.jrubyparser.ast.SymbolNode node) { + return new ObjectLiteralNode(context, translate(node.getPosition()), new RubySymbol(context.getCoreLibrary().getSymbolClass(), node.getName())); + } + + @Override + public Object visitToAryNode(org.jrubyparser.ast.ToAryNode node) { + return unimplemented(node); + } + + @Override + public Object visitTrueNode(org.jrubyparser.ast.TrueNode node) { + return new BooleanLiteralNode(context, translate(node.getPosition()), true); + } + + @Override + public Object visitUndefNode(org.jrubyparser.ast.UndefNode node) { + return unimplemented(node); + } + + @Override + public Object visitUntilNode(org.jrubyparser.ast.UntilNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + RubyNode condition; + + if (node.getCondition() == null) { + condition = new NilNode(context, sourceSection); + } else { + condition = (RubyNode) node.getCondition().accept(this); + } + + final BooleanCastNode conditionCast = BooleanCastNodeFactory.create(context, sourceSection, condition); + final NotNode conditionCastNot = new NotNode(context, sourceSection, conditionCast); + final BooleanCastNode conditionCastNotCast = BooleanCastNodeFactory.create(context, sourceSection, conditionCastNot); + + final RubyNode body = (RubyNode) node.getBody().accept(this); + + return new WhileNode(context, sourceSection, conditionCastNotCast, body); + } + + @Override + public Object visitVAliasNode(org.jrubyparser.ast.VAliasNode node) { + return unimplemented(node); + } + + @Override + public Object visitVCallNode(org.jrubyparser.ast.VCallNode node) { + final org.jrubyparser.ast.Node receiver = new org.jrubyparser.ast.SelfNode(node.getPosition()); + final org.jrubyparser.ast.Node args = null; + final org.jrubyparser.ast.Node callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), receiver, node.getName(), args); + + return callNode.accept(this); + } + + @Override + public Object visitWhenNode(org.jrubyparser.ast.WhenNode node) { + return unimplemented(node); + } + + @Override + public Object visitWhileNode(org.jrubyparser.ast.WhileNode node) { + final SourceSection sourceSection = translate(node.getPosition()); + + RubyNode condition; + + if (node.getCondition() == null) { + condition = new NilNode(context, sourceSection); + } else { + condition = (RubyNode) node.getCondition().accept(this); + } + + final BooleanCastNode conditionCast = BooleanCastNodeFactory.create(context, sourceSection, condition); + + final RubyNode body = (RubyNode) node.getBody().accept(this); + + return new WhileNode(context, sourceSection, conditionCast, body); + } + + @Override + public Object visitXStrNode(org.jrubyparser.ast.XStrNode node) { + SourceSection sourceSection = translate(node.getPosition()); + + final StringLiteralNode literal = new StringLiteralNode(context, sourceSection, node.getValue()); + + return new SystemNode(context, sourceSection, literal); + } + + @Override + public Object visitYieldNode(org.jrubyparser.ast.YieldNode node) { + final List arguments = new ArrayList<>(); + + final org.jrubyparser.ast.Node argsNode = node.getArgs(); + + if (argsNode != null) { + if (argsNode instanceof org.jrubyparser.ast.ListNode) { + arguments.addAll(((org.jrubyparser.ast.ListNode) node.getArgs()).childNodes()); + } else { + arguments.add(node.getArgs()); + } + } + + final List argumentsTranslated = new ArrayList<>(); + + for (org.jrubyparser.ast.Node argument : arguments) { + argumentsTranslated.add((RubyNode) argument.accept(this)); + } + + final RubyNode[] argumentsTranslatedArray = argumentsTranslated.toArray(new RubyNode[argumentsTranslated.size()]); + + return new YieldNode(context, translate(node.getPosition()), argumentsTranslatedArray); + } + + @Override + public Object visitZArrayNode(org.jrubyparser.ast.ZArrayNode node) { + final RubyNode[] values = new RubyNode[0]; + + return new UninitialisedArrayLiteralNode(context, translate(node.getPosition()), values); + } + + @Override + public Object visitZSuperNode(org.jrubyparser.ast.ZSuperNode node) { + return unimplemented(node); + } + + public Object visitArgumentNode(org.jrubyparser.ast.ArgumentNode node) { + return unimplemented(node); + } + + public Object visitCommentNode(org.jrubyparser.ast.CommentNode node) { + return unimplemented(node); + } + + public Object visitKeywordArgNode(org.jrubyparser.ast.KeywordArgNode node) { + return unimplemented(node); + } + + public Object visitKeywordRestArgNode(org.jrubyparser.ast.KeywordRestArgNode node) { + return unimplemented(node); + } + + public Object visitListNode(org.jrubyparser.ast.ListNode node) { + return unimplemented(node); + } + + public Object visitMethodNameNode(org.jrubyparser.ast.MethodNameNode node) { + return unimplemented(node); + } + + public Object visitOptArgNode(org.jrubyparser.ast.OptArgNode node) { + return unimplemented(node); + } + + public Object visitSyntaxNode(org.jrubyparser.ast.SyntaxNode node) { + return unimplemented(node); + } + + public Object visitImplicitNilNode(org.jrubyparser.ast.ImplicitNilNode node) { + return new NilNode(context, translate(node.getPosition())); + } + + public Object visitLambdaNode(org.jrubyparser.ast.LambdaNode node) { + // TODO(cs): code copied and modified from visitIterNode - extract common + + final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getReturnID(), false, false, new UniqueMethodIdentifier()); + final MethodTranslator methodCompiler = new MethodTranslator(context, this, newEnvironment, false, source); + + org.jrubyparser.ast.ArgsNode argsNode; + + if (node.getVar() instanceof org.jrubyparser.ast.ArgsNode) { + argsNode = (org.jrubyparser.ast.ArgsNode) node.getVar(); + } else if (node.getVar() instanceof org.jrubyparser.ast.DAsgnNode) { + final org.jrubyparser.ast.ArgumentNode arg = new org.jrubyparser.ast.ArgumentNode(node.getPosition(), ((org.jrubyparser.ast.DAsgnNode) node.getVar()).getName()); + final org.jrubyparser.ast.ListNode preArgs = new org.jrubyparser.ast.ArrayNode(node.getPosition(), arg); + argsNode = new org.jrubyparser.ast.ArgsNode(node.getPosition(), preArgs, null, null, null, null, null, null); + } else if (node.getVar() == null) { + argsNode = null; + } else { + throw new UnsupportedOperationException(); + } + + final MethodDefinitionNode definitionNode = methodCompiler.compileFunctionNode(translate(node.getPosition()), "(lambda)", argsNode, node.getBody()); + + return new LambdaNode(context, translate(node.getPosition()), definitionNode); + } + + public Object visitUnaryCallNode(org.jrubyparser.ast.UnaryCallNode node) { + final org.jrubyparser.ast.Node callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), node.getReceiver(), node.getName(), null, null); + return callNode.accept(this); + } + + protected Object unimplemented(org.jrubyparser.ast.Node node) { + context.implementationMessage("warning: %s at %s does nothing", node, node.getPosition()); + return new NilNode(context, translate(node.getPosition())); + } + + protected SourceSection translate(final org.jrubyparser.SourcePosition sourcePosition) { + try { + // TODO(cs): get an identifier + final String identifier = "(identifier)"; + + // TODO(cs): work out the start column + final int startColumn = -1; + + final int charLength = sourcePosition.getEndOffset() - sourcePosition.getStartOffset(); + + return new DefaultSourceSection(source, identifier, sourcePosition.getStartLine() + 1, startColumn, sourcePosition.getStartOffset(), charLength); + } catch (UnsupportedOperationException e) { + // In some circumstances JRuby can't tell you what the position is + return translate(new org.jrubyparser.SourcePosition("(unknown)", 0, 0)); + } + } + + protected SequenceNode initFlipFlopStates(SourceSection sourceSection) { + final RubyNode[] initNodes = new RubyNode[environment.getFlipFlopStates().size()]; + + for (int n = 0; n < initNodes.length; n++) { + initNodes[n] = new InitFlipFlopSlotNode(context, sourceSection, environment.getFlipFlopStates().get(n)); + } + + return new SequenceNode(context, sourceSection, initNodes); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/TranslatorEnvironment.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/TranslatorEnvironment.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.parser; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.nodes.*; +import com.oracle.truffle.ruby.nodes.methods.locals.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +public class TranslatorEnvironment { + + private final RubyContext context; + + private final FrameDescriptor frameDescriptor; + + private final List preParameters = new ArrayList<>(); + + private final List optionalParameters = new ArrayList<>(); + private final Map optionalParametersDefaultValues = new HashMap<>(); + + private final List postParameters = new ArrayList<>(); + + private final List flipFlopStates = new ArrayList<>(); + + private FrameSlot restParameter = null; + + private FrameSlot blockParameter = null; + + private JRubyParser parser; + private final long returnID; + + private final boolean ownScopeForAssignments; + private final boolean neverAssignInParentScope; + + protected final TranslatorEnvironment parent; + private String methodName = ""; + private boolean needsDeclarationFrame = false; + private UniqueMethodIdentifier methodIdentifier; + + private int tempIndex; + + public TranslatorEnvironment(RubyContext context, TranslatorEnvironment parent, FrameDescriptor frameDescriptor, JRubyParser parser, long returnID, boolean ownScopeForAssignments, + boolean neverAssignInParentScope, UniqueMethodIdentifier methodIdentifier) { + this.context = context; + this.parent = parent; + this.frameDescriptor = frameDescriptor; + this.parser = parser; + this.returnID = returnID; + this.ownScopeForAssignments = ownScopeForAssignments; + this.neverAssignInParentScope = neverAssignInParentScope; + this.methodIdentifier = methodIdentifier; + } + + public TranslatorEnvironment(RubyContext context, TranslatorEnvironment parent, JRubyParser parser, long returnID, boolean ownScopeForAssignments, boolean neverAssignInParentScope, + UniqueMethodIdentifier methodIdentifier) { + this(context, parent, new FrameDescriptor(RubyFrameTypeConversion.getInstance()), parser, returnID, ownScopeForAssignments, neverAssignInParentScope, methodIdentifier); + } + + public int getLocalVarCount() { + return getFrameDescriptor().getSize(); + } + + public TranslatorEnvironment getParent() { + return parent; + } + + public List getPreParameters() { + return preParameters; + } + + public List getOptionalParameters() { + return optionalParameters; + } + + public Map getOptionalParametersDefaultValues() { + return optionalParametersDefaultValues; + } + + public List getPostParameters() { + return postParameters; + } + + public TranslatorEnvironment getParent(int level) { + assert level >= 0; + if (level == 0) { + return this; + } else { + return parent.getParent(level - 1); + } + } + + public FrameSlot declareVar(String name) { + return getFrameDescriptor().findOrAddFrameSlot(name); + } + + public UniqueMethodIdentifier findMethodForLocalVar(String name) { + TranslatorEnvironment current = this; + do { + FrameSlot slot = current.getFrameDescriptor().findFrameSlot(name); + if (slot != null) { + return current.methodIdentifier; + } + + current = current.parent; + } while (current != null); + + return null; + } + + public RubyNode findLocalVarNode(String name, SourceSection sourceSection) { + TranslatorEnvironment current = this; + int level = -1; + try { + do { + level++; + FrameSlot slot = current.getFrameDescriptor().findFrameSlot(name); + if (slot != null) { + if (level == 0) { + return ReadLocalVariableNodeFactory.create(context, sourceSection, slot); + } else { + return ReadLevelVariableNodeFactory.create(context, sourceSection, slot, level); + } + } + + current = current.parent; + } while (current != null); + } finally { + if (current != null) { + current = this; + while (level-- > 0) { + current.needsDeclarationFrame = true; + current = current.parent; + } + } + } + + return null; + } + + public void setRestParameter(FrameSlot restParameter) { + this.restParameter = restParameter; + } + + public FrameSlot getRestParameter() { + return restParameter; + } + + public void setBlockParameter(FrameSlot blockParameter) { + this.blockParameter = blockParameter; + } + + public FrameSlot getBlockParameter() { + return blockParameter; + } + + public void declareFunction(String name) { + declareVar(name); + } + + public String getMethodName() { + return methodName; + } + + public void setMethodName(String methodName) { + this.methodName = methodName; + } + + public void setNeedsDeclarationFrame() { + needsDeclarationFrame = true; + } + + public boolean needsDeclarationFrame() { + return needsDeclarationFrame; + } + + public FrameDescriptor getFrameDescriptor() { + return frameDescriptor; + } + + public String allocateLocalTemp() { + final String name = "rubytruffle_temp" + tempIndex; + tempIndex++; + declareVar(name); + return name; + } + + public long getReturnID() { + return returnID; + } + + public JRubyParser getParser() { + return parser; + } + + public boolean hasOwnScopeForAssignments() { + return ownScopeForAssignments; + } + + public boolean getNeverAssignInParentScope() { + return neverAssignInParentScope; + } + + public void addMethodDeclarationSlots() { + frameDescriptor.addFrameSlot(RubyModule.VISIBILITY_FRAME_SLOT_ID); + frameDescriptor.addFrameSlot(RubyModule.MODULE_FUNCTION_FLAG_FRAME_SLOT_ID); + } + + public UniqueMethodIdentifier getUniqueMethodIdentifier() { + return methodIdentifier; + } + + public List getFlipFlopStates() { + return flipFlopStates; + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/.checkstyle_checks.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/.checkstyle_checks.xml Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/InputReader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/InputReader.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime; + +import java.io.*; + +/** + * Interface allowing Ruby {@code Kernel#gets} to be configured to use the standard Java readLine, + * some library like JLine, or to be mocked for testing. + */ +public interface InputReader { + + /** + * Show a prompt and read one line of input. + */ + String readLine(String prompt) throws IOException; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/NilPlaceholder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/NilPlaceholder.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime; + +/** + * Represents the Ruby {@code Nil} object, but without being a full Ruby object. This allows us to + * have a simple values that is {@code nil}, but more readily available than the particular instance + * for a context. + */ +public final class NilPlaceholder { + + public static final NilPlaceholder INSTANCE = new NilPlaceholder(); + + private NilPlaceholder() { + } + + @Override + public String toString() { + return ""; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyArguments.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyArguments.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Arguments and other context passed to a Ruby method. Includes the central Ruby context object, + * optionally the scope at the point of declaration (forming a closure), the value of self, a passed + * block, and the formal arguments. + */ +public final class RubyArguments extends Arguments { + + private final MaterializedFrame declarationFrame; + private final Object self; + private final RubyProc block; + private final Object[] arguments; + + public RubyArguments(MaterializedFrame declarationFrame, Object self, RubyProc block, Object... arguments) { + assert self != null; + assert arguments != null; + + this.declarationFrame = declarationFrame; + this.self = self; + this.block = block; + this.arguments = arguments; + } + + public MaterializedFrame getDeclarationFrame() { + return declarationFrame; + } + + /** + * Get the declaration frame a certain number of levels up from the current frame, where the + * current frame is 0. + */ + public static MaterializedFrame getDeclarationFrame(VirtualFrame frame, int level) { + assert level > 0; + + MaterializedFrame parentFrame = frame.getArguments(RubyArguments.class).getDeclarationFrame(); + return getDeclarationFrame(parentFrame, level - 1); + } + + /** + * Get the declaration frame a certain number of levels up from the current frame, where the + * current frame is 0. + */ + @ExplodeLoop + private static MaterializedFrame getDeclarationFrame(MaterializedFrame frame, int level) { + assert frame != null; + assert level >= 0; + + MaterializedFrame parentFrame = frame; + + for (int n = 0; n < level; n++) { + parentFrame = parentFrame.getArguments(RubyArguments.class).getDeclarationFrame(); + } + + return parentFrame; + } + + public Object getSelf() { + return self; + } + + public RubyProc getBlock() { + return block; + } + + public Object[] getArguments() { + return arguments; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyContext.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime; + +import java.math.*; +import java.util.concurrent.atomic.*; + +import jnr.posix.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.ruby.runtime.configuration.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.debug.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; +import com.oracle.truffle.ruby.runtime.subsystems.*; + +/** + * The global state of a running Ruby system. + */ +public class RubyContext implements ExecutionContext { + + private final Configuration configuration; + private final RubyParser parser; + private final CoreLibrary coreLibrary; + private final FeatureManager featureManager; + private final ObjectSpaceManager objectSpaceManager; + private final TraceManager traceManager; + private final ThreadManager threadManager; + private final FiberManager fiberManager; + private final AtExitManager atExitManager; + private final RubyDebugManager debugManager; + private final SourceManager sourceManager; + + private AtomicLong nextObjectID = new AtomicLong(0); + + private String currentDirectory = System.getProperty("user.dir"); + + private POSIX posix = POSIXFactory.getPOSIX(); + + public RubyContext(RubyParser parser) { + this(new Configuration(new ConfigurationBuilder()), parser); + } + + public RubyContext(Configuration configuration, RubyParser parser) { + assert configuration != null; + + this.configuration = configuration; + this.parser = parser; + + objectSpaceManager = new ObjectSpaceManager(this); + traceManager = new TraceManager(this); + + // See note in CoreLibrary#initialize to see why we need to break this into two statements + coreLibrary = new CoreLibrary(this); + coreLibrary.initialize(); + + featureManager = new FeatureManager(this); + atExitManager = new AtExitManager(); + sourceManager = new SourceManager(); + + debugManager = configuration.getDebug() ? new RubyDebugManager(this) : null; + + // Must initialize threads before fibers + + threadManager = new ThreadManager(this); + + if (configuration.getRubyVersion().is19OrLater()) { + fiberManager = new FiberManager(this); + } else { + fiberManager = null; + } + } + + public String getLanguageShortName() { + return configuration.getRubyVersion().getShortName(); + } + + public RubyDebugManager getDebugManager() { + return debugManager; + } + + public void implementationMessage(String format, Object... arguments) { + System.err.println("rubytruffle: " + String.format(format, arguments)); + } + + public void load(Source source) { + execute(this, source, RubyParser.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null); + } + + public void loadFile(String fileName) { + final Source source = sourceManager.get(fileName); + final String code = source.getCode(); + if (code == null) { + throw new RuntimeException("Can't read file " + fileName); + } + execute(this, source, RubyParser.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null); + } + + /** + * Receives runtime notification that execution has halted. + */ + public void haltedAt(Node node, MaterializedFrame frame) { + runShell(node, frame); + } + + public Object eval(String code) { + final Source source = sourceManager.get("(eval)", code); + return execute(this, source, RubyParser.ParserContext.TOP_LEVEL, coreLibrary.getMainObject(), null); + } + + public Object eval(String code, RubyModule module) { + final Source source = sourceManager.get("(eval)", code); + return execute(this, source, RubyParser.ParserContext.MODULE, module, null); + } + + public Object eval(String code, RubyBinding binding) { + final Source source = sourceManager.get("(eval)", code); + return execute(this, source, RubyParser.ParserContext.TOP_LEVEL, binding.getSelf(), binding.getFrame()); + } + + public void runShell(Node node, MaterializedFrame frame) { + MaterializedFrame existingLocals = frame; + + String prompt = "Ruby> "; + if (node != null) { + final SourceSection src = node.getSourceSection(); + if (src != null) { + prompt = (src.getSource().getName() + ":" + src.getStartLine() + "> "); + } + } + + while (true) { + try { + final String line = configuration.getInputReader().readLine(prompt); + + final ShellResult result = evalShell(line, existingLocals); + + configuration.getStandardOut().println("=> " + result.getResult()); + + existingLocals = result.getFrame(); + } catch (BreakShellException e) { + return; + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public ShellResult evalShell(String code, MaterializedFrame existingLocals) { + final Source source = sourceManager.get("(shell)", code); + return (ShellResult) execute(this, source, RubyParser.ParserContext.SHELL, coreLibrary.getMainObject(), existingLocals); + } + + public Object execute(RubyContext context, Source source, RubyParser.ParserContext parserContext, Object self, MaterializedFrame parentFrame) { + if (configuration.getPrintExecutedFiles()) { + implementationMessage("executing: %s", source.getName()); + } + + try { + final RubyParserResult parseResult = parser.parse(context, source, parserContext, parentFrame); + final RubyArguments arguments = new RubyArguments(parentFrame, self, null); + final CallTarget callTarget = Truffle.getRuntime().createCallTarget(parseResult.getRootNode(), parseResult.getFrameDescriptor()); + + return callTarget.call(null, arguments); + } catch (RaiseException e) { + throw e; + } catch (ThrowException e) { + if (context.getConfiguration().getRubyVersion().is18OrEarlier()) { + throw new RaiseException(context.getCoreLibrary().nameErrorUncaughtThrow(e.getTag())); + } else { + throw new RaiseException(context.getCoreLibrary().argumentErrorUncaughtThrow(e.getTag())); + } + } catch (BreakShellException | QuitException e) { + throw e; + } catch (Throwable e) { + throw new RaiseException(ExceptionTranslator.translateException(this, e)); + } + } + + public long getNextObjectID() { + // TODO(CS): We can theoretically run out of long values + + final long id = nextObjectID.getAndIncrement(); + + if (id < 0) { + nextObjectID.set(Long.MIN_VALUE); + throw new RuntimeException("Object IDs exhausted"); + } + + return id; + } + + public void shutdown() { + atExitManager.run(); + + threadManager.leaveGlobalLock(); + + objectSpaceManager.shutdown(); + + if (fiberManager != null) { + fiberManager.shutdown(); + } + } + + public RubyString makeString(String string) { + return new RubyString(coreLibrary.getStringClass(), string); + } + + public RubyString makeString(char string) { + return makeString(Character.toString(string)); + } + + public Configuration getConfiguration() { + return configuration; + } + + public CoreLibrary getCoreLibrary() { + return coreLibrary; + } + + public FeatureManager getFeatureManager() { + return featureManager; + } + + public ObjectSpaceManager getObjectSpaceManager() { + return objectSpaceManager; + } + + public TraceManager getTraceManager() { + return traceManager; + } + + public FiberManager getFiberManager() { + return fiberManager; + } + + public ThreadManager getThreadManager() { + return threadManager; + } + + public RubyParser getParser() { + return parser; + } + + /** + * Utility method to check if an object should be visible in a Ruby program. Used in assertions + * at method boundaries to check that only values we want to be visible to the programmer become + * so. + */ + public static boolean shouldObjectBeVisible(Object object) { + // TODO(cs): RubyMethod should never be visible + + return object instanceof UndefinedPlaceholder || // + object instanceof Boolean || // + object instanceof Integer || // + object instanceof BigInteger || // + object instanceof Double || // + object instanceof RubyBasicObject || // + object instanceof RubyMethod || // + object instanceof NilPlaceholder || // + object instanceof RubyMethod; + } + + public static boolean shouldObjectsBeVisible(Object... objects) { + for (Object object : objects) { + if (!shouldObjectBeVisible(object)) { + return false; + } + } + + return true; + } + + public void setCurrentDirectory(String currentDirectory) { + this.currentDirectory = currentDirectory; + } + + public String getCurrentDirectory() { + return currentDirectory; + } + + public POSIX getPOSIX() { + return posix; + } + + public AtExitManager getAtExitManager() { + return atExitManager; + } + + public SourceManager getSourceManager() { + return sourceManager; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyParser.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyParser.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; + +/** + * Interface to a Ruby parser. + */ +public interface RubyParser { + + public static enum ParserContext { + TOP_LEVEL, SHELL, MODULE + } + + RubyParserResult parse(RubyContext context, Source source, ParserContext parserContext, MaterializedFrame parentFrame); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyParserResult.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/RubyParserResult.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * The result of parsing Ruby code is a root node and a frame descriptor for the method in that + * root. The root node will always be a {@code RubyRootNode}, but this package is below the nodes + * package so currently cannot refer to it. + */ +public class RubyParserResult { + + private RootNode rootNode; + private FrameDescriptor frameDescriptor; + + public RubyParserResult(RootNode rootNode, FrameDescriptor frameDescriptor) { + assert rootNode != null; + assert frameDescriptor != null; + + this.rootNode = rootNode; + this.frameDescriptor = frameDescriptor; + } + + public RootNode getRootNode() { + return rootNode; + } + + public FrameDescriptor getFrameDescriptor() { + return frameDescriptor; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/ShellResult.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/ShellResult.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime; + +import com.oracle.truffle.api.frame.*; + +/** + * The result of executing a line in a shell is a result value and the final frame containing any + * local variables set. + */ +public class ShellResult { + + private final Object result; + private final MaterializedFrame frame; + + public ShellResult(Object result, MaterializedFrame frame) { + assert RubyContext.shouldObjectBeVisible(result); + assert frame != null; + + this.result = result; + this.frame = frame; + } + + public Object getResult() { + return result; + } + + public MaterializedFrame getFrame() { + return frame; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/UndefinedPlaceholder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/UndefinedPlaceholder.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime; + +/** + * The {@link UndefinedPlaceholder} is a value that represents an undefined value in Ruby. This is + * used to differentiate between nil and the true absence of a value, such as an argument that has + * not been passed. + */ +public final class UndefinedPlaceholder { + + public static final UndefinedPlaceholder INSTANCE = new UndefinedPlaceholder(); + + private UndefinedPlaceholder() { + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/configuration/Configuration.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/configuration/Configuration.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.configuration; + +import java.io.*; + +import com.oracle.truffle.ruby.runtime.*; + +/** + * Configurable, immutable global parameters for Ruby. + */ +public class Configuration { + + private final String standardLibrary; + + private final RubyVersion rubyVersion; + + private final boolean verbose; + private final int warningLevel; + private final int taintCheckLevel; + + private final String defaultExternalEncoding; + private final String defaultInternalEncoding; + + private final boolean debug; + private final boolean trace; + private final boolean fullObjectSpace; + + private final boolean printParseTree; + private final boolean printExecutedFiles; + private final boolean printSpiltInstanceVariables; + private final boolean printUninitializedCalls; + private final boolean printJavaExceptions; + private final boolean printRubyExceptions; + + private final PrintStream standardOut; + private final InputReader inputReader; + + public Configuration(ConfigurationBuilder builder) { + assert builder != null; + + standardLibrary = builder.getStandardLibrary(); + + rubyVersion = builder.getRubyVersion(); + + verbose = builder.getVerbose(); + warningLevel = builder.getWarningLevel(); + taintCheckLevel = builder.getTaintCheckLevel(); + + defaultExternalEncoding = builder.getDefaultExternalEncoding(); + defaultInternalEncoding = builder.getDefaultInternalEncoding(); + + debug = builder.getDebug(); + trace = builder.getTrace(); + fullObjectSpace = builder.getFullObjectSpace(); + + printParseTree = builder.getPrintParseTree(); + printExecutedFiles = builder.getPrintExecutedFiles(); + printSpiltInstanceVariables = builder.getPrintSpiltInstanceVariables(); + printUninitializedCalls = builder.getPrintUninitializedCalls(); + printJavaExceptions = builder.getPrintJavaExceptions(); + printRubyExceptions = builder.getPrintRubyExceptions(); + + standardOut = builder.getStandardOut(); + inputReader = builder.getInputReader(); + } + + public String getStandardLibrary() { + return standardLibrary; + } + + public RubyVersion getRubyVersion() { + return rubyVersion; + } + + public boolean getDebug() { + return debug; + } + + public boolean getVerbose() { + return verbose; + } + + public int getWarningLevel() { + return warningLevel; + } + + public int getTaintCheckLevel() { + return taintCheckLevel; + } + + public String getDefaultExternalEncoding() { + return defaultExternalEncoding; + } + + public String getDefaultInternalEncoding() { + return defaultInternalEncoding; + } + + public boolean getTrace() { + return trace; + } + + public boolean getFullObjectSpace() { + return fullObjectSpace; + } + + public boolean getPrintParseTree() { + return printParseTree; + } + + public boolean getPrintExecutedFiles() { + return printExecutedFiles; + } + + public boolean getPrintSpiltInstanceVariables() { + return printSpiltInstanceVariables; + } + + public boolean getPrintUninitializedCalls() { + return printUninitializedCalls; + } + + public boolean getPrintJavaExceptions() { + return printJavaExceptions; + } + + public boolean getPrintRubyExceptions() { + return printRubyExceptions; + } + + public PrintStream getStandardOut() { + return standardOut; + } + + public InputReader getInputReader() { + return inputReader; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/configuration/ConfigurationBuilder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/configuration/ConfigurationBuilder.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.configuration; + +import java.io.*; + +import com.oracle.truffle.ruby.runtime.*; + +/** + * The mutable counterpart to {@link Configuration}. + */ +public class ConfigurationBuilder { + + /** + * The path of the JRuby packaging of the Ruby standard library within our source tree. + */ + public static final String JRUBY_STDLIB_JAR = "lib/jruby-stdlib-1.7.4.jar"; + + private String standardLibrary = JRUBY_STDLIB_JAR; + + private RubyVersion rubyVersion = RubyVersion.RUBY_19; + + private boolean debug = true; + private boolean verbose = false; + private int warningLevel = 0; + private int taintCheckLevel = 0; + + private String defaultExternalEncoding = null; + private String defaultInternalEncoding = null; + + private boolean trace = true; + private boolean fullObjectSpace = false; + + private boolean printParseTree = false; + private boolean printExecutedFiles = false; + private boolean printSpiltInstanceVariables = false; + private boolean printUninitializedCalls = false; + private boolean printJavaExceptions = false; + private boolean printRubyExceptions = false; + + private PrintStream standardOut = System.out; + + private InputReader inputReader = new InputReader() { + + private final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + + @Override + public String readLine(String prompt) throws IOException { + System.err.print(prompt); + return reader.readLine(); + } + + }; + + public ConfigurationBuilder() { + } + + public ConfigurationBuilder(Configuration configuration) { + assert configuration != null; + + standardLibrary = configuration.getStandardLibrary(); + + rubyVersion = configuration.getRubyVersion(); + + debug = configuration.getDebug(); + verbose = configuration.getVerbose(); + warningLevel = configuration.getWarningLevel(); + taintCheckLevel = configuration.getTaintCheckLevel(); + + defaultExternalEncoding = configuration.getDefaultExternalEncoding(); + defaultInternalEncoding = configuration.getDefaultInternalEncoding(); + + trace = configuration.getTrace(); + fullObjectSpace = configuration.getFullObjectSpace(); + + printParseTree = configuration.getPrintParseTree(); + printExecutedFiles = configuration.getPrintExecutedFiles(); + printSpiltInstanceVariables = configuration.getPrintSpiltInstanceVariables(); + printUninitializedCalls = configuration.getPrintUninitializedCalls(); + printJavaExceptions = configuration.getPrintJavaExceptions(); + printRubyExceptions = configuration.getPrintRubyExceptions(); + + standardOut = configuration.getStandardOut(); + } + + public String getStandardLibrary() { + return standardLibrary; + } + + public void setStandardLibrary(String standardLibrary) { + assert standardLibrary != null; + this.standardLibrary = standardLibrary; + } + + public RubyVersion getRubyVersion() { + return rubyVersion; + } + + public void setRubyVersion(RubyVersion rubyVersion) { + assert rubyVersion != null; + this.rubyVersion = rubyVersion; + } + + public boolean getDebug() { + return debug; + } + + public void setDebug(boolean debug) { + this.debug = debug; + } + + public boolean getVerbose() { + return verbose; + } + + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } + + public int getWarningLevel() { + return warningLevel; + } + + public void setWarningLevel(int warningLevel) { + this.warningLevel = warningLevel; + } + + public int getTaintCheckLevel() { + return taintCheckLevel; + } + + public void setTaintCheckLevel(int taintCheckLevel) { + this.taintCheckLevel = taintCheckLevel; + } + + public String getDefaultExternalEncoding() { + return defaultExternalEncoding; + } + + public void setDefaultExternalEncoding(String defaultExternalEncoding) { + assert defaultExternalEncoding != null; + this.defaultExternalEncoding = defaultExternalEncoding; + } + + public String getDefaultInternalEncoding() { + return defaultInternalEncoding; + } + + public void setDefaultInternalEncoding(String defaultInternalEncoding) { + assert defaultInternalEncoding != null; + this.defaultInternalEncoding = defaultInternalEncoding; + } + + public boolean getTrace() { + return trace; + } + + public void setTrace(boolean trace) { + this.trace = trace; + } + + public boolean getFullObjectSpace() { + return fullObjectSpace; + } + + public void setFullObjectSpace(boolean fullObjectSpace) { + this.fullObjectSpace = fullObjectSpace; + } + + public boolean getPrintParseTree() { + return printParseTree; + } + + public void setPrintParseTree(boolean printParseTree) { + this.printParseTree = printParseTree; + } + + public boolean getPrintExecutedFiles() { + return printExecutedFiles; + } + + public void setPrintExecutedFiles(boolean printExecutedFiles) { + this.printExecutedFiles = printExecutedFiles; + } + + public boolean getPrintSpiltInstanceVariables() { + return printSpiltInstanceVariables; + } + + public void setPrintSpiltInstanceVariables(boolean printSpiltInstanceVariables) { + this.printSpiltInstanceVariables = printSpiltInstanceVariables; + } + + public boolean getPrintUninitializedCalls() { + return printUninitializedCalls; + } + + public void setPrintUninitializedCalls(boolean printUninitializedCalls) { + this.printUninitializedCalls = printUninitializedCalls; + } + + public boolean getPrintJavaExceptions() { + return printJavaExceptions; + } + + public void setPrintJavaExceptions(boolean printJavaExceptions) { + this.printJavaExceptions = printJavaExceptions; + } + + public boolean getPrintRubyExceptions() { + return printRubyExceptions; + } + + public void setPrintRubyExceptions(boolean printRubyExceptions) { + this.printRubyExceptions = printRubyExceptions; + } + + public PrintStream getStandardOut() { + return standardOut; + } + + public void setStandardOut(PrintStream standardOut) { + assert standardOut != null; + this.standardOut = standardOut; + } + + public InputReader getInputReader() { + return inputReader; + } + + public void setInputReader(InputReader lineReader) { + assert lineReader != null; + this.inputReader = lineReader; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/configuration/RubyVersion.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/configuration/RubyVersion.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.configuration; + +/** + * A Ruby version that we want to be compatible with. + */ +public enum RubyVersion { + RUBY_18("1.8.7", 374), RUBY_19("1.9.3", 448), RUBY_20("2.0.0", 247), RUBY_21("2.1.0", 0); + + private final String version; + private final int patch; + + private RubyVersion(String version, int patch) { + this.version = version; + this.patch = patch; + } + + public boolean is18OrEarlier() { + return this.compareTo(RUBY_18) <= 0; + } + + public boolean is19OrLater() { + return this.compareTo(RUBY_19) >= 0; + } + + public String getVersion() { + return version; + } + + public int getPatch() { + return patch; + } + + public String getShortName() { + return "Ruby" + getVersion(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/BreakException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/BreakException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.control; + +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Controls a break from a control structure or method. + */ +public final class BreakException extends ControlFlowException { + + private final Object result; + + public BreakException(Object result) { + assert RubyContext.shouldObjectBeVisible(result); + + this.result = result; + } + + public Object getResult() { + return result; + } + + private static final long serialVersionUID = -8650123232850256133L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/BreakShellException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/BreakShellException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.control; + +import com.oracle.truffle.api.nodes.*; + +/** + * Controls breaking out of a shell. + */ +public final class BreakShellException extends ControlFlowException { + + private static final long serialVersionUID = 6448983812696841922L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/ContinuationReturnException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/ContinuationReturnException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.control; + +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Controls return from a continuation. + */ +public final class ContinuationReturnException extends ControlFlowException { + + private final RubyContinuation continuation; + private final Object value; + + public ContinuationReturnException(RubyContinuation continuation, Object value) { + assert continuation != null; + assert RubyContext.shouldObjectBeVisible(value); + + this.continuation = continuation; + this.value = value; + } + + /** + * Get the continuation that caused this. + */ + public RubyContinuation getContinuation() { + return continuation; + } + + /** + * Get the value that has been returned. + */ + public Object getValue() { + return value; + } + + private static final long serialVersionUID = 6215834704293311504L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/ExceptionTranslator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/ExceptionTranslator.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.control; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +public final class ExceptionTranslator { + + /** + * Translate a Java exception into a Ruby exception. + */ + public static RubyBasicObject translateException(RubyContext context, Throwable exception) { + assert context != null; + assert exception != null; + + CompilerAsserts.neverPartOfCompilation(); + + // RaiseException already includes the Ruby exception + + if (exception instanceof RaiseException) { + return ((RaiseException) exception).getRubyException(); + } + + // Translate divide by zero into ZeroDivisionError + + if (exception instanceof ArithmeticException && (exception.getMessage().endsWith("divide by zero") || exception.getMessage().endsWith("/ by zero"))) { + return new RubyException(context.getCoreLibrary().getZeroDivisionErrorClass(), "divided by 0"); + } + + /* + * If we can't translate the exception into a Ruby exception, then the error is ours and we + * report it as as RubyTruffleError. If a programmer sees this then it's a bug in our + * implementation. + */ + + if (context.getConfiguration().getPrintJavaExceptions()) { + exception.printStackTrace(); + } + + String message; + + if (exception.getMessage() == null) { + message = exception.getClass().getSimpleName(); + } else { + message = exception.getMessage(); + } + + return new RubyException(context.getCoreLibrary().getRubyTruffleErrorClass(), message); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/NextException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/NextException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.control; + +import com.oracle.truffle.api.nodes.*; + +/** + * Controls moving to the next iteration in a control structure or method. + */ +public final class NextException extends ControlFlowException { + + private static final long serialVersionUID = -302759969186731457L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/QuitException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/QuitException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.control; + +import com.oracle.truffle.api.nodes.*; + +/** + * Controls breaking out of all executions and ending Ruby execution. + */ +public final class QuitException extends ControlFlowException { + + private static final long serialVersionUID = -3568511099628564190L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/RaiseException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/RaiseException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.control; + +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Ruby exceptions are just Ruby objects, so they cannot also be exceptions unless we made all Ruby + * objects exceptions. A simpler approach is to wrap Ruby exceptions in Java exceptions when we want + * to throw them. The error messages match MRI. Note that throwing is different to raising in Ruby, + * which is the reason we have both {@link ThrowException} and {@link RaiseException}. + */ +public class RaiseException extends RuntimeException { + + private final RubyBasicObject rubyException; + + public RaiseException(RubyBasicObject rubyException) { + this.rubyException = rubyException; + } + + @Override + public String toString() { + return rubyException.toString(); + } + + @Override + public String getMessage() { + return rubyException.toString(); + } + + public RubyBasicObject getRubyException() { + return rubyException; + } + + private static final long serialVersionUID = 7501185855599094740L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/RedoException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/RedoException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.control; + +import com.oracle.truffle.api.nodes.*; + +/** + * Controls re-doing an iteration in a control structure or method. + */ +public final class RedoException extends ControlFlowException { + + private static final long serialVersionUID = -4717868827111714052L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/RetryException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/RetryException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.control; + +import com.oracle.truffle.api.nodes.*; + +/** + * Controls re-trying an iteration in a control structure or method. + */ +public final class RetryException extends ControlFlowException { + + private static final long serialVersionUID = -1675586631300635765L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/ReturnException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/ReturnException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.control; + +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Controls an explicit return from a method. + */ +public final class ReturnException extends ControlFlowException { + + private final long returnID; + private final Object value; + + public ReturnException(long returnID, Object value) { + assert RubyContext.shouldObjectBeVisible(value); + + this.returnID = returnID; + this.value = value; + } + + /** + * Return the return ID of this return that identifies where it intends to return to. + */ + public long getReturnID() { + return returnID; + } + + /** + * Get the value that has been returned. + */ + public Object getValue() { + return value; + } + + private static final long serialVersionUID = -9177536212065610691L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/ThrowException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/control/ThrowException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.control; + +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Controls throwing a value. Note that throwing is different to raising in Ruby, which is the + * reason we have both {@link ThrowException} and {@link RaiseException}. + */ +public class ThrowException extends ControlFlowException { + + private final Object tag; + private final Object value; + + public ThrowException(Object tag, Object value) { + assert tag != null; + assert RubyContext.shouldObjectBeVisible(value); + + this.tag = tag; + this.value = value; + } + + public Object getTag() { + return tag; + } + + public Object getValue() { + return value; + } + + private static final long serialVersionUID = 8693305627979840677L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/CoreLibrary.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/CoreLibrary.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,649 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.io.*; +import java.math.*; +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +public class CoreLibrary { + + private final RubyContext context; + + private RubyClass argumentErrorClass; + private RubyClass arrayClass; + private RubyClass basicObjectClass; + private RubyClass bignumClass; + private RubyClass bindingClass; + private RubyClass classClass; + private RubyClass continuationClass; + private RubyClass dirClass; + private RubyClass exceptionClass; + private RubyClass falseClass; + private RubyClass fiberClass; + private RubyClass fileClass; + private RubyClass fixnumClass; + private RubyClass floatClass; + private RubyClass hashClass; + private RubyClass integerClass; + private RubyClass ioClass; + private RubyClass loadErrorClass; + private RubyClass localJumpErrorClass; + private RubyClass matchDataClass; + private RubyClass moduleClass; + private RubyClass nameErrorClass; + private RubyClass nilClass; + private RubyClass noMethodErrorClass; + private RubyClass numericClass; + private RubyClass objectClass; + private RubyClass procClass; + private RubyClass processClass; + private RubyClass rangeClass; + private RubyClass rangeErrorClass; + private RubyClass regexpClass; + private RubyClass rubyTruffleErrorClass; + private RubyClass runtimeErrorClass; + private RubyClass standardErrorClass; + private RubyClass stringClass; + private RubyClass structClass; + private RubyClass symbolClass; + private RubyClass syntaxErrorClass; + private RubyClass systemCallErrorClass; + private RubyClass systemExitClass; + private RubyClass threadClass; + private RubyClass timeClass; + private RubyClass trueClass; + private RubyClass typeErrorClass; + private RubyClass zeroDivisionErrorClass; + + private RubyModule comparableModule; + private RubyModule configModule; + private RubyModule errnoModule; + private RubyModule kernelModule; + private RubyModule mathModule; + private RubyModule objectSpaceModule; + private RubyModule signalModule; + + private RubyModule debugModule; + private RubyArray argv; + private RubyBasicObject globalVariablesObject; + private RubyBasicObject mainObject; + private RubyFalseClass falseObject; + private RubyNilClass nilObject; + private RubyTrueClass trueObject; + + public CoreLibrary(RubyContext context) { + this.context = context; + } + + public void initialize() { + // Create the cyclic classes and modules + + classClass = new RubyClass.RubyClassClass(context); + basicObjectClass = new RubyClass(context, classClass, null, null, "BasicObject"); + objectClass = new RubyClass(null, basicObjectClass, "Object"); + moduleClass = new RubyModule.RubyModuleClass(context); + + // Close the cycles + + moduleClass.unsafeSetRubyClass(classClass); + classClass.unsafeSetSuperclass(moduleClass); + moduleClass.unsafeSetSuperclass(objectClass); + classClass.unsafeSetRubyClass(classClass); + + // Create all other classes and modules + + numericClass = new RubyClass(null, objectClass, "Numeric"); + integerClass = new RubyClass(null, numericClass, "Integer"); + + exceptionClass = new RubyException.RubyExceptionClass(objectClass, "Exception"); + standardErrorClass = new RubyException.RubyExceptionClass(exceptionClass, "StandardError"); + + ioClass = new RubyClass(null, objectClass, "IO"); + + argumentErrorClass = new RubyException.RubyExceptionClass(standardErrorClass, "ArgumentError"); + arrayClass = new RubyArray.RubyArrayClass(objectClass); + bignumClass = new RubyClass(null, integerClass, "Bignum"); + bindingClass = new RubyClass(null, objectClass, "Binding"); + continuationClass = new RubyClass(null, objectClass, "Continuation"); + comparableModule = new RubyModule(moduleClass, null, "Comparable"); + configModule = new RubyModule(moduleClass, null, "Config"); + debugModule = new RubyModule(moduleClass, null, "Debug"); + dirClass = new RubyClass(null, objectClass, "Dir"); + errnoModule = new RubyModule(moduleClass, null, "Errno"); + falseClass = new RubyClass(null, objectClass, "FalseClass"); + fiberClass = new RubyFiber.RubyFiberClass(objectClass); + fileClass = new RubyClass(null, ioClass, "File"); + fixnumClass = new RubyClass(null, integerClass, "Fixnum"); + floatClass = new RubyClass(null, objectClass, "Float"); + hashClass = new RubyHash.RubyHashClass(objectClass); + kernelModule = new RubyModule(moduleClass, null, "Kernel"); + loadErrorClass = new RubyException.RubyExceptionClass(standardErrorClass, "LoadError"); + localJumpErrorClass = new RubyException.RubyExceptionClass(standardErrorClass, "LocalJumpError"); + matchDataClass = new RubyClass(null, objectClass, "MatchData"); + mathModule = new RubyModule(moduleClass, null, "Math"); + nameErrorClass = new RubyClass(null, standardErrorClass, "NameError"); + nilClass = new RubyClass(null, objectClass, "NilClass"); + noMethodErrorClass = new RubyException.RubyExceptionClass(standardErrorClass, "NoMethodError"); + objectSpaceModule = new RubyModule(moduleClass, null, "ObjectSpace"); + procClass = new RubyProc.RubyProcClass(objectClass); + processClass = new RubyClass(null, objectClass, "Process"); + rangeClass = new RubyClass(null, objectClass, "Range"); + rangeErrorClass = new RubyException.RubyExceptionClass(standardErrorClass, "RangeError"); + regexpClass = new RubyRegexp.RubyRegexpClass(objectClass); + rubyTruffleErrorClass = new RubyException.RubyExceptionClass(standardErrorClass, "RubyTruffleError"); + runtimeErrorClass = new RubyException.RubyExceptionClass(standardErrorClass, "RuntimeError"); + stringClass = new RubyString.RubyStringClass(objectClass); + structClass = new RubyClass(null, ioClass, "Struct"); + signalModule = new RubyModule(moduleClass, null, "Signal"); + symbolClass = new RubyClass(null, objectClass, "Symbol"); + syntaxErrorClass = new RubyException.RubyExceptionClass(standardErrorClass, "SyntaxError"); + systemCallErrorClass = new RubyException.RubyExceptionClass(standardErrorClass, "SystemCallError"); + systemExitClass = new RubyException.RubyExceptionClass(exceptionClass, "SystemExit"); + threadClass = new RubyThread.RubyThreadClass(objectClass); + timeClass = new RubyTime.RubyTimeClass(objectClass); + trueClass = new RubyClass(null, objectClass, "TrueClass"); + typeErrorClass = new RubyException.RubyExceptionClass(standardErrorClass, "TypeError"); + zeroDivisionErrorClass = new RubyException.RubyExceptionClass(standardErrorClass, "ZeroDivisionError"); + + // Includes + + objectClass.include(kernelModule); + + // Set constants + + objectClass.setConstant("RUBY_VERSION", new RubyString(stringClass, context.getConfiguration().getRubyVersion().getVersion())); + objectClass.setConstant("RUBY_PATCHLEVEL", context.getConfiguration().getRubyVersion().getPatch()); + objectClass.setConstant("RUBY_ENGINE", new RubyString(stringClass, "rubytruffle")); + objectClass.setConstant("RUBY_PLATFORM", new RubyString(stringClass, "jvm")); + + argv = new RubyArray(arrayClass, new ObjectArrayStore()); + objectClass.setConstant("ARGV", argv); + objectClass.setConstant("ENV", getEnv()); + + final RubyHash configHash = new RubyHash(hashClass); + configHash.put(new RubyString(stringClass, "ruby_install_name"), new RubyString(stringClass, "rubytruffle")); + configHash.put(new RubyString(stringClass, "RUBY_INSTALL_NAME"), new RubyString(stringClass, "rubytruffle")); + configHash.put(new RubyString(stringClass, "host_os"), new RubyString(stringClass, "unknown")); + configHash.put(new RubyString(stringClass, "exeext"), new RubyString(stringClass, "")); + configHash.put(new RubyString(stringClass, "EXEEXT"), new RubyString(stringClass, "rubytruffle")); + configModule.setConstant("CONFIG", configHash); + objectClass.setConstant("RbConfig", configModule); + + mathModule.setConstant("PI", Math.PI); + + fileClass.setConstant("SEPARATOR", new RubyString(stringClass, File.separator)); + fileClass.setConstant("Separator", new RubyString(stringClass, File.separator)); + fileClass.setConstant("ALT_SEPARATOR", NilPlaceholder.INSTANCE); + fileClass.setConstant("PATH_SEPARATOR", new RubyString(stringClass, File.pathSeparator)); + fileClass.setConstant("FNM_SYSCASE", 0); + + errnoModule.setConstant("ENOENT", new RubyClass(null, systemCallErrorClass, "ENOENT")); + errnoModule.setConstant("EPERM", new RubyClass(null, systemCallErrorClass, "EPERM")); + errnoModule.setConstant("ENOTEMPTY", new RubyClass(null, systemCallErrorClass, "ENOTEMPTY")); + errnoModule.setConstant("EEXIST", new RubyClass(null, systemCallErrorClass, "EEXIST")); + errnoModule.setConstant("EXDEV", new RubyClass(null, systemCallErrorClass, "EXDEV")); + errnoModule.setConstant("EACCES", new RubyClass(null, systemCallErrorClass, "EACCES")); + + // Add all classes and modules as constants in Object + + final RubyModule[] modules = {argumentErrorClass, // + arrayClass, // + basicObjectClass, // + bignumClass, // + bindingClass, // + classClass, // + continuationClass, // + comparableModule, // + configModule, // + debugModule, // + dirClass, // + errnoModule, // + exceptionClass, // + falseClass, // + fiberClass, // + fileClass, // + fixnumClass, // + floatClass, // + hashClass, // + integerClass, // + ioClass, // + kernelModule, // + loadErrorClass, // + localJumpErrorClass, // + matchDataClass, // + mathModule, // + moduleClass, // + nameErrorClass, // + nilClass, // + noMethodErrorClass, // + numericClass, // + objectClass, // + objectSpaceModule, // + procClass, // + processClass, // + rangeClass, // + rangeErrorClass, // + regexpClass, // + rubyTruffleErrorClass, // + runtimeErrorClass, // + signalModule, // + standardErrorClass, // + stringClass, // + structClass, // + symbolClass, // + syntaxErrorClass, // + systemCallErrorClass, // + systemExitClass, // + threadClass, // + timeClass, // + trueClass, // + typeErrorClass, // + zeroDivisionErrorClass}; + + for (RubyModule module : modules) { + objectClass.setConstant(module.getName(), module); + } + + // Create some key objects + + mainObject = new RubyObject(objectClass); + nilObject = new RubyNilClass(nilClass); + trueObject = new RubyTrueClass(trueClass); + falseObject = new RubyFalseClass(falseClass); + + // Create the globals object + + globalVariablesObject = new RubyBasicObject(objectClass); + globalVariablesObject.switchToPrivateLayout(); + globalVariablesObject.setInstanceVariable("$:", new RubyArray(arrayClass, new ObjectArrayStore())); + } + + public void initializeAfterMethodsAdded() { + bignumClass.getSingletonClass().undefMethod("new"); + falseClass.getSingletonClass().undefMethod("new"); + fixnumClass.getSingletonClass().undefMethod("new"); + floatClass.getSingletonClass().undefMethod("new"); + integerClass.getSingletonClass().undefMethod("new"); + nilClass.getSingletonClass().undefMethod("new"); + numericClass.getSingletonClass().undefMethod("new"); + trueClass.getSingletonClass().undefMethod("new"); + } + + public RubyBasicObject box(Object object) { + assert RubyContext.shouldObjectBeVisible(object); + + // TODO(cs): pool common object instances like small Fixnums? + + if (object instanceof RubyBasicObject) { + return (RubyBasicObject) object; + } + + if (object instanceof Boolean) { + if ((boolean) object) { + return trueObject; + } else { + return falseObject; + } + } + + if (object instanceof Integer) { + return new RubyFixnum(fixnumClass, (int) object); + } + + if (object instanceof BigInteger) { + return new RubyBignum(bignumClass, (BigInteger) object); + } + + if (object instanceof Double) { + return new RubyFloat(floatClass, (double) object); + } + + if (object instanceof NilPlaceholder) { + return nilObject; + } + + CompilerDirectives.transferToInterpreter(); + + throw new UnsupportedOperationException("Don't know how to box " + object.getClass().getName()); + } + + public RubyException runtimeError(String message) { + return new RubyException(runtimeErrorClass, message); + } + + public RubyException frozenError(String className) { + return runtimeError(String.format("can't modify frozen %s", className)); + } + + public RubyException argumentError(String message) { + return new RubyException(argumentErrorClass, message); + } + + public RubyException argumentError(int passed, int required) { + return argumentError(String.format("wrong number of arguments (%d for %d)", passed, required)); + } + + public RubyException argumentErrorUncaughtThrow(Object tag) { + return argumentError(String.format("uncaught throw `%s'", tag)); + } + + public RubyException localJumpError(String message) { + return new RubyException(localJumpErrorClass, message); + } + + public RubyException unexpectedReturn() { + return localJumpError("unexpected return"); + } + + public RubyException typeError(String message) { + return new RubyException(typeErrorClass, message); + } + + public RubyException typeError(String from, String to) { + return typeError(String.format("can't convert %s to %s", from, to)); + } + + public RubyException typeErrorIsNotA(String value, String expectedType) { + return typeError(String.format("%s is not a %s", value, expectedType)); + } + + public RubyException typeErrorNeedsToBe(String name, String expectedType) { + return typeError(String.format("%s needs to be %s", name, expectedType)); + } + + public RubyException rangeError(String message) { + return new RubyException(rangeErrorClass, message); + } + + public RubyException nameError(String message) { + return new RubyException(nameErrorClass, message); + } + + public RubyException nameErrorUninitializedConstant(String name) { + return nameError(String.format("uninitialized constant %s", name)); + } + + public RubyException nameErrorNoMethod(String name, String object) { + return nameError(String.format("undefined local variable or method `%s' for %s", name, object)); + } + + public RubyException nameErrorInstanceNameNotAllowable(String name) { + return nameError(String.format("`%s' is not allowable as an instance variable name", name)); + } + + public RubyException nameErrorUncaughtThrow(Object tag) { + return nameError(String.format("uncaught throw `%s'", tag)); + } + + public RubyException noMethodError(String message) { + return new RubyException(context.getCoreLibrary().getNoMethodErrorClass(), message); + } + + public RubyException noMethodError(String name, String object) { + return noMethodError(String.format("undefined method `%s' for %s", name, object)); + } + + public RubyException loadError(String message) { + return new RubyException(context.getCoreLibrary().getLoadErrorClass(), message); + } + + public RubyException loadErrorCannotLoad(String name) { + return loadError(String.format("cannot load such file -- %s", name)); + } + + public RubyException zeroDivisionError() { + return new RubyException(context.getCoreLibrary().getZeroDivisionErrorClass(), "divided by 0"); + } + + public RubyContext getContext() { + return context; + } + + public RubyClass getArgumentErrorClass() { + return argumentErrorClass; + } + + public RubyClass getArrayClass() { + return arrayClass; + } + + public RubyClass getBasicObjectClass() { + return basicObjectClass; + } + + public RubyClass getBignumClass() { + return bignumClass; + } + + public RubyClass getBindingClass() { + return bindingClass; + } + + public RubyClass getClassClass() { + return classClass; + } + + public RubyModule getComparableClass() { + return comparableModule; + } + + public RubyClass getContinuationClass() { + return continuationClass; + } + + public RubyClass getDirClass() { + return dirClass; + } + + public RubyClass getExceptionClass() { + return exceptionClass; + } + + public RubyClass getFalseClass() { + return falseClass; + } + + public RubyClass getFiberClass() { + return fiberClass; + } + + public RubyClass getFileClass() { + return fileClass; + } + + public RubyClass getFixnumClass() { + return fixnumClass; + } + + public RubyClass getFloatClass() { + return floatClass; + } + + public RubyClass getHashClass() { + return hashClass; + } + + public RubyClass getIntegerClass() { + return integerClass; + } + + public RubyClass getIoClass() { + return ioClass; + } + + public RubyClass getLoadErrorClass() { + return loadErrorClass; + } + + public RubyClass getLocalJumpErrorClass() { + return localJumpErrorClass; + } + + public RubyClass getMatchDataClass() { + return matchDataClass; + } + + public RubyClass getModuleClass() { + return moduleClass; + } + + public RubyClass getNameErrorClass() { + return nameErrorClass; + } + + public RubyClass getNilClass() { + return nilClass; + } + + public RubyClass getNoMethodErrorClass() { + return noMethodErrorClass; + } + + public RubyClass getNumericClass() { + return numericClass; + } + + public RubyClass getObjectClass() { + return objectClass; + } + + public RubyClass getProcClass() { + return procClass; + } + + public RubyClass getProcessClass() { + return processClass; + } + + public RubyClass getRangeClass() { + return rangeClass; + } + + public RubyClass getRangeErrorClass() { + return rangeErrorClass; + } + + public RubyClass getRegexpClass() { + return regexpClass; + } + + public RubyClass getRubyTruffleErrorClass() { + return rubyTruffleErrorClass; + } + + public RubyClass getRuntimeErrorClass() { + return runtimeErrorClass; + } + + public RubyModule getSignalModule() { + return signalModule; + } + + public RubyClass getStandardErrorClass() { + return standardErrorClass; + } + + public RubyClass getStringClass() { + return stringClass; + } + + public RubyClass getStructClass() { + return structClass; + } + + public RubyClass getSymbolClass() { + return symbolClass; + } + + public RubyClass getSyntaxErrorClass() { + return syntaxErrorClass; + } + + public RubyClass getSystemCallErrorClass() { + return systemCallErrorClass; + } + + public RubyClass getThreadClass() { + return threadClass; + } + + public RubyClass getTimeClass() { + return timeClass; + } + + public RubyClass getTrueClass() { + return trueClass; + } + + public RubyClass getTypeErrorClass() { + return typeErrorClass; + } + + public RubyClass getZeroDivisionErrorClass() { + return zeroDivisionErrorClass; + } + + public RubyModule getKernelModule() { + return kernelModule; + } + + public RubyModule getMathModule() { + return mathModule; + } + + public RubyModule getObjectSpaceModule() { + return objectSpaceModule; + } + + public RubyModule getDebugModule() { + return debugModule; + } + + public RubyArray getArgv() { + return argv; + } + + public RubyBasicObject getGlobalVariablesObject() { + return globalVariablesObject; + } + + public RubyBasicObject getMainObject() { + return mainObject; + } + + public RubyFalseClass getFalseObject() { + return falseObject; + } + + public RubyNilClass getNilObject() { + return nilObject; + } + + public RubyTrueClass getTrueObject() { + return trueObject; + } + + public RubyHash getEnv() { + final RubyHash hash = new RubyHash(context.getCoreLibrary().getHashClass()); + + for (Map.Entry variable : System.getenv().entrySet()) { + hash.put(context.makeString(variable.getKey()), context.makeString(variable.getValue())); + } + + return hash; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/GeneralConversions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/GeneralConversions.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.math.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.runtime.*; + +public class GeneralConversions { + + /** + * Convert a value to a boolean, without doing any lookup. + */ + public static boolean toBoolean(Object value) { + assert value != null; + + if (value instanceof NilPlaceholder) { + return false; + } + + if (value instanceof Boolean) { + return (boolean) value; + } + + if (value instanceof RubyTrueClass) { + return true; + } + + if (value instanceof RubyFalseClass) { + return false; + } + + return true; + } + + /** + * Convert a value to a {@code Fixnum}, without doing any lookup. + */ + public static int toFixnum(Object value) { + assert value != null; + + if (value instanceof NilPlaceholder || value instanceof RubyNilClass) { + return 0; + } + + if (value instanceof Integer) { + return (int) value; + } + + if (value instanceof RubyFixnum) { + return ((RubyFixnum) value).getValue(); + } + + if (value instanceof BigInteger) { + throw new UnsupportedOperationException(); + } + + if (value instanceof RubyBignum) { + throw new UnsupportedOperationException(); + } + + if (value instanceof Double) { + return (int) (double) value; + } + + if (value instanceof RubyFloat) { + return (int) ((RubyFloat) value).getValue(); + } + + CompilerDirectives.transferToInterpreter(); + + throw new UnsupportedOperationException(value.getClass().toString()); + } + + /** + * Convert a value to a {@code Float}, without doing any lookup. + */ + public static double toFloat(Object value) { + assert value != null; + + if (value instanceof NilPlaceholder || value instanceof RubyNilClass) { + return 0; + } + + if (value instanceof Integer) { + return (int) value; + } + + if (value instanceof RubyFixnum) { + return ((RubyFixnum) value).getValue(); + } + + if (value instanceof BigInteger) { + return ((BigInteger) value).doubleValue(); + } + + if (value instanceof RubyBignum) { + return ((RubyBignum) value).getValue().doubleValue(); + } + + if (value instanceof Double) { + return (double) value; + } + + if (value instanceof RubyFloat) { + return ((RubyFloat) value).getValue(); + } + + CompilerDirectives.transferToInterpreter(); + + throw new UnsupportedOperationException(); + } + + /** + * Given a {@link BigInteger} value, produce either a {@code Fixnum} or {@code Bignum} . + */ + public static Object fixnumOrBignum(BigInteger value) { + assert value != null; + + if (value.compareTo(RubyFixnum.MIN_VALUE_BIG) >= 0 && value.compareTo(RubyFixnum.MAX_VALUE_BIG) <= 0) { + return value.intValue(); + } else { + return value; + } + } + + /** + * Given a {@code long} value, produce either a {@code Fixnum} or {@code Bignum} . + */ + public static Object fixnumOrBignum(long value) { + if (value >= RubyFixnum.MIN_VALUE && value <= RubyFixnum.MAX_VALUE) { + return (int) value; + } else { + return BigInteger.valueOf(value); + } + } + + /** + * Given a reference, produce either {@code nil} or the object. . + */ + public static Object instanceOrNil(Object object) { + if (object == null) { + return NilPlaceholder.INSTANCE; + } else { + return object; + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyBignum.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyBignum.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.math.*; + +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Bignum} class. + */ +public class RubyBignum extends RubyObject implements Unboxable { + + private final BigInteger value; + + public RubyBignum(RubyClass bignumClass, BigInteger value) { + super(bignumClass); + + assert value != null; + + this.value = value; + } + + public BigInteger getValue() { + return value; + } + + public Object unbox() { + return value; + } + + public static RubyArray divMod(RubyContext context, BigInteger a, BigInteger b) { + final BigInteger[] quotientRemainder = a.divideAndRemainder(b); + + final Object quotient = GeneralConversions.fixnumOrBignum(quotientRemainder[0]); + final Object remainder = GeneralConversions.fixnumOrBignum(quotientRemainder[1]); + + final ObjectImmutablePairArrayStore store = new ObjectImmutablePairArrayStore(quotient, remainder); + return new RubyArray(context.getCoreLibrary().getArrayClass(), store); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof RubyBignum)) { + return false; + } + RubyBignum other = (RubyBignum) obj; + if (value == null) { + if (other.value != null) { + return false; + } + } else if (!value.equals(other.value)) { + return false; + } + return true; + } + + @Override + public String toString() { + return value.toString(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyBinding.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyBinding.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import com.oracle.truffle.api.frame.*; + +/** + * Represents the Ruby {@code Binding} class. + */ +public class RubyBinding extends RubyObject { + + private final Object self; + private final MaterializedFrame frame; + + public RubyBinding(RubyClass bindingClass, Object self, MaterializedFrame frame) { + super(bindingClass); + + assert self != null; + assert frame != null; + + this.self = self; + this.frame = frame; + } + + public Object getSelf() { + return self; + } + + public MaterializedFrame getFrame() { + return frame; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyClass.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.util.*; + +import com.oracle.truffle.api.CompilerDirectives.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.lookup.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Class} class. Note that most of the functionality you might associate + * with {@code Class} is actually in {@code Module}, implemented by {@link RubyModule}. + */ +public class RubyClass extends RubyModule { + + /** + * The class from which we create the object that is {@code Class}. A subclass of + * {@link RubyClass} so that we can override {@link #newInstance} and allocate a + * {@link RubyClass} rather than a normal {@link RubyBasicObject}. + */ + public static class RubyClassClass extends RubyClass { + + public RubyClassClass(RubyContext context) { + super(context, null, null, null, "Class"); + } + + @Override + public RubyBasicObject newInstance() { + return new RubyClass(null, getContext().getCoreLibrary().getObjectClass(), "(unnamed class)"); + } + + } + + @CompilationFinal private RubyClass superclass; + + // We maintain a list of subclasses so we can notify them when they need to update their layout. + private final Set subClasses = Collections.newSetFromMap(new WeakHashMap()); + + /* + * The layout to use for instances of this class - do not confuse with objectLayout, which is + * the layout for this object - the class. + */ + private ObjectLayout objectLayoutForInstances = null; + + public RubyClass(RubyModule parentModule, RubyClass rubySuperclass, String name) { + this(parentModule, rubySuperclass, name, false); + } + + public RubyClass(RubyModule parentModule, RubyClass rubySuperclass, String name, boolean isSingleton) { + this(rubySuperclass.getContext(), rubySuperclass.getContext().getCoreLibrary().getClassClass(), parentModule, rubySuperclass, name); + + if (!isSingleton) { + getSingletonClass(); + } + } + + /** + * This constructor supports initialization and solves boot-order problems and should not + * normally be used from outside this class. + */ + public RubyClass(RubyContext context, RubyClass classClass, RubyModule parentModule, RubyClass superclass, String name) { + super(context, classClass, parentModule, name); + + if (superclass == null) { + objectLayoutForInstances = ObjectLayout.EMPTY; + } else { + unsafeSetSuperclass(superclass); + } + } + + public RubyClass getSuperclass() { + assert superclass != null; + return superclass; + } + + @Override + public RubyClass getSingletonClass() { + if (rubySingletonClass == null) { + RubyClass singletonSuperclass; + + if (superclass == null) { + singletonSuperclass = getRubyClass(); + } else { + singletonSuperclass = superclass.getSingletonClass(); + } + + rubySingletonClass = new RubyClass(getParentModule(), singletonSuperclass, String.format("#", getName()), true); + + lookupNode = new LookupFork(rubySingletonClass, lookupNode); + } + + return rubySingletonClass; + } + + /** + * This method supports initialization and solves boot-order problems and should not normally be + * used. + */ + public void unsafeSetSuperclass(RubyClass newSuperclass) { + assert superclass == null; + + superclass = newSuperclass; + superclass.addDependent(this); + superclass.subClasses.add(this); + + include(superclass); + + objectLayoutForInstances = new ObjectLayout(getName(), getContext(), superclass.objectLayoutForInstances); + } + + public RubyBasicObject newInstance() { + return new RubyObject(this); + } + + /** + * Is an instance of this class assignable to some location expecting some other class? + */ + public boolean assignableTo(RubyClass otherClass) { + if (this == otherClass) { + return true; + } + + if (superclass == null) { + return false; + } + + return superclass.assignableTo(otherClass); + } + + /** + * Returns the object layout that objects of this class should use. Do not confuse with + * {@link #getObjectLayout}, which for {@link RubyClass} will return the layout of the class + * object itself. + */ + public ObjectLayout getObjectLayoutForInstances() { + return objectLayoutForInstances; + } + + /** + * Change the layout to be used for instances of this object. + */ + public void setObjectLayoutForInstances(ObjectLayout newObjectLayoutForInstances) { + objectLayoutForInstances = newObjectLayoutForInstances; + + for (RubyClass subClass : subClasses) { + subClass.renewObjectLayoutForInstances(); + } + } + + private void renewObjectLayoutForInstances() { + objectLayoutForInstances = objectLayoutForInstances.renew(getContext(), superclass.objectLayoutForInstances); + + for (RubyClass subClass : subClasses) { + subClass.renewObjectLayoutForInstances(); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyContinuation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyContinuation.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * Represents the Ruby {@code Continuation} class. We only support continuations that just move up + * the stack and are one-shot. + */ +public class RubyContinuation extends RubyObject { + + /* + * A continuation is dead if we have already resumed it once. We will not be able to resume it + * again due to the current implementation being an exception thrown to go back up the stack. + */ + private boolean dead = false; + + public RubyContinuation(RubyClass rubyClass) { + super(rubyClass); + } + + /** + * To enter a continuation means to remember the execution state at this point, reify that into + * an object, and then call the passed block. For our implementation, the continuation will be + * dead when this method resumes. + */ + public Object enter(RubyProc block) { + try { + return block.call(null, this); + } catch (ContinuationReturnException e) { + // Thrown in call + + // Check the exception is for this continuation + + if (e.getContinuation() == this) { + return e.getValue(); + } else { + throw e; + } + } finally { + dead = true; + } + } + + /** + * To call a continuation means to go back to the execution state when it was created. For our + * implementation we can only do this once, and only if that means jumping back up the stack. + */ + public void call(Object... args) { + if (dead) { + throw new UnsupportedOperationException("Only continuations that just move up the stack and are one-shot are supported"); + } + + Object returnValue; + + if (args.length == 0) { + returnValue = NilPlaceholder.INSTANCE; + } else if (args.length == 1) { + returnValue = args[0]; + } else { + returnValue = RubyArray.specializedFromObjects(getRubyClass().getContext().getCoreLibrary().getArrayClass(), args); + } + + // Caught in enter + + throw new ContinuationReturnException(this, returnValue); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Exception} class. + */ +public class RubyException extends RubyObject { + + /** + * The class from which we create the object that is {@code Exception}. A subclass of + * {@link RubyClass} so that we can override {@link #newInstance} and allocate a + * {@link RubyException} rather than a normal {@link RubyBasicObject}. + */ + public static class RubyExceptionClass extends RubyClass { + + public RubyExceptionClass(RubyClass superClass, String name) { + super(null, superClass, name); + } + + @Override + public RubyBasicObject newInstance() { + return new RubyException(this); + } + + } + + private RubyString message; + + public RubyException(RubyClass rubyClass) { + super(rubyClass); + message = rubyClass.getContext().makeString("(object uninitialized)"); + } + + public RubyException(RubyClass rubyClass, String message) { + this(rubyClass, rubyClass.getContext().makeString(message)); + } + + public RubyException(RubyClass rubyClass, RubyString message) { + this(rubyClass); + initialize(message); + } + + public void initialize(RubyString setMessage) { + assert setMessage != null; + message = setMessage; + } + + public RubyString getMessage() { + return message; + } + + @Override + public String toString() { + return message.toString(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyFalseClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyFalseClass.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code FalseClass} class. + */ +public class RubyFalseClass extends RubyObject implements Unboxable { + + public RubyFalseClass(RubyClass objectClass) { + super(objectClass); + } + + public Object unbox() { + return false; + } + + @Override + public String toString() { + return "false"; + } + + @Override + public boolean equals(Object other) { + return other instanceof RubyFalseClass || (other instanceof Boolean && !((boolean) other)); + } + + @Override + public int hashCode() { + return Boolean.FALSE.hashCode(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyFiber.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyFiber.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.util.concurrent.*; + +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.objects.*; +import com.oracle.truffle.ruby.runtime.subsystems.*; + +/** + * Represents the Ruby {@code Fiber} class. The current implementation uses Java threads and message + * passing. Note that the relationship between Java threads, Ruby threads and Ruby fibers is + * complex. A Java thread might be running a fiber that on difference resumptions is representing + * different Ruby threads. Take note of the lock contracts on {@link #waitForResume} and + * {@link #resume}. + */ +public class RubyFiber extends RubyObject { + + public static class RubyFiberClass extends RubyClass { + + public RubyFiberClass(RubyClass objectClass) { + super(null, objectClass, "Fiber"); + } + + @Override + public RubyBasicObject newInstance() { + return new RubyFiber(this, getContext().getFiberManager(), getContext().getThreadManager()); + } + + } + + private interface FiberMessage { + } + + private class FiberResumeMessage implements FiberMessage { + + private final RubyThread thread; + private final RubyFiber sendingFiber; + private final Object arg; + + public FiberResumeMessage(RubyThread thread, RubyFiber sendingFiber, Object arg) { + this.thread = thread; + this.sendingFiber = sendingFiber; + this.arg = arg; + } + + public RubyThread getThread() { + return thread; + } + + public RubyFiber getSendingFiber() { + return sendingFiber; + } + + public Object getArg() { + return arg; + } + + } + + private class FiberExitMessage implements FiberMessage { + } + + public class FiberExitException extends ControlFlowException { + + private static final long serialVersionUID = 1522270454305076317L; + + } + + private final FiberManager fiberManager; + private final ThreadManager threadManager; + + private BlockingQueue messageQueue = new ArrayBlockingQueue<>(1); + public RubyFiber lastResumedByFiber = null; + + public RubyFiber(RubyClass rubyClass, FiberManager fiberManager, ThreadManager threadManager) { + super(rubyClass); + this.fiberManager = fiberManager; + this.threadManager = threadManager; + } + + public void initialize(RubyProc block) { + final RubyFiber finalFiber = this; + final RubyProc finalBlock = block; + + new Thread(new Runnable() { + + @Override + public void run() { + fiberManager.registerFiber(finalFiber); + + try { + try { + final Object arg = finalFiber.waitForResume(); + final Object result = finalBlock.call(null, arg); + finalFiber.lastResumedByFiber.resume(finalFiber, result); + } catch (FiberExitException e) { + // Naturally exit the thread on catching this + } + } finally { + fiberManager.unregisterFiber(finalFiber); + } + } + + }).start(); + } + + /** + * Send the Java thread that represents this fiber to sleep until it recieves a resume or exit + * message. On entry, assumes that the GIL is not held. On exit, holding the GIL. + */ + public Object waitForResume() { + FiberMessage message = null; + + do { + try { + // TODO(cs) what is a suitable timeout? + message = messageQueue.poll(1, TimeUnit.SECONDS); + } catch (InterruptedException e) { + // Poll again + } + } while (message == null); + + if (message instanceof FiberExitMessage) { + throw new FiberExitException(); + } + + final FiberResumeMessage resumeMessage = (FiberResumeMessage) message; + + threadManager.enterGlobalLock(resumeMessage.getThread()); + + fiberManager.setCurrentFiber(this); + + lastResumedByFiber = resumeMessage.getSendingFiber(); + return resumeMessage.getArg(); + } + + /** + * Send a message to a fiber by posting into a message queue. Doesn't explicitly notify the Java + * thread (although the queue implementation may) and doesn't wait for the message to be + * received. On entry, assumes the the GIL is held. On exit, not holding the GIL. + */ + public void resume(RubyFiber sendingFiber, Object... args) { + Object arg; + + if (args.length == 0) { + arg = NilPlaceholder.INSTANCE; + } else if (args.length == 1) { + arg = args[0]; + } else { + arg = RubyArray.specializedFromObjects(getRubyClass().getContext().getCoreLibrary().getArrayClass(), args); + } + + final RubyThread runningThread = threadManager.leaveGlobalLock(); + + messageQueue.add(new FiberResumeMessage(runningThread, sendingFiber, arg)); + } + + public void shutdown() { + messageQueue.add(new FiberExitMessage()); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyFile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyFile.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.io.*; + +import com.oracle.truffle.ruby.runtime.*; + +/** + * Represents the Ruby {@code File} class. + */ +public class RubyFile extends RubyObject { + + private final Reader reader; + private final Writer writer; + + public RubyFile(RubyClass rubyClass, Reader reader, Writer writer) { + super(rubyClass); + this.reader = reader; + this.writer = writer; + } + + public void close() { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + public static String expandPath(String fileName) { + // TODO(cs): see the other expandPath + + try { + return new File(fileName).getCanonicalPath(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static String expandPath(String fileName, String dir) { + /* + * TODO(cs): this isn't quite correct - I think we want to collapse .., but we don't want to + * resolve symlinks etc. This might be where we want to start borrowing JRuby's + * implementation, but it looks quite tied to their data structures. + */ + + try { + return new File(dir, fileName).getCanonicalPath(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static RubyFile open(RubyContext context, String fileName, String mode) { + Reader reader; + Writer writer; + + if (mode.equals("rb")) { + try { + reader = new InputStreamReader(new FileInputStream(fileName)); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + + writer = null; + } else if (mode.equals("w")) { + reader = null; + + try { + writer = new OutputStreamWriter(new FileOutputStream(fileName)); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + } else { + throw new UnsupportedOperationException(); + } + + final RubyFile file = new RubyFile(context.getCoreLibrary().getFileClass(), reader, writer); + + return file; + } + + public Reader getReader() { + return reader; + } + + public Writer getWriter() { + return writer; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyFixnum.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyFixnum.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.math.*; + +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Fixnum} class. + */ +public class RubyFixnum extends RubyObject implements Unboxable { + + public static final int MIN_VALUE = Integer.MIN_VALUE; + public static final int MAX_VALUE = Integer.MAX_VALUE; + + public static final BigInteger MIN_VALUE_BIG = BigInteger.valueOf(MIN_VALUE); + public static final BigInteger MAX_VALUE_BIG = BigInteger.valueOf(MAX_VALUE); + + public static final int SIZE = Integer.SIZE; + + private final int value; + + public RubyFixnum(RubyClass fixnumClass, int value) { + super(fixnumClass); + this.value = value; + } + + public int getValue() { + return value; + } + + @Override + public String toString() { + return Integer.toString(value); + } + + @Override + public boolean equals(Object other) { + if (other instanceof Integer) { + return value == (int) other; + } else if (other instanceof RubyFixnum) { + return value == ((RubyFixnum) other).value; + } else if (other instanceof BigInteger) { + return ((BigInteger) other).equals(value); + } else if (other instanceof RubyBignum) { + return ((RubyBignum) other).getValue().equals(value); + } else if (other instanceof Double) { + return value == (double) other; + } else if (other instanceof RubyFloat) { + return value == ((RubyFloat) other).getValue(); + } else { + return super.equals(other); + } + } + + @Override + public int hashCode() { + throw new UnsupportedOperationException(); + } + + public Object unbox() { + return value; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyFloat.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyFloat.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Float} class. + */ +public class RubyFloat extends RubyObject implements Unboxable { + + private final double value; + + public RubyFloat(RubyClass floatClass, double value) { + super(floatClass); + this.value = value; + } + + public double getValue() { + return value; + } + + @Override + public String toString() { + return Double.toString(value); + } + + @Override + public boolean equals(Object other) { + if (other instanceof Integer) { + return value == (int) other; + } else if (other instanceof RubyFixnum) { + return value == ((RubyFixnum) other).getValue(); + } else if (other instanceof Double) { + return value == (double) other; + } else if (other instanceof RubyFloat) { + return value == ((RubyFloat) other).value; + } else { + return super.equals(other); + } + } + + @Override + public int hashCode() { + throw new UnsupportedOperationException(); + } + + public Object unbox() { + return value; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyHash.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyHash.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.util.*; + +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Hash} class. + */ +public class RubyHash extends RubyObject { + + /** + * The class from which we create the object that is {@code Hash}. A subclass of + * {@link RubyClass} so that we can override {@link #newInstance} and allocate a + * {@link RubyHash} rather than a normal {@link RubyBasicObject}. + */ + public static class RubyHashClass extends RubyClass { + + public RubyHashClass(RubyClass objectClass) { + super(null, objectClass, "Hash"); + } + + @Override + public RubyBasicObject newInstance() { + return new RubyHash(this); + } + + } + + public final Map storage = new LinkedHashMap<>(); + @CompilationFinal public RubyProc defaultBlock = null; + + public RubyHash(RubyClass rubyClass, RubyProc defaultBlock) { + super(rubyClass); + initialize(defaultBlock); + } + + public RubyHash(RubyClass rubyClass) { + super(rubyClass); + } + + public void initialize(RubyProc setDefaultBlock) { + defaultBlock = setDefaultBlock; + } + + @Override + public Object dup() { + final RubyHash newHash = new RubyHash(rubyClass); + newHash.setInstanceVariables(getInstanceVariables()); + newHash.storage.putAll(storage); + return newHash; + } + + public void put(Object key, Object value) { + checkFrozen(); + + storage.put(key, value); + } + + public Object get(Object key) { + return storage.get(key); + } + + public Map getMap() { + return storage; + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("{"); + + for (Map.Entry entry : storage.entrySet()) { + if (builder.length() > 1) { + builder.append(", "); + } + + builder.append(entry.getKey().toString()); + builder.append("=>"); + builder.append(entry.getValue().toString()); + } + + builder.append("}"); + return builder.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((storage == null) ? 0 : storage.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof RubyHash)) { + return false; + } + RubyHash other = (RubyHash) obj; + if (storage == null) { + if (other.storage != null) { + return false; + } + } else if (!storage.equals(other.storage)) { + return false; + } + return true; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyMatchData.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyMatchData.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +/** + * Represents the Ruby {@code MatchData} class. + */ +public class RubyMatchData extends RubyObject { + + private final Object[] values; + + public RubyMatchData(RubyClass rubyClass, Object[] values) { + super(rubyClass); + this.values = values; + } + + public Object[] valuesAt(int... indices) { + final Object[] result = new Object[indices.length]; + + for (int n = 0; n < indices.length; n++) { + result[n] = values[indices[n]]; + } + + return result; + } + + public Object[] getValues() { + return values; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyModule.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyModule.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.lookup.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Module} class. + */ +public class RubyModule extends RubyObject implements LookupNode { + + /** + * The class from which we create the object that is {@code Module}. A subclass of + * {@link RubyClass} so that we can override {@link #newInstance} and allocate a + * {@link RubyModule} rather than a normal {@link RubyBasicObject}. + */ + public static class RubyModuleClass extends RubyClass { + + public RubyModuleClass(RubyContext context) { + super(context, null, null, null, "Module"); + } + + @Override + public RubyBasicObject newInstance() { + return new RubyModule(this, null, "(unnamed module)"); + } + + } + + /** + * The slot within a module definition method frame where we store the implicit state that is + * the current visibility for new methods. + */ + public static final Object VISIBILITY_FRAME_SLOT_ID = new Object(); + + /** + * The slot within a module definition method frame where we store the implicit state that is + * the flag for whether or not new methods will be module methods (functions is the term). + */ + public static final Object MODULE_FUNCTION_FLAG_FRAME_SLOT_ID = new Object(); + + // The context is stored here - objects can obtain it via their class (which is a module) + private final RubyContext context; + + /* + * The module in which this module was defined. By analogy, if superclass is the dynamic scope, + * the parent module is the lexical scope. + */ + private final RubyModule parentModule; + + /* + * The first thing to lookup names in. Not always the class, as we also have singleton classes, + * included modules etc. + */ + private LookupNode lookupParent = LookupTerminal.INSTANCE; + + private final String name; + private final Map methods = new HashMap<>(); + private final Map constants = new HashMap<>(); + private final Map classVariables = new HashMap<>(); + + private final CyclicAssumption unmodifiedAssumption; + + /** + * Keep track of other modules that depend on the configuration of this module in some way. The + * include subclasses and modules that include this module. + */ + private final Set dependents = Collections.newSetFromMap(new WeakHashMap()); + + public RubyModule(RubyClass rubyClass, RubyModule parentModule, String name) { + this(rubyClass.getContext(), rubyClass, parentModule, name); + } + + public RubyModule(RubyContext context, RubyClass rubyClass, RubyModule parentModule, String name) { + super(rubyClass); + + this.context = context; + this.parentModule = parentModule; + this.name = name; + + unmodifiedAssumption = new CyclicAssumption(name + " is unmodified"); + + /* + * Modules always go into the object space manager. Manually allocate an objectID, because + * the lazy mechanism uses the Ruby class of the object, which may not be set yet during + * bootstrap. + */ + + objectID = context.getNextObjectID(); + context.getObjectSpaceManager().add(this); + } + + public RubyModule getParentModule() { + return parentModule; + } + + public void include(RubyModule module) { + checkFrozen(); + + lookupParent = new LookupFork(module, lookupParent); + newVersion(); + module.addDependent(this); + } + + /** + * Set the value of a constant, possibly redefining it. + */ + public void setConstant(String constantName, Object value) { + assert RubyContext.shouldObjectBeVisible(value); + + checkFrozen(); + + getConstants().put(constantName, value); + newVersion(); + // TODO(CS): warn when redefining a constant + } + + public void setClassVariable(String variableName, Object value) { + assert RubyContext.shouldObjectBeVisible(value); + + checkFrozen(); + + if (!setClassVariableIfAlreadySet(variableName, value)) { + classVariables.put(variableName, value); + } + } + + public boolean setClassVariableIfAlreadySet(String variableName, Object value) { + assert RubyContext.shouldObjectBeVisible(value); + + checkFrozen(); + + if (lookupParent.setClassVariableIfAlreadySet(variableName, value)) { + return true; + } + + if (classVariables.containsKey(variableName)) { + classVariables.put(variableName, value); + return true; + } + + return false; + } + + public void removeClassVariable(String variableName) { + checkFrozen(); + + classVariables.remove(variableName); + } + + public void setModuleConstant(String constantName, Object value) { + checkFrozen(); + + setConstant(constantName, value); + getSingletonClass().setConstant(constantName, value); + } + + public void addMethod(RubyMethod method) { + checkFrozen(); + getMethods().put(method.getName(), method); + newVersion(); + } + + /** + * Remove a method from this module. + */ + public void removeMethod(String methodName) { + checkFrozen(); + + getMethods().remove(methodName); + newVersion(); + } + + public void undefMethod(String methodName) { + undefMethod(lookupMethod(methodName)); + } + + public void undefMethod(RubyMethod method) { + addMethod(method.undefined()); + } + + /** + * Alias a method. + */ + public void alias(String newName, String oldName) { + final RubyMethod method = lookupMethod(oldName); + + if (method == null) { + CompilerDirectives.transferToInterpreter(); + throw new RuntimeException("Couldn't alias as coudln't find " + oldName); + } + + addMethod(method.withNewName(newName)); + } + + @Override + public Object lookupConstant(String constantName) { + Object value; + + // Look in this module + + value = getConstants().get(constantName); + + if (value != null) { + return value; + } + + // Look in the parent module + + if (parentModule != null) { + value = parentModule.lookupConstant(constantName); + + if (value != null) { + return value; + } + } + + // Look in the lookup parent + + return lookupParent.lookupConstant(constantName); + } + + @Override + public Object lookupClassVariable(String variableName) { + // Look in this module + + final Object value = classVariables.get(variableName); + + if (value != null) { + return value; + } + + // Look in the parent + + return lookupParent.lookupClassVariable(variableName); + } + + public Set getClassVariables() { + final Set classVariablesSet = new HashSet<>(); + + classVariablesSet.addAll(classVariables.keySet()); + classVariablesSet.addAll(lookupParent.getClassVariables()); + + return classVariablesSet; + } + + @Override + public RubyMethod lookupMethod(String methodName) { + // Look in this module + + final RubyMethod method = getMethods().get(methodName); + + if (method != null) { + return method; + } + + // Look in the parent + + return lookupParent.lookupMethod(methodName); + } + + public void appendFeatures(RubyModule other) { + // TODO(CS): check only run once + + for (Map.Entry constantEntry : getConstants().entrySet()) { + final String constantName = constantEntry.getKey(); + final Object constantValue = constantEntry.getValue(); + other.setModuleConstant(constantName, constantValue); + } + + for (Map.Entry methodEntry : getMethods().entrySet()) { + final String methodName = methodEntry.getKey(); + final RubyMethod method = methodEntry.getValue(); + other.addMethod(method.withNewName(methodName)); + } + } + + public RubyContext getContext() { + return context; + } + + public String getName() { + return name; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public String toString() { + return name; + } + + public void newVersion() { + unmodifiedAssumption.invalidate(); + + // Make dependents new versions + + for (RubyModule dependent : dependents) { + dependent.newVersion(); + } + } + + public void addDependent(RubyModule dependent) { + dependents.add(dependent); + } + + public Assumption getUnmodifiedAssumption() { + return unmodifiedAssumption.getAssumption(); + } + + public void getMethods(Map foundMethods) { + lookupParent.getMethods(foundMethods); + + for (RubyMethod method : methods.values()) { + foundMethods.put(method.getName(), method); + } + } + + public static void setCurrentVisibility(Frame frame, Visibility visibility) { + final FrameSlot slot = frame.getFrameDescriptor().findFrameSlot(VISIBILITY_FRAME_SLOT_ID); + + frame.setObject(slot, visibility); + } + + public void visibilityMethod(PackedFrame frame, Object[] arguments, Visibility visibility) { + if (arguments.length == 0) { + setCurrentVisibility(frame.unpack(), visibility); + } else { + for (Object arg : arguments) { + final RubyMethod method = lookupMethod(arg.toString()); + + if (method == null) { + throw new RuntimeException("Couldn't find method " + arg.toString()); + } + + /* + * If the method was already defined in this class, that's fine {@link addMethod} + * will overwrite it, otherwise we do actually want to add a copy of the method with + * a different visibility to this module. + */ + + addMethod(method.withNewVisibility(visibility)); + } + } + } + + public List getDeclaredMethods() { + return new ArrayList<>(getMethods().values()); + } + + public void moduleEval(String source) { + getRubyClass().getContext().eval(source, this); + } + + public Map getConstants() { + return constants; + } + + public Map getMethods() { + return methods; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyNilClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyNilClass.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import com.oracle.truffle.ruby.runtime.*; + +/** + * Represents the Ruby {@code NilClass} class. + */ +public class RubyNilClass extends RubyObject { + + public RubyNilClass(RubyClass rubyClass) { + super(rubyClass); + } + + @Override + public boolean equals(Object other) { + return other instanceof RubyNilClass || other instanceof NilPlaceholder; + } + + @Override + public int hashCode() { + return 0; + } + + public static boolean isNil(Object block) { + return block instanceof NilPlaceholder || block instanceof RubyNilClass; + } + + @Override + public String toString() { + return ""; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyObject.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyObject.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Object} class. + */ +public class RubyObject extends RubyBasicObject { + + public boolean frozen = false; + + public RubyObject(RubyClass rubyClass) { + super(rubyClass); + } + + public void checkFrozen() { + if (frozen) { + CompilerDirectives.transferToInterpreter(); + throw new RaiseException(getRubyClass().getContext().getCoreLibrary().frozenError(getRubyClass().getName().toLowerCase())); + } + } + + public Object dup() { + final RubyObject newObject = new RubyObject(rubyClass); + newObject.setInstanceVariables(getInstanceVariables()); + return newObject; + } + + public static String checkInstanceVariableName(RubyContext context, String name) { + if (!name.startsWith("@")) { + throw new RaiseException(context.getCoreLibrary().nameErrorInstanceNameNotAllowable(name)); + } + + return name.substring(1); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyProc.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyProc.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Proc} class. + */ +public class RubyProc extends RubyObject { + + /** + * The class from which we create the object that is {@code Proc}. A subclass of + * {@link RubyClass} so that we can override {@link #newInstance} and allocate a + * {@link RubyProc} rather than a normal {@link RubyBasicObject}. + */ + public static class RubyProcClass extends RubyClass { + + public RubyProcClass(RubyClass objectClass) { + super(null, objectClass, "Proc"); + } + + @Override + public RubyBasicObject newInstance() { + return new RubyProc(this); + } + + } + + public static enum Type { + PROC, LAMBDA + } + + @CompilationFinal private Type type; + @CompilationFinal private Object self; + @CompilationFinal private RubyProc block; + @CompilationFinal private RubyMethod method; + + public RubyProc(RubyClass procClass) { + super(procClass); + } + + public RubyProc(RubyClass procClass, Type type, Object self, RubyProc block, RubyMethod method) { + super(procClass); + initialize(type, self, block, method); + } + + public void initialize(Type setType, Object setSelf, RubyProc setBlock, RubyMethod setMethod) { + assert setSelf != null; + assert RubyContext.shouldObjectBeVisible(setSelf); + type = setType; + self = setSelf; + block = setBlock; + method = setMethod; + } + + public Object getSelf() { + return self; + } + + @CompilerDirectives.SlowPath + public Object call(PackedFrame caller, Object... args) { + return callWithModifiedSelf(caller, self, args); + } + + public Object callWithModifiedSelf(PackedFrame caller, Object modifiedSelf, Object... args) { + assert modifiedSelf != null; + + try { + return method.call(caller, modifiedSelf, block, args); + } catch (ReturnException e) { + switch (type) { + case PROC: + throw e; + case LAMBDA: + return e.getValue(); + default: + throw new IllegalStateException(); + } + } + } + + public RubyMethod getMethod() { + return method; + } + + public Type getType() { + return type; + } + + public RubyProc getBlock() { + return block; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyRegexp.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyRegexp.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.util.regex.*; + +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Regexp} class. + */ +public class RubyRegexp extends RubyObject { + + /** + * The class from which we create the object that is {@code Regexp}. A subclass of + * {@link RubyClass} so that we can override {@link #newInstance} and allocate a + * {@link RubyRegexp} rather than a normal {@link RubyBasicObject}. + */ + public static class RubyRegexpClass extends RubyClass { + + public RubyRegexpClass(RubyClass objectClass) { + super(null, objectClass, "Regexp"); + } + + @Override + public RubyBasicObject newInstance() { + return new RubyRegexp(getContext().getCoreLibrary().getRegexpClass()); + } + + } + + @CompilationFinal private Pattern pattern; + + public RubyRegexp(RubyClass regexpClass) { + super(regexpClass); + } + + public RubyRegexp(RubyClass regexpClass, String pattern) { + this(regexpClass); + initialize(compile(pattern)); + } + + public RubyRegexp(RubyClass regexpClass, Pattern pattern) { + this(regexpClass); + initialize(pattern); + } + + public void initialize(String setPattern) { + pattern = compile(setPattern); + } + + public void initialize(Pattern setPattern) { + pattern = setPattern; + } + + public Object matchOperator(Frame frame, String string) { + final RubyContext context = getRubyClass().getContext(); + + final Matcher matcher = pattern.matcher(string); + + if (matcher.find()) { + for (int n = 1; n < matcher.groupCount() + 1; n++) { + final FrameSlot slot = frame.getFrameDescriptor().findFrameSlot("$" + n); + + if (slot != null) { + frame.setObject(slot, context.makeString(matcher.group(n))); + } + } + + return matcher.start(); + } else { + return NilPlaceholder.INSTANCE; + } + } + + public Pattern getPattern() { + return pattern; + } + + public Object match(String string) { + final RubyContext context = getRubyClass().getContext(); + + final Matcher matcher = pattern.matcher(string); + + if (!matcher.find()) { + return NilPlaceholder.INSTANCE; + } + + final Object[] values = new Object[matcher.groupCount() + 1]; + + for (int n = 0; n < matcher.groupCount() + 1; n++) { + final String group = matcher.group(n); + + if (group == null) { + values[n] = NilPlaceholder.INSTANCE; + } else { + values[n] = context.makeString(group); + } + } + + return new RubyMatchData(context.getCoreLibrary().getMatchDataClass(), values); + } + + @Override + public int hashCode() { + return pattern.pattern().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof RubyRegexp)) { + return false; + } + RubyRegexp other = (RubyRegexp) obj; + if (pattern == null) { + if (other.pattern != null) { + return false; + } + } else if (!pattern.pattern().equals(other.pattern.pattern())) { + return false; + } + return true; + } + + public static Pattern compile(String pattern) { + return Pattern.compile(pattern, Pattern.MULTILINE | Pattern.UNIX_LINES); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyString.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyString.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.math.*; +import java.nio.*; +import java.nio.charset.*; +import java.util.*; +import java.util.regex.*; + +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.array.*; +import com.oracle.truffle.ruby.runtime.core.range.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code String} class. + */ +public class RubyString extends RubyObject { + + /** + * The class from which we create the object that is {@code String}. A subclass of + * {@link RubyClass} so that we can override {@link #newInstance} and allocate a + * {@link RubyString} rather than a normal {@link RubyBasicObject}. + */ + public static class RubyStringClass extends RubyClass { + + public RubyStringClass(RubyClass objectClass) { + super(null, objectClass, "String"); + } + + @Override + public RubyBasicObject newInstance() { + return new RubyString(getContext().getCoreLibrary().getStringClass(), ""); + } + + } + + private boolean fromJavaString; + + private Charset encoding; + private byte[] bytes; + + private String cachedStringValue; + + /** + * Construct a string from a Java {@link String}, lazily converting to bytes as needed. + */ + public RubyString(RubyClass stringClass, String value) { + super(stringClass); + fromJavaString = true; + encoding = null; + bytes = null; + cachedStringValue = value; + } + + /** + * Construct a string from bytes representing characters in an encoding, lazily converting to a + * Java {@link String} as needed. + */ + public RubyString(RubyClass stringClass, Charset encoding, byte[] bytes) { + super(stringClass); + fromJavaString = false; + this.encoding = encoding; + this.bytes = bytes; + cachedStringValue = null; + } + + public RubyString(RubyString copyOf) { + super(copyOf.getRubyClass().getContext().getCoreLibrary().getStringClass()); + fromJavaString = copyOf.fromJavaString; + encoding = copyOf.encoding; + + if (copyOf.bytes != null) { + bytes = Arrays.copyOf(copyOf.bytes, copyOf.bytes.length); + } else { + bytes = null; + } + + cachedStringValue = copyOf.cachedStringValue; + } + + public boolean isFromJavaString() { + return fromJavaString; + } + + public byte[] getBytes() { + return bytes; + } + + public void replace(String value) { + fromJavaString = true; + encoding = null; + bytes = null; + cachedStringValue = value; + } + + @Override + public String toString() { + if (cachedStringValue == null) { + cachedStringValue = encoding.decode(ByteBuffer.wrap(bytes)).toString(); + } + + return cachedStringValue; + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return false; + } + + // If the other value is a Java string, use our Java string representation to compare + + if (other instanceof String) { + return toString().equals(other); + } + + if (other instanceof RubyString) { + final RubyString otherString = (RubyString) other; + + // If we both came from Java strings, use them to compare + + if (fromJavaString && otherString.fromJavaString) { + return toString().equals(other.toString()); + } + + // If we both have the same encoding, compare bytes + + if (encoding == otherString.encoding) { + return Arrays.equals(bytes, otherString.bytes); + } + + // If we don't have the same encoding, we need some more advanced logic + + throw new UnsupportedOperationException("Can't compare strings in different encodings yet"); + } + + return false; + } + + @Override + public int hashCode() { + return toString().hashCode(); + } + + public static Object getIndex(RubyContext context, String string, Object[] args) { + if (args.length == 1) { + final Object index = args[0]; + + if (index instanceof Integer) { + final int stringLength = string.length(); + final int normalisedIndex = ArrayUtilities.normaliseIndex(stringLength, (int) index); + + return context.makeString(string.charAt(normalisedIndex)); + } else if (index instanceof FixnumRange) { + final FixnumRange range = (FixnumRange) index; + + final int stringLength = string.length(); + + if (range.doesExcludeEnd()) { + final int begin = ArrayUtilities.normaliseIndex(stringLength, range.getBegin()); + final int exclusiveEnd = ArrayUtilities.normaliseExclusiveIndex(stringLength, range.getExclusiveEnd()); + return context.makeString(string.substring(begin, exclusiveEnd)); + } else { + final int begin = ArrayUtilities.normaliseIndex(stringLength, range.getBegin()); + final int inclusiveEnd = ArrayUtilities.normaliseIndex(stringLength, range.getInclusiveEnd()); + return context.makeString(string.substring(begin, inclusiveEnd + 1)); + } + } else { + throw new UnsupportedOperationException("Don't know how to index a string with " + index.getClass()); + } + } else { + final int rangeStart = (int) args[0]; + int rangeLength = (int) args[1]; + + if (rangeLength > string.length() - rangeStart) { + rangeLength = string.length() - rangeStart; + } + + if (rangeStart > string.length()) { + return NilPlaceholder.INSTANCE; + } + + return context.makeString(string.substring(rangeStart, rangeStart + rangeLength)); + } + } + + @Override + public Object dup() { + return new RubyString(this); + } + + public void concat(RubyString other) { + if (fromJavaString && other.fromJavaString) { + cachedStringValue += other.cachedStringValue; + encoding = null; + bytes = null; + } else { + throw new UnsupportedOperationException("Don't know how to append strings with encodings"); + } + } + + public static String ljust(String string, int length, String padding) { + final StringBuilder builder = new StringBuilder(); + + builder.append(string); + + int n = 0; + + while (builder.length() < length) { + builder.append(padding.charAt(n)); + + n++; + + if (n == padding.length()) { + n = 0; + } + } + + return builder.toString(); + } + + public static String rjust(String string, int length, String padding) { + final StringBuilder builder = new StringBuilder(); + + int n = 0; + + while (builder.length() + string.length() < length) { + builder.append(padding.charAt(n)); + + n++; + + if (n == padding.length()) { + n = 0; + } + } + + builder.append(string); + + return builder.toString(); + } + + public static RubyArray scan(RubyContext context, String string, Pattern pattern) { + final Matcher matcher = pattern.matcher(string); + + final RubyArray results = new RubyArray(context.getCoreLibrary().getArrayClass()); + + while (matcher.find()) { + if (matcher.groupCount() == 0) { + results.push(context.makeString(matcher.group(0))); + } else { + final RubyArray subResults = new RubyArray(context.getCoreLibrary().getArrayClass()); + + for (int n = 1; n < matcher.groupCount() + 1; n++) { + subResults.push(context.makeString(matcher.group(n))); + } + + results.push(subResults); + } + } + + return results; + } + + public Object toInteger() { + if (toString().length() == 0) { + return 0; + } + + try { + final int value = Integer.parseInt(toString()); + + if (value >= RubyFixnum.MIN_VALUE && value <= RubyFixnum.MAX_VALUE) { + return value; + } else { + return BigInteger.valueOf(value); + } + } catch (NumberFormatException e) { + return new BigInteger(toString()); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubySymbol.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubySymbol.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Symbol} class. + */ +public class RubySymbol extends RubyObject { + + private final String symbol; + + public RubySymbol(RubyClass symbolClass, String symbol) { + super(symbolClass); + this.symbol = symbol.intern(); + } + + public RubyProc toProc() { + final RubyContext context = getRubyClass().getContext(); + + final CallTarget callTarget = new CallTarget() { + + @Override + public Object call(PackedFrame frame, Arguments args) { + final RubyArguments rubyArgs = (RubyArguments) args; + final Object receiver = rubyArgs.getArguments()[0]; + final Object[] sendArgs = Arrays.copyOfRange(rubyArgs.getArguments(), 1, rubyArgs.getArguments().length); + final RubyBasicObject receiverObject = context.getCoreLibrary().box(receiver); + return receiverObject.send(symbol, rubyArgs.getBlock(), sendArgs); + } + + }; + + final CallTargetMethodImplementation methodImplementation = new CallTargetMethodImplementation(callTarget, null); + final RubyMethod method = new RubyMethod(null, null, new UniqueMethodIdentifier(), symbol, null, Visibility.PUBLIC, false, methodImplementation); + + return new RubyProc(context.getCoreLibrary().getProcClass(), RubyProc.Type.PROC, NilPlaceholder.INSTANCE, null, method); + } + + @Override + public String toString() { + return symbol; + } + + @Override + public String inspect() { + return ":" + symbol; + } + + @Override + public int hashCode() { + return symbol.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } else if (other instanceof RubySymbol) { + return symbol == ((RubySymbol) other).symbol; + } else if (other instanceof RubyString) { + return other.equals(symbol); + } else { + return super.equals(other); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyThread.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.util.*; +import java.util.concurrent.*; + +import com.oracle.truffle.ruby.runtime.objects.*; +import com.oracle.truffle.ruby.runtime.subsystems.*; + +/** + * Represents the Ruby {@code Thread} class. Implemented using Java threads, but note that there is + * not a one-to-one mapping between Ruby threads and Java threads - specifically in combination with + * fibers as they are currently implemented as their own Java threads. + */ +public class RubyThread extends RubyObject { + + /** + * The class from which we create the object that is {@code Thread}. A subclass of + * {@link RubyClass} so that we can override {@link #newInstance} and allocate a + * {@link RubyThread} rather than a normal {@link RubyBasicObject}. + */ + public static class RubyThreadClass extends RubyClass { + + public RubyThreadClass(RubyClass objectClass) { + super(null, objectClass, "Thread"); + } + + @Override + public RubyBasicObject newInstance() { + return new RubyThread(this, getContext().getThreadManager()); + } + + } + + private final ThreadManager manager; + + private final CountDownLatch finished = new CountDownLatch(1); + + private final int hashCode = new Random().nextInt(); + + public RubyThread(RubyClass rubyClass, ThreadManager manager) { + super(rubyClass); + this.manager = manager; + } + + public void initialize(RubyProc block) { + final RubyProc finalBlock = block; + + initialize(new Runnable() { + + @Override + public void run() { + finalBlock.call(null); + } + + }); + } + + public void initialize(Runnable runnable) { + final RubyThread finalThread = this; + final Runnable finalRunnable = runnable; + + new Thread(new Runnable() { + + @Override + public void run() { + finalThread.manager.registerThread(finalThread); + finalThread.manager.enterGlobalLock(finalThread); + + try { + finalRunnable.run(); + } finally { + finalThread.manager.leaveGlobalLock(); + finalThread.manager.unregisterThread(finalThread); + finalThread.finished.countDown(); + } + } + + }).start(); + } + + @Override + public int hashCode() { + return hashCode; + } + + public void shutdown() { + } + + public void join() { + final RubyThread runningThread = getRubyClass().getContext().getThreadManager().leaveGlobalLock(); + + try { + while (true) { + try { + finished.await(); + break; + } catch (InterruptedException e) { + // Await again + } + } + } finally { + runningThread.manager.enterGlobalLock(runningThread); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyTime.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyTime.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.text.*; +import java.util.*; + +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code Time} class. This is a very rough implementation and is only really + * enough to run benchmark harnesses. + */ +public class RubyTime extends RubyObject { + + /** + * The class from which we create the object that is {@code Time}. A subclass of + * {@link RubyClass} so that we can override {@link #newInstance} and allocate a + * {@link RubyTime} rather than a normal {@link RubyBasicObject}. + */ + public static class RubyTimeClass extends RubyClass { + + public RubyTimeClass(RubyClass objectClass) { + super(null, objectClass, "Time"); + } + + @Override + public RubyBasicObject newInstance() { + return new RubyTime(this, milisecondsToNanoseconds(System.currentTimeMillis())); + } + + } + + private final long nanoseconds; + + public RubyTime(RubyClass timeClass, long nanoseconds) { + super(timeClass); + this.nanoseconds = nanoseconds; + } + + /** + * Subtract one time from another, producing duration in seconds. + */ + public double subtract(RubyTime other) { + return nanosecondsToSecond(nanoseconds - other.nanoseconds); + } + + @Override + public String toString() { + /* + * I think this is ISO 8601 with a custom time part. Note that Ruby's time formatting syntax + * is different to Java's. + */ + + return new SimpleDateFormat("Y-MM-d H:m:ss Z").format(toDate()); + } + + private Date toDate() { + return new Date(nanosecondsToMiliseconds(nanoseconds)); + } + + public static RubyTime fromDate(RubyClass timeClass, long timeMiliseconds) { + return new RubyTime(timeClass, milisecondsToNanoseconds(timeMiliseconds)); + } + + private static long milisecondsToNanoseconds(long miliseconds) { + return miliseconds * 1000000; + } + + private static long nanosecondsToMiliseconds(long nanoseconds) { + return nanoseconds / 1000000; + } + + private static double nanosecondsToSecond(long nanoseconds) { + return nanoseconds / 1e9; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyTrueClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyTrueClass.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Represents the Ruby {@code TrueClass} class. + */ +public class RubyTrueClass extends RubyObject implements Unboxable { + + public RubyTrueClass(RubyClass objectClass) { + super(objectClass); + } + + public Object unbox() { + return true; + } + + @Override + public String toString() { + return "true"; + } + + @Override + public boolean equals(Object other) { + return other instanceof RubyTrueClass || (other instanceof Boolean && (boolean) other); + } + + @Override + public int hashCode() { + return Boolean.TRUE.hashCode(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/StringFormatter.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/StringFormatter.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core; + +import java.io.*; +import java.util.*; + +import com.oracle.truffle.ruby.runtime.core.array.*; + +public class StringFormatter { + + public static String format(String format, List values) { + final ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); + final PrintStream printStream = new PrintStream(byteArray); + + format(printStream, format, values); + + return byteArray.toString(); + } + + public static void format(PrintStream stream, String format, List values) { + /* + * See http://www.ruby-doc.org/core-1.9.3/Kernel.html#method-i-sprintf. + * + * At the moment we just do the basics that we need. We will need a proper lexer later on. + * Or better than that we could compile to Truffle nodes if the format string is constant! I + * don't think we can easily translate to Java's format syntax, otherwise JRuby would do + * that and they don't. + */ + + // I'm not using a for loop, because Checkstyle won't let me modify the control variable + + int n = 0; + int v = 0; + + while (n < format.length()) { + final char c = format.charAt(n); + n++; + + if (c == '%') { + // %[flags][width][.precision]type + + final String flagChars = "0"; + + boolean zeroPad = false; + + while (n < format.length() && flagChars.indexOf(format.charAt(n)) != -1) { + switch (format.charAt(n)) { + case '0': + zeroPad = true; + break; + } + + n++; + } + + int width; + + if (n < format.length() && Character.isDigit(format.charAt(n))) { + final int widthStart = n; + + while (Character.isDigit(format.charAt(n))) { + n++; + } + + width = Integer.parseInt(format.substring(widthStart, n)); + } else { + width = 0; + } + + int precision; + + if (format.charAt(n) == '.') { + n++; + + final int precisionStart = n; + + while (Character.isDigit(format.charAt(n))) { + n++; + } + + precision = Integer.parseInt(format.substring(precisionStart, n)); + } else { + precision = 5; + } + + final char type = format.charAt(n); + n++; + + final StringBuilder formatBuilder = new StringBuilder(); + + formatBuilder.append("%"); + + if (width > 0) { + if (zeroPad) { + formatBuilder.append("0"); + } + + formatBuilder.append(width); + } + + switch (type) { + case 'd': { + formatBuilder.append("d"); + final int value = GeneralConversions.toFixnum(values.get(v)); + stream.printf(formatBuilder.toString(), value); + break; + } + + case 'f': { + formatBuilder.append("."); + formatBuilder.append(precision); + formatBuilder.append("f"); + final double value = GeneralConversions.toFloat(values.get(v)); + stream.printf(formatBuilder.toString(), value); + break; + } + + default: + throw new RuntimeException("Kernel#sprintf error"); + } + + v++; + } else { + stream.print(c); + } + } + } + + public static void formatPuts(PrintStream stream, List args) { + if (args.size() > 0) { + formatPutsInner(stream, args); + } else { + stream.println(); + } + } + + public static void formatPutsInner(PrintStream stream, List args) { + if (args.size() > 0) { + for (Object arg : args) { + if (arg instanceof RubyArray) { + final RubyArray array = (RubyArray) arg; + formatPutsInner(stream, array.asList()); + } else { + stream.println(arg); + } + } + } + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/ArrayStore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/ArrayStore.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.array; + +/** + * Interface to various ways to store values in arrays. + */ +public interface ArrayStore { + + /** + * Get the size of the store. + */ + int size(); + + /** + * Get a value from the array using a normalized index. + */ + Object get(int normalisedIndex); + + /** + * Get a range of values from an array store. + */ + ArrayStore getRange(int normalisedBegin, int truncatedNormalisedExclusiveEnd); + + /** + * Set a value at an index, or throw {@link GeneraliseArrayStoreException} if that's not + * possible. + */ + void set(int normalisedIndex, Object value) throws GeneraliseArrayStoreException; + + /** + * Set a range to be a single value, or throw {@link GeneraliseArrayStoreException} if that's + * not possible. + */ + void setRangeSingle(int normalisedBegin, int truncatedNormalisedExclusiveEnd, Object value) throws GeneraliseArrayStoreException; + + /** + * Set a range to be a copied from another array, or throw {@link GeneraliseArrayStoreException} + * if that's not possible. + */ + void setRangeArray(int normalisedBegin, int normalisedExclusiveEnd, ArrayStore other) throws GeneraliseArrayStoreException; + + /** + * Insert a value at an index, or throw {@link GeneraliseArrayStoreException} if that's not + * possible. + */ + void insert(int normalisedIndex, Object value) throws GeneraliseArrayStoreException; + + /** + * Push a value onto the end, or throw {@link GeneraliseArrayStoreException} if that's not + * possible. + */ + void push(Object value) throws GeneraliseArrayStoreException; + + /** + * Delete a value at an index, returning the value. + */ + Object deleteAt(int normalisedIndex); + + /** + * Does a store contain a value? + */ + boolean contains(Object value); + + /** + * Duplicate the store. + */ + ArrayStore dup(); + + /** + * Duplicate the store, in a format which can store an object. + */ + ArrayStore generalizeFor(Object type); + + /** + * Get the type of value stored. + */ + Object getIndicativeValue(); + + /** + * Get the contents of the store as a new array. + */ + Object[] toObjectArray(); + + /** + * Does one store equal another. + */ + boolean equals(ArrayStore other); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/ArrayUtilities.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/ArrayUtilities.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.array; + +/** + * Simple array utilities, not tied to any particular implementation. + */ +public abstract class ArrayUtilities { + + /** + * Apply Ruby's wrap-around index semantics. + */ + public static int normaliseIndex(int length, int index) { + if (index < 0) { + return length + index; + } else { + return index; + } + } + + /** + * Apply Ruby's wrap-around index semantics. + */ + public static int normaliseExclusiveIndex(int length, int exclusiveIndex) { + if (exclusiveIndex < 0) { + return length + exclusiveIndex + 1; + } else { + return exclusiveIndex; + } + } + + /** + * If an exclusive index is beyond the end of the array, truncate it to be length of the array. + */ + public static int truncateNormalisedExclusiveIndex(int length, int normalisedExclusiveEnd) { + return Math.min(length, normalisedExclusiveEnd); + } + + /** + * What capacity should we allocate for a given requested length? + */ + public static int capacityFor(int length) { + return Math.max(16, length * 2); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/BaseArrayStore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/BaseArrayStore.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.array; + +/** + * Contains implementations of as much of the array stores that we could easily share. Much of the + * rest depends on static types in method signatures, so is lexically almost the same, but isn't the + * same in type, and as the whole point is to avoid boxing, we can't use Java's generics. + */ +public abstract class BaseArrayStore implements ArrayStore { + + protected int capacity; + protected int size; + + @Override + public int size() { + return size; + } + + /** + * Set a range in the array to be another range. You must ensure that the otherValues array is + * of the same type as your values array. + */ + protected void setRangeArrayMatchingTypes(int normalisedBegin, int normalisedExclusiveEnd, Object otherValues, int otherSize) { + // Is the range the whole array? + + if (normalisedBegin == 0 && normalisedExclusiveEnd == size) { + // Do we already have enough space? + + if (otherSize <= capacity) { + // Copy to our existing array. + final Object values = getValuesArrayObject(); + System.arraycopy(otherValues, 0, values, 0, otherSize); + } else { + // Create a new copy of their array. + setCapacityWithNewArray(otherSize); + final Object values = getValuesArrayObject(); + System.arraycopy(otherValues, 0, values, 0, otherSize); + } + + size = otherSize; + } else { + final int rangeLength = normalisedExclusiveEnd - normalisedBegin; + + // Create extra space - might be negative if the new range is shorter, or zero. + + final int extraSpaceNeeded = otherSize - rangeLength; + + if (extraSpaceNeeded > 0) { + createSpace(normalisedBegin, extraSpaceNeeded); + } else if (extraSpaceNeeded < 0) { + deleteSpace(normalisedBegin, -extraSpaceNeeded); + } + + // Copy across the new values. + final Object values = getValuesArrayObject(); + System.arraycopy(otherValues, 0, values, normalisedBegin, otherSize); + } + } + + protected void createSpace(int normalisedBegin, int count) { + /* + * Is this space at the end or in the middle? + */ + + if (normalisedBegin == size) { + createSpaceAtEnd(count); + } else { + /* + * Create space in the middle - is the array already big enough? + */ + + final int elementsToMove = size - normalisedBegin; + + if (size + count > capacity) { + /* + * The array isn't big enough. We don't want to use Arrays.copyOf because that will + * do wasted copying of the elements we are about to move. However - is + * Arrays.copyOf clever enough to see that only one instance of Array is using the + * block and use realloc, potentially avoiding a malloc and winning? + */ + + final Object values = getValuesArrayObject(); + setCapacityWithNewArray(ArrayUtilities.capacityFor(size + count)); + final Object newValues = getValuesArrayObject(); + System.arraycopy(values, 0, newValues, 0, normalisedBegin); + System.arraycopy(values, normalisedBegin, newValues, normalisedBegin + count, elementsToMove); + } else { + /* + * The array is already big enough - we can copy elements already in the array to + * make space. + */ + + final Object values = getValuesArrayObject(); + System.arraycopy(values, normalisedBegin, values, normalisedBegin + count, elementsToMove); + } + + size += count; + } + } + + protected void createSpaceAtEnd(int count) { + /* + * Create space at the end - we can do this by creating a copy of the array if needed. + */ + + if (size + count > capacity) { + setCapacityByCopying(ArrayUtilities.capacityFor(size + count)); + } + + size += count; + } + + protected void deleteSpace(int normalisedBegin, int count) { + final Object values = getValuesArrayObject(); + final int elementsToMove = size - normalisedBegin - count; + + if (elementsToMove > 0) { + System.arraycopy(values, normalisedBegin + count, values, normalisedBegin, elementsToMove); + } + + size -= count; + } + + protected abstract void setCapacityByCopying(int newCapacity); + + protected abstract void setCapacityWithNewArray(int newCapacity); + + protected abstract Object getValuesArrayObject(); + + @Override + public boolean equals(ArrayStore other) { + for (int n = 0; n < size; n++) { + if (!other.get(n).equals(get(n))) { + return false; + } + } + + return true; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/EmptyArrayStore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/EmptyArrayStore.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.array; + +import com.oracle.truffle.ruby.runtime.*; + +/** + * An array store that can only be empty. + */ +public final class EmptyArrayStore implements ArrayStore { + + public static final EmptyArrayStore INSTANCE = new EmptyArrayStore(); + + private EmptyArrayStore() { + } + + @Override + public int size() { + return 0; + } + + @Override + public Object get(int normalisedIndex) { + return NilPlaceholder.INSTANCE; + } + + @Override + public ArrayStore getRange(int normalisedBegin, int truncatedNormalisedExclusiveEnd) { + return null; // Represents Nil + } + + @Override + public void set(int normalisedIndex, Object value) throws GeneraliseArrayStoreException { + throw new GeneraliseArrayStoreException(); + } + + @Override + public void setRangeSingle(int normalisedBegin, int truncatedNormalisedExclusiveEnd, Object value) throws GeneraliseArrayStoreException { + throw new GeneraliseArrayStoreException(); + } + + @Override + public void setRangeArray(int normalisedBegin, int normalisedExclusiveEnd, ArrayStore other) throws GeneraliseArrayStoreException { + throw new GeneraliseArrayStoreException(); + } + + @Override + public void insert(int normalisedIndex, Object value) throws GeneraliseArrayStoreException { + throw new GeneraliseArrayStoreException(); + } + + @Override + public void push(Object value) throws GeneraliseArrayStoreException { + throw new GeneraliseArrayStoreException(); + } + + @Override + public Object deleteAt(int normalisedIndex) { + throw new UnsupportedOperationException("Cannot delete from an empty array"); + } + + @Override + public ArrayStore dup() { + return this; + } + + @Override + public boolean contains(Object value) { + return false; + } + + @Override + public ArrayStore generalizeFor(Object type) { + if (type instanceof Integer) { + return new FixnumArrayStore(); + } else { + return new ObjectArrayStore(); + } + } + + @Override + public Object getIndicativeValue() { + return null; + } + + @Override + public Object[] toObjectArray() { + return new Object[]{}; + } + + @Override + public boolean equals(ArrayStore other) { + if (other == null) { + return false; + } else if (other == this) { + return true; + } else { + return other.size() == 0; + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/FixnumArrayStore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/FixnumArrayStore.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.array; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A store for an array of Fixnums. + */ +public final class FixnumArrayStore extends BaseArrayStore { + + private int[] values; + + public FixnumArrayStore() { + this(new int[]{}); + } + + public FixnumArrayStore(int[] values) { + this.values = values; + size = values.length; + capacity = values.length; + } + + @Override + public Object get(int normalisedIndex) { + try { + return getFixnum(normalisedIndex); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + + public int getFixnum(int normalisedIndex) throws UnexpectedResultException { + if (normalisedIndex >= size) { + throw new UnexpectedResultException(NilPlaceholder.INSTANCE); + } + + return values[normalisedIndex]; + } + + @Override + public ArrayStore getRange(int normalisedBegin, int truncatedNormalisedExclusiveEnd) { + if (normalisedBegin >= size) { + return null; // Represents Nil + } + + return new FixnumArrayStore(Arrays.copyOfRange(values, normalisedBegin, truncatedNormalisedExclusiveEnd)); + } + + @Override + public void set(int normalisedIndex, Object value) throws GeneraliseArrayStoreException { + if (value instanceof Integer) { + setFixnum(normalisedIndex, (int) value); + } else { + throw new GeneraliseArrayStoreException(); + } + } + + public void setFixnum(int normalisedIndex, int value) throws GeneraliseArrayStoreException { + if (normalisedIndex > size) { + throw new GeneraliseArrayStoreException(); + } + + if (normalisedIndex == size) { + push(value); + } else { + values[normalisedIndex] = value; + } + } + + @Override + public void setRangeSingle(int normalisedBegin, int truncatedNormalisedExclusiveEnd, Object value) throws GeneraliseArrayStoreException { + if (value instanceof Integer) { + setRangeSingleFixnum(normalisedBegin, truncatedNormalisedExclusiveEnd, (int) value); + } else { + throw new GeneraliseArrayStoreException(); + } + } + + public void setRangeSingleFixnum(int normalisedBegin, int truncatedNormalisedExclusiveEnd, int value) { + // Is the range the whole array? + + if (normalisedBegin == 0 && truncatedNormalisedExclusiveEnd == size) { + // Reset length and set the value. + size = 1; + values[0] = value; + } else { + // Delete the range, except for the first value. + deleteSpace(normalisedBegin + 1, truncatedNormalisedExclusiveEnd - normalisedBegin - 1); + + // Set the value we left in. + values[normalisedBegin] = value; + } + } + + @Override + public void setRangeArray(int normalisedBegin, int normalisedExclusiveEnd, ArrayStore other) throws GeneraliseArrayStoreException { + if (other instanceof FixnumArrayStore) { + setRangeArrayFixnum(normalisedBegin, normalisedExclusiveEnd, (FixnumArrayStore) other); + } else { + throw new GeneraliseArrayStoreException(); + } + } + + public void setRangeArrayFixnum(int normalisedBegin, int normalisedExclusiveEnd, FixnumArrayStore other) { + setRangeArrayMatchingTypes(normalisedBegin, normalisedExclusiveEnd, other.values, other.size); + } + + @Override + public void insert(int normalisedIndex, Object value) throws GeneraliseArrayStoreException { + if (value instanceof Integer) { + insertFixnum(normalisedIndex, (int) value); + } else { + throw new GeneraliseArrayStoreException(); + } + } + + public void insertFixnum(int normalisedIndex, int value) throws GeneraliseArrayStoreException { + if (normalisedIndex > size) { + throw new GeneraliseArrayStoreException(); + } + + createSpace(normalisedIndex, 1); + values[normalisedIndex] = value; + } + + @Override + public void push(Object value) throws GeneraliseArrayStoreException { + if (value instanceof Integer) { + pushFixnum((int) value); + } else { + throw new GeneraliseArrayStoreException(); + } + } + + public void pushFixnum(int value) { + createSpaceAtEnd(1); + values[size - 1] = value; + } + + @Override + public Object deleteAt(int normalisedIndex) { + try { + return deleteAtFixnum(normalisedIndex); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + + public int deleteAtFixnum(int normalisedIndex) throws UnexpectedResultException { + if (normalisedIndex >= size) { + CompilerDirectives.transferToInterpreter(); + throw new UnexpectedResultException(NilPlaceholder.INSTANCE); + } + + final int value = values[normalisedIndex]; + + deleteSpace(normalisedIndex, 1); + + return value; + } + + @Override + public ArrayStore dup() { + return new FixnumArrayStore(Arrays.copyOf(values, size)); + } + + @Override + public boolean contains(Object value) { + if (!(value instanceof Integer)) { + return false; + } + + final int intValue = (int) value; + + for (int n = 0; n < size; n++) { + if (values[n] == intValue) { + return true; + } + } + + return false; + } + + @Override + public ArrayStore generalizeFor(Object type) { + return new ObjectArrayStore(toObjectArray()); + } + + @Override + public Object getIndicativeValue() { + return 0; + } + + @Override + protected void setCapacityByCopying(int newCapacity) { + values = Arrays.copyOf(values, newCapacity); + capacity = values.length; + } + + @Override + protected void setCapacityWithNewArray(int newCapacity) { + values = new int[newCapacity]; + capacity = values.length; + } + + @Override + protected Object getValuesArrayObject() { + return values; + } + + @Override + public Object[] toObjectArray() { + final Object[] objectValues = new Object[size]; + + // System.arraycopy will not box. + + for (int n = 0; n < size; n++) { + objectValues[n] = values[n]; + } + + return objectValues; + } + + @Override + public boolean equals(ArrayStore other) { + if (other instanceof FixnumArrayStore) { + return equals((FixnumArrayStore) other); + } else { + return super.equals(other); + } + } + + public boolean equals(FixnumArrayStore other) { + if (other == null) { + return false; + } else if (other == this) { + return true; + } else if (other.size != size) { + return false; + } else if (other.capacity == capacity) { + return Arrays.equals(other.values, values); + } else { + for (int n = 0; n < size; n++) { + if (other.values[n] != values[n]) { + return false; + } + } + + return true; + } + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/FixnumImmutablePairArrayStore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/FixnumImmutablePairArrayStore.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.array; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A store for a pair of Fixnums. + */ +public final class FixnumImmutablePairArrayStore extends BaseArrayStore { + + private final int first; + private final int second; + + public FixnumImmutablePairArrayStore(int first, int second) { + size = 2; + capacity = 2; + this.first = first; + this.second = second; + } + + @Override + public int size() { + return 2; + } + + @Override + public Object get(int normalisedIndex) { + switch (normalisedIndex) { + case 0: + return first; + case 1: + return second; + default: + return NilPlaceholder.INSTANCE; + } + } + + public int getFixnum(int normalisedIndex) throws UnexpectedResultException { + switch (normalisedIndex) { + case 0: + return first; + case 1: + return second; + default: + CompilerDirectives.transferToInterpreter(); + throw new UnexpectedResultException(NilPlaceholder.INSTANCE); + } + } + + @Override + public ArrayStore getRange(int normalisedBegin, int truncatedNormalisedExclusiveEnd) { + if (normalisedBegin >= size) { + return null; // Represents Nil + } + + return new FixnumArrayStore(Arrays.copyOfRange(new int[]{first, second}, normalisedBegin, truncatedNormalisedExclusiveEnd)); + } + + @Override + public void set(int normalisedIndex, Object value) throws GeneraliseArrayStoreException { + CompilerDirectives.transferToInterpreter(); + throw new GeneraliseArrayStoreException(); + } + + @Override + public void setRangeSingle(int normalisedBegin, int truncatedNormalisedExclusiveEnd, Object value) throws GeneraliseArrayStoreException { + CompilerDirectives.transferToInterpreter(); + throw new GeneraliseArrayStoreException(); + } + + @Override + public void setRangeArray(int normalisedBegin, int normalisedExclusiveEnd, ArrayStore other) throws GeneraliseArrayStoreException { + CompilerDirectives.transferToInterpreter(); + throw new GeneraliseArrayStoreException(); + } + + @Override + public void insert(int normalisedIndex, Object value) throws GeneraliseArrayStoreException { + CompilerDirectives.transferToInterpreter(); + throw new GeneraliseArrayStoreException(); + } + + @Override + public void push(Object value) throws GeneraliseArrayStoreException { + CompilerDirectives.transferToInterpreter(); + throw new GeneraliseArrayStoreException(); + } + + @Override + public Object deleteAt(int normalisedIndex) { + throw new UnsupportedOperationException(); + } + + @Override + public ArrayStore dup() { + return this; + } + + @Override + public boolean contains(Object value) { + if (value instanceof Integer) { + final int intValue = (int) value; + return first == intValue || second == intValue; + } else { + return false; + } + } + + @Override + public ArrayStore generalizeFor(Object type) { + return new ObjectArrayStore(toObjectArray()); + } + + @Override + public Object getIndicativeValue() { + return 0; + } + + @Override + protected void setCapacityByCopying(int newCapacity) { + throw new UnsupportedOperationException(); + } + + @Override + protected void setCapacityWithNewArray(int newCapacity) { + throw new UnsupportedOperationException(); + } + + @Override + protected Object getValuesArrayObject() { + return new int[]{first, second}; + } + + @Override + public Object[] toObjectArray() { + return new Object[]{first, second}; + } + + @Override + public boolean equals(ArrayStore other) { + if (other instanceof FixnumImmutablePairArrayStore) { + return equals((FixnumImmutablePairArrayStore) other); + } else { + return super.equals(other); + } + } + + public boolean equals(FixnumImmutablePairArrayStore other) { + if (other == null) { + return false; + } else if (other == this) { + return true; + } else { + return other.first == first && other.second == second; + } + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/GeneraliseArrayStoreException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/GeneraliseArrayStoreException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.array; + +import com.oracle.truffle.api.nodes.*; + +/** + * An exception that signals that an ArrayStore cannot store a given object because of its type, and + * that the store must be generalized to accommodate it. + */ +public class GeneraliseArrayStoreException extends SlowPathException { + + private static final long serialVersionUID = -7648655548414168177L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/ObjectArrayStore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/ObjectArrayStore.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.array; + +import java.util.*; + +import com.oracle.truffle.ruby.runtime.*; + +/** + * A store for an array of any objects. + */ +public final class ObjectArrayStore extends BaseArrayStore { + + private Object[] values; + + public ObjectArrayStore() { + this(new Object[]{}); + } + + public ObjectArrayStore(Object[] values) { + this.values = values; + size = values.length; + capacity = values.length; + } + + @Override + public Object get(int normalisedIndex) { + if (normalisedIndex >= size) { + return NilPlaceholder.INSTANCE; + } + + return values[normalisedIndex]; + } + + @Override + public ArrayStore getRange(int normalisedBegin, int normalisedExclusiveEnd) { + if (normalisedBegin >= size) { + return null; // Represents Nil + } + + return new ObjectArrayStore(Arrays.copyOfRange(values, normalisedBegin, normalisedExclusiveEnd)); + } + + @Override + public void set(int normalisedIndex, Object value) throws GeneraliseArrayStoreException { + if (normalisedIndex > size) { + final int originalLength = size; + createSpace(size, normalisedIndex - size + 1); + Arrays.fill(values, originalLength, normalisedIndex, NilPlaceholder.INSTANCE); + values[normalisedIndex] = value; + } else if (normalisedIndex == size) { + push(value); + } else { + values[normalisedIndex] = value; + } + } + + @Override + public void setRangeSingle(int normalisedBegin, int normalisedExclusiveEnd, Object value) throws GeneraliseArrayStoreException { + // Is the range the whole array? + + if (normalisedBegin == 0 && normalisedExclusiveEnd == size) { + // Reset length and set the value. + size = 1; + values[0] = value; + } else { + // Delete the range, except for the first value. + deleteSpace(normalisedBegin + 1, normalisedExclusiveEnd - normalisedBegin - 1); + + // Set the value we left in. + System.err.println(normalisedBegin + " in " + size + " with " + values.length); + values[normalisedBegin] = value; + } + } + + @Override + public void setRangeArray(int normalisedBegin, int normalisedExclusiveEnd, ArrayStore other) throws GeneraliseArrayStoreException { + setRangeArray(normalisedBegin, normalisedExclusiveEnd, (ObjectArrayStore) other.generalizeFor(null)); + } + + public void setRangeArray(int normalisedBegin, int normalisedExclusiveEnd, ObjectArrayStore other) { + setRangeArrayMatchingTypes(normalisedBegin, normalisedExclusiveEnd, other.values, other.size); + } + + @Override + public void insert(int normalisedIndex, Object value) throws GeneraliseArrayStoreException { + if (normalisedIndex > size) { + final int originalLength = size; + createSpaceAtEnd(normalisedIndex - size + 1); + Arrays.fill(values, originalLength, normalisedIndex, NilPlaceholder.INSTANCE); + values[normalisedIndex] = value; + } else { + createSpace(normalisedIndex, 1); + values[normalisedIndex] = value; + } + } + + @Override + public void push(Object value) throws GeneraliseArrayStoreException { + createSpaceAtEnd(1); + values[size - 1] = value; + } + + @Override + public Object deleteAt(int normalisedIndex) { + if (normalisedIndex >= size) { + return NilPlaceholder.INSTANCE; + } + + final Object value = values[normalisedIndex]; + + deleteSpace(normalisedIndex, 1); + + return value; + } + + @Override + public ArrayStore dup() { + return new ObjectArrayStore(Arrays.copyOf(values, size)); + } + + @Override + public boolean contains(Object value) { + for (int n = 0; n < size; n++) { + if (values[n].equals(value)) { + return true; + } + } + + return false; + } + + @Override + public ObjectArrayStore generalizeFor(Object type) { + return this; + } + + @Override + public Object getIndicativeValue() { + return null; + } + + @Override + protected void setCapacityByCopying(int newCapacity) { + values = Arrays.copyOf(values, newCapacity); + capacity = values.length; + } + + @Override + protected void setCapacityWithNewArray(int newCapacity) { + values = new Object[newCapacity]; + capacity = values.length; + } + + @Override + protected Object getValuesArrayObject() { + return values; + } + + public Object[] getValues() { + return values; + } + + @Override + public Object[] toObjectArray() { + if (values.length == size) { + return values; + } else { + return Arrays.copyOf(values, size); + } + } + + @Override + public boolean equals(ArrayStore other) { + if (other instanceof ObjectArrayStore) { + return equals((ObjectArrayStore) other); + } else { + return super.equals(other); + } + } + + public boolean equals(ObjectArrayStore other) { + if (other == null) { + return false; + } else if (other == this) { + return true; + } else if (other.size != size) { + return false; + } else if (other.capacity == capacity) { + return Arrays.equals(other.values, values); + } else { + for (int n = 0; n < size; n++) { + if (!other.values[n].equals(values[n])) { + return false; + } + } + + return true; + } + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/ObjectImmutablePairArrayStore.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/ObjectImmutablePairArrayStore.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.array; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A store for a pair of objects. + */ +public final class ObjectImmutablePairArrayStore extends BaseArrayStore { + + private final Object first; + private final Object second; + + public ObjectImmutablePairArrayStore(Object first, Object second) { + size = 2; + capacity = 2; + this.first = first; + this.second = second; + } + + @Override + public int size() { + return 2; + } + + @Override + public Object get(int normalisedIndex) { + switch (normalisedIndex) { + case 0: + return first; + case 1: + return second; + default: + return NilPlaceholder.INSTANCE; + } + } + + @Override + public ArrayStore getRange(int normalisedBegin, int truncatedNormalisedExclusiveEnd) { + if (normalisedBegin >= size) { + return null; // Represents Nil + } + + return new ObjectArrayStore(Arrays.copyOfRange(new Object[]{first, second}, normalisedBegin, truncatedNormalisedExclusiveEnd)); + } + + @Override + public void set(int normalisedIndex, Object value) throws GeneraliseArrayStoreException { + CompilerDirectives.transferToInterpreter(); + throw new GeneraliseArrayStoreException(); + } + + @Override + public void setRangeSingle(int normalisedBegin, int truncatedNormalisedExclusiveEnd, Object value) throws GeneraliseArrayStoreException { + CompilerDirectives.transferToInterpreter(); + throw new GeneraliseArrayStoreException(); + } + + @Override + public void setRangeArray(int normalisedBegin, int normalisedExclusiveEnd, ArrayStore other) throws GeneraliseArrayStoreException { + CompilerDirectives.transferToInterpreter(); + throw new GeneraliseArrayStoreException(); + } + + @Override + public void insert(int normalisedIndex, Object value) throws GeneraliseArrayStoreException { + CompilerDirectives.transferToInterpreter(); + throw new GeneraliseArrayStoreException(); + } + + @Override + public void push(Object value) throws GeneraliseArrayStoreException { + CompilerDirectives.transferToInterpreter(); + throw new GeneraliseArrayStoreException(); + } + + @Override + public Object deleteAt(int normalisedIndex) { + throw new UnsupportedOperationException(); + } + + @Override + public ArrayStore dup() { + return this; + } + + @Override + public boolean contains(Object value) { + return first.equals(value) || second.equals(value); + } + + @Override + public ArrayStore generalizeFor(Object type) { + return new ObjectArrayStore(toObjectArray()); + } + + @Override + public Object getIndicativeValue() { + return 0; + } + + @Override + protected void setCapacityByCopying(int newCapacity) { + throw new UnsupportedOperationException(); + } + + @Override + protected void setCapacityWithNewArray(int newCapacity) { + throw new UnsupportedOperationException(); + } + + @Override + protected Object getValuesArrayObject() { + return new Object[]{first, second}; + } + + @Override + public Object[] toObjectArray() { + return new Object[]{first, second}; + } + + @Override + public boolean equals(ArrayStore other) { + if (other instanceof ObjectImmutablePairArrayStore) { + return equals((ObjectImmutablePairArrayStore) other); + } else { + return super.equals(other); + } + } + + public boolean equals(ObjectImmutablePairArrayStore other) { + if (other == null) { + return false; + } else if (other == this) { + return true; + } else { + return other.first == first && other.second == second; + } + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/RubyArray.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/array/RubyArray.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.array; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.SlowPath; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Implements the Ruby {@code Array} class. + */ +@SuppressWarnings("unused") +public final class RubyArray extends RubyObject { + + public static class RubyArrayClass extends RubyClass { + + public RubyArrayClass(RubyClass objectClass) { + super(null, objectClass, "Array"); + } + + @Override + public RubyBasicObject newInstance() { + return new RubyArray(this); + } + + } + + @CompilationFinal private ArrayStore store; + + public RubyArray(RubyClass arrayClass) { + this(arrayClass, EmptyArrayStore.INSTANCE); + } + + public RubyArray(RubyClass arrayClass, ArrayStore store) { + super(arrayClass); + this.store = store; + } + + private static RubyArray selfAsArray(Object self) { + if (self instanceof RubyArray) { + return (RubyArray) self; + } else { + throw new IllegalStateException(); + } + } + + @CompilerDirectives.SlowPath + public static RubyArray specializedFromObject(RubyClass arrayClass, Object object) { + ArrayStore store; + + if (object instanceof Integer || object instanceof RubyFixnum) { + store = new FixnumArrayStore(new int[]{GeneralConversions.toFixnum(object)}); + } else { + store = new ObjectArrayStore(new Object[]{object}); + } + + return new RubyArray(arrayClass, store); + } + + /** + * Create a Ruby array from a Java array of objects, choosing the best store. + */ + @CompilerDirectives.SlowPath + public static RubyArray specializedFromObjects(RubyClass arrayClass, Object... objects) { + if (objects.length == 0) { + return new RubyArray(arrayClass); + } + + boolean canUseFixnum = true; + + for (Object object : objects) { + if (!(object instanceof Integer || object instanceof RubyFixnum)) { + canUseFixnum = false; + } + } + + ArrayStore store; + + if (canUseFixnum) { + final int[] values = new int[objects.length]; + + for (int n = 0; n < objects.length; n++) { + values[n] = GeneralConversions.toFixnum(objects[n]); + } + + store = new FixnumArrayStore(values); + } else { + store = new ObjectArrayStore(objects); + } + + return new RubyArray(arrayClass, store); + } + + public Object get(int index) { + return store.get(ArrayUtilities.normaliseIndex(store.size(), index)); + } + + public Object getRangeInclusive(int begin, int inclusiveEnd) { + final int l = store.size(); + final int normalisedInclusiveEnd = ArrayUtilities.normaliseIndex(l, inclusiveEnd); + return getRangeExclusive(begin, normalisedInclusiveEnd + 1); + } + + public Object getRangeExclusive(int begin, int exclusiveEnd) { + final int l = store.size(); + final int normalisedBegin = ArrayUtilities.normaliseIndex(l, begin); + final int truncatedNormalisedExclusiveEnd = ArrayUtilities.truncateNormalisedExclusiveIndex(l, ArrayUtilities.normaliseExclusiveIndex(l, exclusiveEnd)); + + final Object range = store.getRange(normalisedBegin, truncatedNormalisedExclusiveEnd); + + if (range == null) { + return new RubyArray(getRubyClass()); + } else { + return new RubyArray(getRubyClass(), (ArrayStore) range); + } + } + + public void set(int index, Object value) { + checkFrozen(); + + final int l = store.size(); + final int normalisedIndex = ArrayUtilities.normaliseIndex(l, index); + + try { + store.set(normalisedIndex, value); + } catch (GeneraliseArrayStoreException e) { + store = store.generalizeFor(value); + + try { + store.set(normalisedIndex, value); + } catch (GeneraliseArrayStoreException ex) { + throwSecondGeneraliseException(); + } + } + } + + public void setRangeSingleInclusive(int begin, int inclusiveEnd, Object value) { + final int l = store.size(); + final int normalisedInclusiveEnd = ArrayUtilities.normaliseIndex(l, inclusiveEnd); + setRangeSingleExclusive(begin, normalisedInclusiveEnd + 1, value); + } + + public void setRangeSingleExclusive(int begin, int exclusiveEnd, Object value) { + checkFrozen(); + + final int l = store.size(); + final int normalisedBegin = ArrayUtilities.normaliseIndex(l, begin); + final int truncatedNormalisedExclusiveEnd = ArrayUtilities.truncateNormalisedExclusiveIndex(l, ArrayUtilities.normaliseExclusiveIndex(l, exclusiveEnd)); + + try { + store.setRangeSingle(normalisedBegin, truncatedNormalisedExclusiveEnd, value); + } catch (GeneraliseArrayStoreException e) { + store = store.generalizeFor(value); + + try { + store.setRangeSingle(normalisedBegin, truncatedNormalisedExclusiveEnd, value); + } catch (GeneraliseArrayStoreException ex) { + throwSecondGeneraliseException(); + } + } + } + + public void setRangeArrayInclusive(int begin, int inclusiveEnd, RubyArray other) { + final int l = store.size(); + final int normalisedInclusiveEnd = ArrayUtilities.normaliseIndex(l, inclusiveEnd); + setRangeArrayExclusive(begin, normalisedInclusiveEnd + 1, other); + } + + public void setRangeArrayExclusive(int begin, int exclusiveEnd, RubyArray other) { + checkFrozen(); + + final int l = store.size(); + final int normalisedBegin = ArrayUtilities.normaliseIndex(l, begin); + final int normalisedExclusiveEnd = ArrayUtilities.normaliseExclusiveIndex(l, exclusiveEnd); + + try { + store.setRangeArray(normalisedBegin, normalisedExclusiveEnd, other.store); + } catch (GeneraliseArrayStoreException e) { + store = store.generalizeFor(other.store.getIndicativeValue()); + + try { + store.setRangeArray(normalisedBegin, normalisedExclusiveEnd, other.store); + } catch (GeneraliseArrayStoreException ex) { + throwSecondGeneraliseException(); + } + } + } + + public void insert(int index, Object value) { + checkFrozen(); + + final int l = store.size(); + final int normalisedIndex = ArrayUtilities.normaliseIndex(l, index); + + try { + store.insert(normalisedIndex, value); + } catch (GeneraliseArrayStoreException e) { + store = store.generalizeFor(value); + + try { + store.insert(normalisedIndex, value); + } catch (GeneraliseArrayStoreException ex) { + throwSecondGeneraliseException(); + } + } + } + + public void push(Object value) { + checkFrozen(); + + if (store instanceof EmptyArrayStore) { + /* + * Normally we want to transfer to interpreter to generalize an array store, but the + * special case of an empty array is common, will never cause rewrites and has a simple + * implementation, so treat it as a special case. + */ + store = ((EmptyArrayStore) store).generalizeFor(value); + } + + try { + store.push(value); + } catch (GeneraliseArrayStoreException e) { + store = store.generalizeFor(value); + + try { + store.push(value); + } catch (GeneraliseArrayStoreException ex) { + throw new IllegalStateException("Generalised to support a specific value, but value still rejected by store"); + } + } + } + + public void unshift(Object value) { + insert(0, value); + } + + public Object deleteAt(int index) { + checkFrozen(); + + final int l = store.size(); + final int normalisedIndex = ArrayUtilities.normaliseIndex(l, index); + + return store.deleteAt(normalisedIndex); + } + + @Override + @CompilerDirectives.SlowPath + public Object dup() { + return new RubyArray(getRubyClass(), store.dup()); + } + + public ArrayStore getArrayStore() { + return store; + } + + public List asList() { + final RubyArray array = this; + + return new AbstractList() { + + @Override + public Object get(int n) { + return array.get(n); + } + + @Override + public int size() { + return array.size(); + } + + }; + } + + public Object[] toObjectArray() { + return store.toObjectArray(); + } + + private static void throwSecondGeneraliseException() { + CompilerAsserts.neverPartOfCompilation(); + throw new RuntimeException("Generalised based on a value, but the new store also rejected that value."); + } + + public int size() { + return store.size(); + } + + public boolean contains(Object value) { + return store.contains(value); + } + + /** + * Recursive Cartesian product. + *

+ * The Array#product method is supposed to be able to take a block, to which it yields tuples as + * they are produced, so it might be worth abstracting this method into sending tuples to some + * interface, which either adds them to an array, or yields them to the block. + */ + @SlowPath + public static RubyArray product(RubyClass arrayClass, RubyArray[] arrays, int l) { + if (arrays.length - l == 1) { + final RubyArray firstArray = arrays[0]; + + final RubyArray tuples = new RubyArray(arrayClass); + + for (int i = 0; i < firstArray.size(); i++) { + final RubyArray tuple = new RubyArray(arrayClass); + tuple.push(firstArray.get(i)); + tuples.push(tuple); + } + + return tuples; + } else { + final RubyArray intermediateTuples = product(arrayClass, arrays, l - 1); + final RubyArray lastArray = arrays[l - 1]; + + final RubyArray tuples = new RubyArray(arrayClass); + + for (int n = 0; n < intermediateTuples.size(); n++) { + for (int i = 0; i < lastArray.size(); i++) { + final RubyArray tuple = (RubyArray) ((RubyArray) intermediateTuples.get(n)).dup(); + tuple.push(lastArray.get(i)); + tuples.push(tuple); + } + } + + return tuples; + } + } + + public boolean equals(RubyArray other) { + if (other == null) { + return false; + } else if (other == this) { + return true; + } else { + return store.equals(other.store); + } + } + + @Override + public boolean equals(Object other) { + if (other instanceof RubyArray) { + return equals((RubyArray) other); + } else { + return false; + } + } + + @Override + public int hashCode() { + getRubyClass().getContext().implementationMessage("Array#hash returns nonsense"); + return 0; + } + + public RubyArray relativeComplement(RubyArray other) { + // TODO(cs): specialize for different stores + + final RubyArray result = new RubyArray(getRubyClass().getContext().getCoreLibrary().getArrayClass()); + + for (Object value : asList()) { + if (!other.contains(value)) { + result.push(value); + } + } + + return result; + } + + public String join(String separator) { + final StringBuilder builder = new StringBuilder(); + + for (int n = 0; n < size(); n++) { + if (n > 0) { + builder.append(separator); + } + + builder.append(get(n).toString()); + } + + return builder.toString(); + } + + public static String join(Object[] parts, String separator) { + final StringBuilder builder = new StringBuilder(); + + for (int n = 0; n < parts.length; n++) { + if (n > 0) { + builder.append(separator); + } + + builder.append(parts[n].toString()); + } + + return builder.toString(); + } + + public void flattenTo(RubyArray result) { + for (int n = 0; n < size(); n++) { + final Object value = get(n); + + if (value instanceof RubyArray) { + ((RubyArray) value).flattenTo(result); + } else { + result.push(value); + } + } + } + + public boolean isEmpty() { + return store.size() == 0; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/range/FixnumRange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/range/FixnumRange.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.range; + +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * A range that has {@code Fixnum} begin and end. + */ +public class FixnumRange extends RubyRange { + + private final int begin; + private final int end; + private final boolean excludeEnd; + + public FixnumRange(RubyClass rangeClass, int begin, int end, boolean excludeEnd) { + super(rangeClass); + this.begin = begin; + this.end = end; + this.excludeEnd = excludeEnd; + } + + @Override + public String toString() { + if (excludeEnd) { + return begin + "..." + end; + } else { + return begin + ".." + end; + } + } + + @Override + public RubyArray toArray() { + final int length = getLength(); + + if (length < 0) { + return new RubyArray(getRubyClass().getContext().getCoreLibrary().getArrayClass()); + } else { + final int[] values = new int[length]; + + for (int n = 0; n < length; n++) { + values[n] = begin + n; + } + + return new RubyArray(getRubyClass().getContext().getCoreLibrary().getArrayClass(), new FixnumArrayStore(values)); + } + } + + private int getLength() { + if (excludeEnd) { + return end - begin; + } else { + return end - begin + 1; + } + } + + public final int getBegin() { + return begin; + } + + public final int getEnd() { + return end; + } + + public final int getInclusiveEnd() { + if (excludeEnd) { + return end - 1; + } else { + return end; + } + } + + public final int getExclusiveEnd() { + if (excludeEnd) { + return end; + } else { + return end + 1; + } + } + + @Override + public boolean doesExcludeEnd() { + return excludeEnd; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + begin; + result = prime * result + end; + result = prime * result + (excludeEnd ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof FixnumRange)) { + return false; + } + FixnumRange other = (FixnumRange) obj; + if (begin != other.begin) { + return false; + } + if (end != other.end) { + return false; + } + if (excludeEnd != other.excludeEnd) { + return false; + } + return true; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/range/ObjectRange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/range/ObjectRange.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.range; + +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +public class ObjectRange extends RubyRange { + + private final Object begin; + private final Object end; + private final boolean excludeEnd; + + public ObjectRange(RubyClass rangeClass, Object begin, Object end, boolean excludeEnd) { + super(rangeClass); + this.begin = begin; + this.end = end; + this.excludeEnd = excludeEnd; + } + + @Override + public RubyArray toArray() { + throw new UnsupportedOperationException(); + } + + public Object getBegin() { + return begin; + } + + public Object getEnd() { + return end; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((begin == null) ? 0 : begin.hashCode()); + result = prime * result + ((end == null) ? 0 : end.hashCode()); + result = prime * result + (excludeEnd ? 1231 : 1237); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ObjectRange)) { + return false; + } + ObjectRange other = (ObjectRange) obj; + if (begin == null) { + if (other.begin != null) { + return false; + } + } else if (!begin.equals(other.begin)) { + return false; + } + if (end == null) { + if (other.end != null) { + return false; + } + } else if (!end.equals(other.end)) { + return false; + } + if (excludeEnd != other.excludeEnd) { + return false; + } + return true; + } + + @Override + public boolean doesExcludeEnd() { + // TODO Auto-generated method stub + return false; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/range/RubyRange.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/range/RubyRange.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.core.range; + +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +public abstract class RubyRange extends RubyObject { + + public RubyRange(RubyClass rangeClass) { + super(rangeClass); + } + + public abstract RubyArray toArray(); + + public abstract boolean doesExcludeEnd(); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/MethodLocal.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/MethodLocal.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * A method and local variable identifier tuple. + */ +public class MethodLocal { + + private final UniqueMethodIdentifier method; + private final String local; + + public MethodLocal(UniqueMethodIdentifier method, String local) { + super(); + this.method = method; + this.local = local; + } + + @Override + public String toString() { + return "MethodLocal [method=" + method + ", local=" + local + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((local == null) ? 0 : local.hashCode()); + result = prime * result + ((method == null) ? 0 : method.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof MethodLocal)) { + return false; + } + MethodLocal other = (MethodLocal) obj; + if (local == null) { + if (other.local != null) { + return false; + } + } else if (!local.equals(other.local)) { + return false; + } + if (method == null) { + if (other.method != null) { + return false; + } + } else if (!method.equals(other.method)) { + return false; + } + return true; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakAfterProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakAfterProbe.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A Ruby probe for invoking a breakpoint shell after a child execution method completes. + */ +public final class RubyBreakAfterProbe extends RubyProbe { + + public RubyBreakAfterProbe(RubyContext context) { + super(context); + } + + @Override + public void leave(Node astNode, VirtualFrame frame) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, boolean result) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, int result) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, double result) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, Object result) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + + @Override + public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakBeforeProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyBreakBeforeProbe.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A probe for invoking a breakpoint shell before a child execution method. + */ +public final class RubyBreakBeforeProbe extends RubyProbe { + + public RubyBreakBeforeProbe(RubyContext context) { + super(context); + } + + @Override + public void enter(Node astNode, VirtualFrame frame) { + context.getDebugManager().haltedAt(astNode, frame.materialize()); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyDebugManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyDebugManager.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.nodes.instrument.*; +import com.oracle.truffle.api.nodes.instrument.InstrumentationProbeNode.ProbeChain; +import com.oracle.truffle.api.source.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * Manager for Ruby AST execution. + */ +public final class RubyDebugManager implements DebugManager { + + // TODO (mlvdv) no REPL support yet for debugging "locals"; only lines + + private static enum BreakpointStatus { + + /** + * Created for a source location but not yet attached for some legitimate reason: new and + * not yet attached; new and the source file hasn't been loaded yet; old and the source file + * is in the process of being reloaded. + */ + PENDING("Pending"), + + /** + * Has an active break probe in the AST. + */ + ATTACHED("Active"), + + /** + * Should be attached, but the line location cannot be found in the source. + */ + ERROR("Error: line not found"), + + /** + * Abandoned, not attached. + */ + RETIRED("Retired"); + + private final String name; + + BreakpointStatus(String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + } + + /** + * Map: Source lines ==> source chains known to be at line locations in an AST. + */ + private final Map linesToProbeChains = new HashMap<>(); + + private final Set loadedSources = new HashSet<>(); + + private Source beingLoaded = null; + + /** + * Map: Source lines ==> attached Breakpoints/procs to be activated before execution at line. + */ + private final Map linesToBreakpoints = new TreeMap<>(); + + /** + * Map: Method locals in AST ==> Method local assignments where breakpoints can be attached. + */ + private final Map localsToProbeChains = new HashMap<>(); + + /** + * Map: Method locals ==> Breakpoints & procs to be activated after assignment to a local. + */ + private final Map localsToAttachedBreakpoints = new HashMap<>(); + + private final RubyContext context; + + public RubyDebugManager(RubyContext context) { + this.context = context; + } + + public void notifyStartLoading(Source source) { + + beingLoaded = source; + + // Forget all the probe chains from previous loading + final List locations = new ArrayList<>(); + for (SourceLineLocation lineLocation : linesToProbeChains.keySet()) { + if (lineLocation.getSource().equals(beingLoaded)) { + locations.add(lineLocation); + } + } + for (SourceLineLocation lineLocation : locations) { + linesToProbeChains.remove(lineLocation); + } + + // Forget whatever we knew, and detach from old AST/ProbeChain if needed + for (RubyLineBreakpoint breakpoint : linesToBreakpoints.values()) { + if (breakpoint.getSourceLineLocation().getSource().equals(beingLoaded)) { + breakpoint.setPending(); + } + } + } + + public void notifyFinishedLoading(Source source) { + assert source == beingLoaded; + + // Any pending breakpoints are now erroneous, didn't find the line. + for (RubyLineBreakpoint breakpoint : linesToBreakpoints.values()) { + if (breakpoint.getSourceLineLocation().getSource().equals(beingLoaded)) { + if (breakpoint.status == BreakpointStatus.PENDING) { + breakpoint.setError(); + } + } + } + + loadedSources.add(source); + beingLoaded = null; + + } + + /** + * Notifies the manager about creation of a newly created probe chain associated with a proxy + * for an AST node at a specific location in the source text. + */ + public void registerProbeChain(SourceSection sourceSection, ProbeChain probeChain) { + + assert sourceSection.getSource().equals(beingLoaded); + + // Remember this probe chain, indexed by line number + final SourceLineLocation lineLocation = new SourceLineLocation(sourceSection.getSource(), sourceSection.getStartLine()); + linesToProbeChains.put(lineLocation, probeChain); + + final RubyLineBreakpoint breakpoint = linesToBreakpoints.get(lineLocation); + if (breakpoint != null && breakpoint.location.equals(lineLocation)) { + // We only register while we're loading; + // While we're loading, there should only be pending breakpoints for this source + assert breakpoint.status == BreakpointStatus.PENDING; + + // Found a line/probeChain where a pending breakpoint should be set + breakpoint.attach(probeChain); + } + } + + @Override + public RubyLineBreakpoint setBreakpoint(SourceLineLocation lineLocation) { + + RubyLineBreakpoint breakpoint = linesToBreakpoints.get(lineLocation); + + if (breakpoint != null) { + switch (breakpoint.status) { + case ATTACHED: + throw new RuntimeException("Breakpoint already set at line " + lineLocation); + + case PENDING: + case ERROR: + throw new RuntimeException("Breakpoint already pending at line " + lineLocation); + + default: + assert false; + } + } else { + breakpoint = new RubyLineBreakpoint(lineLocation, new RubyBreakBeforeProbe(context)); + linesToBreakpoints.put(lineLocation, breakpoint); + + final ProbeChain probeChain = linesToProbeChains.get(lineLocation); + if (probeChain != null) { + breakpoint.attach(probeChain); + } + } + + return breakpoint; + } + + @Override + public RubyLineBreakpoint setConditionalBreakpoint(SourceLineLocation lineLocation, String condition) { + throw new UnsupportedOperationException("conditional breakpoints not yet supported"); + } + + @Override + public LineBreakpoint[] getBreakpoints() { + return linesToBreakpoints.values().toArray(new LineBreakpoint[0]); + } + + @Override + public void removeBreakpoint(SourceLineLocation lineLocation) { + final RubyLineBreakpoint breakpoint = linesToBreakpoints.get(lineLocation); + if (breakpoint == null) { + throw new RuntimeException("No break/proc located at line " + lineLocation); + } + linesToBreakpoints.remove(lineLocation); + breakpoint.retire(); + } + + /** + * Sets a Ruby proc of no arguments to be run before a specified line is executed. + */ + public void setLineProc(SourceLineLocation lineLocation, RubyProc proc) { + + RubyLineBreakpoint breakpoint = linesToBreakpoints.get(lineLocation); + + if (breakpoint != null) { + switch (breakpoint.status) { + case ATTACHED: + throw new RuntimeException("Breakpoint already set at line " + lineLocation); + + case PENDING: + case ERROR: + throw new RuntimeException("Breakpoint already pending at line " + lineLocation); + + default: + assert false; + } + } else { + breakpoint = new RubyLineBreakpoint(lineLocation, new RubyProcBeforeProbe(context, proc)); + linesToBreakpoints.put(lineLocation, breakpoint); + + final ProbeChain probeChain = linesToProbeChains.get(lineLocation); + if (probeChain != null) { + breakpoint.attach(probeChain); + } + } + } + + /** + * Registers the chain of probes associated with a method local variable in the AST. + */ + public void registerLocalDebugProxy(UniqueMethodIdentifier methodIdentifier, String localName, ProbeChain probeChain) { + final MethodLocal methodLocal = new MethodLocal(methodIdentifier, localName); + localsToProbeChains.put(methodLocal, probeChain); + } + + /** + * Sets a breakpoint after assignment to a method local variable in the AST. + */ + public void setLocalBreak(UniqueMethodIdentifier methodIdentifier, String localName) { + final MethodLocal methodLocal = new MethodLocal(methodIdentifier, localName); + ProbeChain probeChain = localsToProbeChains.get(methodLocal); + if (probeChain == null) { + throw new RuntimeException("Can't find method local " + methodLocal); + } + RubyProbe probe = localsToAttachedBreakpoints.get(methodLocal); + if (probe != null) { + throw new RuntimeException("Breakpoint already set on method local " + methodLocal); + } + probe = new RubyBreakAfterProbe(context); + localsToAttachedBreakpoints.put(methodLocal, probe); + probeChain.appendProbe(probe); + } + + /** + * Sets a Ruby proc of one argument to be run after a method local assignment, passed the new + * value. + */ + public void setLocalProc(UniqueMethodIdentifier methodIdentifier, String localName, RubyProc proc) { + final MethodLocal methodLocal = new MethodLocal(methodIdentifier, localName); + ProbeChain probeChain = localsToProbeChains.get(methodLocal); + if (probeChain == null) { + throw new RuntimeException("Can't find method local " + methodLocal); + } + RubyProbe probe = localsToAttachedBreakpoints.get(methodLocal); + if (probe != null) { + throw new RuntimeException("Assignment proc already set on method local " + methodLocal); + } + probe = new RubyProcAfterProbe(context, proc); + localsToAttachedBreakpoints.put(methodLocal, probe); + probeChain.appendProbe(probe); + } + + /** + * Removes a break or proc on assignment to a method local variable in the AST. + */ + public void removeLocalProbe(UniqueMethodIdentifier methodIdentifier, String localName) { + final MethodLocal methodLocal = new MethodLocal(methodIdentifier, localName); + RubyProbe probe = localsToAttachedBreakpoints.get(methodLocal); + if (probe == null) { + throw new RuntimeException("No breakpoint set on method local " + methodLocal); + } + localsToProbeChains.get(methodLocal).removeProbe(probe); + localsToAttachedBreakpoints.remove(methodLocal); + } + + /** + * Receives notification of a suspended execution context; execution resumes when this method + * returns. + * + * @param astNode a guest language AST node that represents the current execution site, assumed + * not to be any kind of {@link InstrumentationNode}, + * @param frame execution frame at the site where execution suspended + */ + public void haltedAt(Node astNode, MaterializedFrame frame) { + context.haltedAt(astNode, frame); + } + + private static final class RubyLineBreakpoint implements DebugManager.LineBreakpoint, Comparable { + + private final SourceLineLocation location; + + private RubyProbe probe; // non-null until RETIRED, but may get replaced. + private ProbeChain probeChain = null; // only non-null when ATTACHED + private BreakpointStatus status = BreakpointStatus.PENDING; + + public RubyLineBreakpoint(SourceLineLocation location, RubyProbe probe) { + this.location = location; + this.probe = probe; + } + + public SourceLineLocation getSourceLineLocation() { + return location; + } + + // ensure sorted by location + public int compareTo(Object o) { + final RubyLineBreakpoint other = (RubyLineBreakpoint) o; + return location.compareTo(other.location); + } + + @Override + public String getDebugStatus() { + return status == null ? "" : status.name; + } + + private void attach(ProbeChain chain) { + assert status == BreakpointStatus.PENDING; + + probeChain = chain; + probeChain.appendProbe(probe); + + status = BreakpointStatus.ATTACHED; + } + + private void setPending() { + switch (status) { + case ATTACHED: + detach(); + // TODO (mlvdv) replace the probe + status = BreakpointStatus.PENDING; + break; + case ERROR: + status = BreakpointStatus.PENDING; + break; + case PENDING: + break; + case RETIRED: + assert false; + break; + default: + assert false; + } + } + + public void setError() { + assert status == BreakpointStatus.PENDING; + + status = BreakpointStatus.ERROR; + } + + private void detach() { + assert status == BreakpointStatus.ATTACHED; + + probeChain.removeProbe(probe); + probeChain = null; + + status = BreakpointStatus.PENDING; + } + + private void retire() { + + if (probeChain != null) { + probeChain.removeProbe(probe); + } + probe = null; + probeChain = null; + + status = BreakpointStatus.RETIRED; + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProbe.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.nodes.instrument.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A "probe node" implemented specifically for the Ruby implementation; subclasses need only + * override those members of {@link InstrumentationProbeEvents} for which some action is needed. + */ +public abstract class RubyProbe extends InstrumentationProbeNode.DefaultProbeNode { + + protected final RubyContext context; + + public RubyProbe(RubyContext context) { + this.context = context; + assert context != null; + } + + public RubyContext getContext() { + return context; + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcAfterProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcAfterProbe.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * A probe for instrumenting a Ruby program with a Ruby procedure to run on the return value from + * node execution. + */ +public final class RubyProcAfterProbe extends RubyProbe { + + private final RubyProc proc; + + public RubyProcAfterProbe(RubyContext context, RubyProc proc) { + super(context); + this.proc = proc; + } + + @Override + public void leave(Node astNode, VirtualFrame frame) { + proc.call(frame.pack()); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, boolean result) { + proc.call(frame.pack(), result); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, int result) { + proc.call(frame.pack(), result); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, double result) { + proc.call(frame.pack(), result); + } + + @Override + public void leave(Node astNode, VirtualFrame frame, Object result) { + proc.call(frame.pack(), result); + } + + @Override + public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + proc.call(frame.pack()); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcBeforeProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyProcBeforeProbe.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * A probe for instrumenting a Ruby program with a Ruby procedure to run before a call. + */ +public final class RubyProcBeforeProbe extends RubyProbe { + + private final RubyProc proc; + + public RubyProcBeforeProbe(RubyContext context, RubyProc proc) { + super(context); + this.proc = proc; + } + + @Override + public void enter(Node astNode, VirtualFrame frame) { + proc.call(frame.pack()); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyTraceProbe.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/debug/RubyTraceProbe.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.debug; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.subsystems.*; + +/** + * A "trace" probe that has no runtime cost until activated, at which time it invokes a trace + * message. + */ +public final class RubyTraceProbe extends RubyProbe { + + private final Assumption notTracingAssumption; + + @CompilerDirectives.CompilationFinal private boolean tracingEverEnabled = false; + + public RubyTraceProbe(RubyContext context) { + super(context); + this.notTracingAssumption = context.getTraceManager().getNotTracingAssumption(); + } + + @Override + public void enter(Node astNode, VirtualFrame frame) { + if (!tracingEverEnabled) { + try { + notTracingAssumption.check(); + } catch (InvalidAssumptionException e) { + tracingEverEnabled = true; + } + } + final TraceManager traceManager = context.getTraceManager(); + if (tracingEverEnabled && traceManager.hasTraceProc()) { + final SourceSection sourceSection = astNode.getEncapsulatingSourceSection(); + traceManager.trace("line", sourceSection.getSource().getName(), sourceSection.getStartLine(), 0, null, null); + } + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/lookup/LookupFork.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/lookup/LookupFork.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.lookup; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * A fork in the lookup graph. Look at first and then look at second. + */ +public class LookupFork implements LookupNode { + + private LookupNode first; + private LookupNode second; + + public LookupFork(LookupNode first, LookupNode second) { + this.first = first; + this.second = second; + } + + public boolean setClassVariableIfAlreadySet(String variableName, Object value) { + if (first.setClassVariableIfAlreadySet(variableName, value)) { + return true; + } + + return second.setClassVariableIfAlreadySet(variableName, value); + } + + @Override + public Object lookupConstant(String constantName) { + final Object firstResult = first.lookupConstant(constantName); + + if (firstResult != null) { + return firstResult; + } + + return second.lookupConstant(constantName); + } + + @Override + public Object lookupClassVariable(String classVariable) { + final Object firstResult = first.lookupClassVariable(classVariable); + + if (firstResult != null) { + return firstResult; + } + + return second.lookupClassVariable(classVariable); + } + + @Override + public RubyMethod lookupMethod(String methodName) { + final RubyMethod firstResult = first.lookupMethod(methodName); + + if (firstResult != null) { + return firstResult; + } + + return second.lookupMethod(methodName); + } + + @Override + public Assumption getUnmodifiedAssumption() { + return new UnionAssumption(first.getUnmodifiedAssumption(), second.getUnmodifiedAssumption()); + } + + public LookupNode getFirst() { + return first; + } + + public LookupNode getSecond() { + return second; + } + + public Set getClassVariables() { + final Set classVariables = new HashSet<>(); + classVariables.addAll(first.getClassVariables()); + classVariables.addAll(second.getClassVariables()); + return classVariables; + } + + public void getMethods(Map methods) { + second.getMethods(methods); + first.getMethods(methods); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/lookup/LookupNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/lookup/LookupNode.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.lookup; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * A node in the lookup graph. We abstract from modules and classes because with includes and + * singletons and so on there are more nodes in the graph than there are classes and modules. + */ +public interface LookupNode { + + boolean setClassVariableIfAlreadySet(String variableName, Object value); + + Object lookupConstant(String constantName); + + Object lookupClassVariable(String variableName); + + RubyMethod lookupMethod(String methodName); + + Assumption getUnmodifiedAssumption(); + + Set getClassVariables(); + + void getMethods(Map methods); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/lookup/LookupTerminal.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/lookup/LookupTerminal.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.lookup; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * A terminal in the lookup graph. + */ +public class LookupTerminal implements LookupNode { + + public static final LookupTerminal INSTANCE = new LookupTerminal(); + + public boolean setClassVariableIfAlreadySet(String variableName, Object value) { + return false; + } + + @Override + public Object lookupConstant(String constantName) { + return null; + } + + @Override + public Object lookupClassVariable(String constantName) { + return null; + } + + @Override + public RubyMethod lookupMethod(String methodName) { + return null; + } + + @Override + public Assumption getUnmodifiedAssumption() { + return AlwaysValidAssumption.INSTANCE; + } + + public Set getClassVariables() { + return Collections.emptySet(); + } + + public void getMethods(Map methods) { + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/Arity.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/Arity.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.methods; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; + +/** + * Represents the arity, or parameter contract, of a method. + */ +public class Arity { + + private final int minimum; + public static final int NO_MINIMUM = 0; + + private final int maximum; + public static final int NO_MAXIMUM = Integer.MAX_VALUE; + + public static final Arity NO_ARGS = new Arity(0, 0); + public static final Arity ONE_ARG = new Arity(1, 1); + + public Arity(int minimum, int maximum) { + this.minimum = minimum; + this.maximum = maximum; + } + + public void checkArguments(RubyContext context, Object[] arguments) { + if (arguments.length < minimum || arguments.length > maximum) { + CompilerDirectives.transferToInterpreter(); + throw new RaiseException(context.getCoreLibrary().argumentError(arguments.length, minimum)); + } + } + + public int getMinimum() { + return minimum; + } + + public int getMaximum() { + return maximum; + } + + @Override + public String toString() { + return String.format("Arity(%d, %d)", minimum, maximum); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/CallTargetMethodImplementation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/CallTargetMethodImplementation.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.methods; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +public class CallTargetMethodImplementation implements MethodImplementation { + + private final CallTarget callTarget; + private final MaterializedFrame declarationFrame; + + public CallTargetMethodImplementation(CallTarget callTarget, MaterializedFrame declarationFrame) { + assert callTarget != null; + + this.callTarget = callTarget; + this.declarationFrame = declarationFrame; + } + + public Object call(PackedFrame caller, Object self, RubyProc block, Object... args) { + assert RubyContext.shouldObjectBeVisible(self); + assert RubyContext.shouldObjectsBeVisible(args); + + RubyArguments arguments = new RubyArguments(declarationFrame, self, block, args); + + final Object result = callTarget.call(caller, arguments); + + assert RubyContext.shouldObjectBeVisible(result); + + return result; + } + + public MaterializedFrame getDeclarationFrame() { + return declarationFrame; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/MethodImplementation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/MethodImplementation.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.methods; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.core.*; + +public interface MethodImplementation { + + Object call(PackedFrame caller, Object self, RubyProc block, Object... args); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/RubyMethod.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/RubyMethod.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.methods; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Any kind of Ruby method - so normal methods in classes and modules, but also blocks, procs, + * lambdas and native methods written in Java. + */ +public class RubyMethod { + + private final SourceSection sourceSection; + private final RubyModule declaringModule; + private final UniqueMethodIdentifier uniqueIdentifier; + private final String intrinsicName; + private final String name; + private final Visibility visibility; + private final boolean undefined; + + private final MethodImplementation implementation; + + public RubyMethod(SourceSection sourceSection, RubyModule declaringModule, UniqueMethodIdentifier uniqueIdentifier, String intrinsicName, String name, Visibility visibility, boolean undefined, + MethodImplementation implementation) { + this.sourceSection = sourceSection; + this.declaringModule = declaringModule; + this.uniqueIdentifier = uniqueIdentifier; + this.intrinsicName = intrinsicName; + this.name = name; + this.visibility = visibility; + this.undefined = undefined; + this.implementation = implementation; + } + + public Object call(PackedFrame caller, Object self, RubyProc block, Object... args) { + assert RubyContext.shouldObjectBeVisible(self); + assert RubyContext.shouldObjectsBeVisible(args); + + final Object result = implementation.call(caller, self, block, args); + + assert RubyContext.shouldObjectBeVisible(result); + + return result; + } + + public SourceSection getSourceSection() { + return sourceSection; + } + + public UniqueMethodIdentifier getUniqueIdentifier() { + return uniqueIdentifier; + } + + public String getIntrinsicName() { + return intrinsicName; + } + + public String getName() { + return name; + } + + public Visibility getVisibility() { + return visibility; + } + + public boolean isUndefined() { + return undefined; + } + + public MethodImplementation getImplementation() { + return implementation; + } + + public RubyMethod withNewName(String newName) { + if (newName.equals(name)) { + return this; + } + + return new RubyMethod(sourceSection, declaringModule, uniqueIdentifier, intrinsicName, newName, visibility, undefined, implementation); + } + + public RubyMethod withNewVisibility(Visibility newVisibility) { + if (newVisibility == visibility) { + return this; + } + + return new RubyMethod(sourceSection, declaringModule, uniqueIdentifier, intrinsicName, name, newVisibility, undefined, implementation); + } + + public RubyMethod withDeclaringModule(RubyModule newDeclaringModule) { + if (newDeclaringModule == declaringModule) { + return this; + } + + return new RubyMethod(sourceSection, newDeclaringModule, uniqueIdentifier, intrinsicName, name, visibility, undefined, implementation); + } + + public RubyMethod undefined() { + if (undefined) { + return this; + } + + return new RubyMethod(sourceSection, declaringModule, uniqueIdentifier, intrinsicName, name, visibility, true, implementation); + } + + public boolean isVisibleTo(RubyBasicObject caller) { + if (caller instanceof RubyModule) { + if (isVisibleTo((RubyModule) caller)) { + return true; + } + } + + if (isVisibleTo(caller.getRubyClass())) { + return true; + } + + if (isVisibleTo(caller.getSingletonClass())) { + return true; + } + + return false; + } + + private boolean isVisibleTo(RubyModule module) { + switch (visibility) { + case PUBLIC: + return true; + + case PROTECTED: + return true; + + case PRIVATE: + if (module == declaringModule) { + return true; + } + + if (module.getSingletonClass() == declaringModule) { + return true; + } + + if (module.getParentModule() != null && isVisibleTo(module.getParentModule())) { + return true; + } + + return false; + + default: + return false; + } + } + + @Override + public String toString() { + return implementation.toString(); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/UniqueMethodIdentifier.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/UniqueMethodIdentifier.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.methods; + +/** + * An object that uniquely identifies a method as it appears in the source code, and that follows + * the method as a normal method, when it becomes a proc, when it changes access or is aliased etc. + */ +public class UniqueMethodIdentifier { + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/Visibility.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/methods/Visibility.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.methods; + +/** + * Represents the visibility of a method. + */ +public enum Visibility { + PUBLIC, PROTECTED, PRIVATE +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/FixnumStorageLocation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/FixnumStorageLocation.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.objects; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A storage location for Fixnums. + */ +public class FixnumStorageLocation extends PrimitiveStorageLocation { + + public FixnumStorageLocation(ObjectLayout objectLayout, long offset, int mask) { + super(objectLayout, offset, mask); + } + + @Override + public Object read(RubyBasicObject object, boolean condition) { + try { + return readFixnum(object, condition); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + + public int readFixnum(RubyBasicObject object, boolean condition) throws UnexpectedResultException { + if (isSet(object)) { + return CompilerDirectives.unsafeGetInt(object, offset, condition, this); + } else { + throw new UnexpectedResultException(NilPlaceholder.INSTANCE); + } + } + + @Override + public void write(RubyBasicObject object, Object value) throws GeneralizeStorageLocationException { + if (value instanceof Integer) { + writeFixnum(object, (int) value); + } else if (value instanceof NilPlaceholder) { + markAsUnset(object); + } else { + throw new GeneralizeStorageLocationException(); + } + } + + public void writeFixnum(RubyBasicObject object, int value) { + CompilerDirectives.unsafePutInt(object, offset, value, null); + markAsSet(object); + } + + @Override + public Class getStoredClass() { + return Integer.class; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/FloatStorageLocation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/FloatStorageLocation.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.objects; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * A storage location for Floats. + */ +public class FloatStorageLocation extends PrimitiveStorageLocation { + + public FloatStorageLocation(ObjectLayout objectLayout, long offset, int mask) { + super(objectLayout, offset, mask); + } + + @Override + public Object read(RubyBasicObject object, boolean condition) { + try { + return readFloat(object, condition); + } catch (UnexpectedResultException e) { + return e.getResult(); + } + } + + public double readFloat(RubyBasicObject object, boolean condition) throws UnexpectedResultException { + if (isSet(object)) { + return CompilerDirectives.unsafeGetDouble(object, offset, condition, this); + } else { + throw new UnexpectedResultException(NilPlaceholder.INSTANCE); + } + } + + @Override + public void write(RubyBasicObject object, Object value) throws GeneralizeStorageLocationException { + if (value instanceof Double) { + writeFloat(object, (double) value); + } else if (value instanceof NilPlaceholder) { + markAsUnset(object); + } else { + throw new GeneralizeStorageLocationException(); + } + } + + public void writeFloat(RubyBasicObject object, Double value) { + CompilerDirectives.unsafePutDouble(object, offset, value, null); + markAsSet(object); + } + + @Override + public Class getStoredClass() { + return Double.class; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/GeneralizeStorageLocationException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/GeneralizeStorageLocationException.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.objects; + +/** + * Indicates that a storage location cannot store the type of value that you asked it to. + */ +public class GeneralizeStorageLocationException extends Exception { + + private static final long serialVersionUID = -5136358171098229961L; + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/ObjectLayout.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/ObjectLayout.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.objects; + +import java.lang.reflect.*; +import java.util.*; +import java.util.Map.Entry; + +import sun.misc.*; + +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.nodes.NodeUtil.*; +import com.oracle.truffle.ruby.runtime.*; + +/** + * Maps names of instance variables to storage locations, which are either the offset of a primitive + * field in {@code RubyBasicObject}, or an index into an object array in {@code RubyBasicObject}. + * Object layouts are chained, with each having zero or one parents. + *

+ * Object layouts are immutable, with the methods for adding new instance variables of generalizing + * the type of existing instance variables returning new object layouts. + */ +public class ObjectLayout { + + public static final ObjectLayout EMPTY = new ObjectLayout("(empty)"); + + private final String originHint; + + private final ObjectLayout parent; + + private final Map storageLocations = new HashMap<>(); + + private final int primitiveStorageLocationsUsed; + private final int objectStorageLocationsUsed; + + public static final long FIRST_OFFSET = getFirstOffset(); + + private ObjectLayout(String originHint) { + this.originHint = originHint; + this.parent = null; + primitiveStorageLocationsUsed = 0; + objectStorageLocationsUsed = 0; + } + + public ObjectLayout(String originHint, RubyContext context, ObjectLayout parent) { + this(originHint, context, parent, new HashMap()); + } + + public ObjectLayout(String originHint, RubyContext context, ObjectLayout parent, Map storageTypes) { + this.originHint = originHint; + this.parent = parent; + + // Start our offsets from where the parent ends + + int primitiveStorageLocationIndex; + int objectStorageLocationIndex; + + if (parent == null) { + primitiveStorageLocationIndex = 0; + objectStorageLocationIndex = 0; + } else { + primitiveStorageLocationIndex = parent.primitiveStorageLocationsUsed; + objectStorageLocationIndex = parent.objectStorageLocationsUsed; + } + + // Go through the variables we've been asked to store + + for (Entry entry : storageTypes.entrySet()) { + // TODO(cs): what if parent has it, but we need a more general type? + + final String name = entry.getKey(); + final Class type = entry.getValue(); + + if (parent == null || parent.findStorageLocation(name) == null) { + boolean canStoreInPrimitive = false; + int primitivesNeeded = 0; + + if (type == Integer.class) { + canStoreInPrimitive = true; + primitivesNeeded = 1; + } else if (type == Double.class) { + canStoreInPrimitive = true; + primitivesNeeded = 2; + } + + if (canStoreInPrimitive && primitiveStorageLocationIndex + primitivesNeeded <= RubyBasicObject.PRIMITIVE_STORAGE_LOCATIONS_COUNT) { + final long offset = FIRST_OFFSET + Unsafe.ARRAY_INT_INDEX_SCALE * primitiveStorageLocationIndex; + final int mask = 1 << primitiveStorageLocationIndex; + + StorageLocation newStorageLocation = null; + + if (type == Integer.class) { + newStorageLocation = new FixnumStorageLocation(this, offset, mask); + } else if (type == Double.class) { + newStorageLocation = new FloatStorageLocation(this, offset, mask); + } + + storageLocations.put(entry.getKey(), newStorageLocation); + primitiveStorageLocationIndex += primitivesNeeded; + } else { + if (canStoreInPrimitive && context.getConfiguration().getPrintSpiltInstanceVariables()) { + context.implementationMessage("instance variable %s of type %s spilt due to lack of space", name, type.getName()); + } + + final ObjectStorageLocation newStorageLocation = new ObjectStorageLocation(this, objectStorageLocationIndex); + storageLocations.put(entry.getKey(), newStorageLocation); + objectStorageLocationIndex++; + } + } + } + + primitiveStorageLocationsUsed = primitiveStorageLocationIndex; + objectStorageLocationsUsed = objectStorageLocationIndex; + } + + /** + * Create a new version of this layout, but with a different parent. The new parent probably + * comes from the same Ruby class as it did, but it's a new layout because layouts are + * immutable, so modifications to the superclass yields a new layout. + */ + public ObjectLayout renew(RubyContext context, ObjectLayout newParent) { + return new ObjectLayout(originHint + ".renewed", context, newParent, getStorageTypes()); + } + + /** + * Create a new version of this layout but with a new variable. + */ + public ObjectLayout withNewVariable(RubyContext context, String name, Class type) { + final Map storageTypes = getStorageTypes(); + storageTypes.put(name, type); + return new ObjectLayout(originHint + ".withnew", context, parent, storageTypes); + } + + /** + * Create a new version of this layout but with an existing variable generalized to support any + * type. + */ + public ObjectLayout withGeneralisedVariable(RubyContext context, String name) { + return withNewVariable(context, name, Object.class); + } + + /** + * Get a map of instance variable names to the type that they store. + */ + public Map getStorageTypes() { + Map storageTypes = new HashMap<>(); + + for (Entry entry : storageLocations.entrySet()) { + final String name = entry.getKey(); + final StorageLocation storageLocation = entry.getValue(); + + if (storageLocation.getStoredClass() != null) { + storageTypes.put(name, storageLocation.getStoredClass()); + } + } + + return storageTypes; + } + + /** + * Get a map of instance variable names to the type that they store, but including both this + * layout and all parent layouts. + */ + public Map getAllStorageLocations() { + final Map allStorageLocations = new HashMap<>(); + + allStorageLocations.putAll(storageLocations); + + if (parent != null) { + allStorageLocations.putAll(parent.getAllStorageLocations()); + } + + return allStorageLocations; + } + + /** + * Find a storage location from a name, including in parents. + */ + public StorageLocation findStorageLocation(String name) { + final StorageLocation storageLocation = storageLocations.get(name); + + if (storageLocation != null) { + return storageLocation; + } + + if (parent == null) { + return null; + } + + return parent.findStorageLocation(name); + } + + public int getObjectStorageLocationsUsed() { + return objectStorageLocationsUsed; + } + + /** + * Does this layout include another layout? That is, is that other layout somewhere in the chain + * of parents? We say 'include' because all of the variables in a parent layout are available in + * your layout as well. + */ + public boolean contains(ObjectLayout other) { + ObjectLayout layout = this; + + do { + if (other == layout) { + return true; + } + + layout = layout.parent; + } while (layout != null); + + return false; + } + + public String getOriginHint() { + return originHint; + } + + private static long getFirstOffset() { + try { + final Field fieldOffsetProviderField = NodeUtil.class.getDeclaredField("unsafeFieldOffsetProvider"); + fieldOffsetProviderField.setAccessible(true); + final FieldOffsetProvider fieldOffsetProvider = (FieldOffsetProvider) fieldOffsetProviderField.get(null); + + final Field firstPrimitiveField = RubyBasicObject.class.getDeclaredField("primitiveStorageLocation01"); + return fieldOffsetProvider.objectFieldOffset(firstPrimitiveField); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/ObjectStorageLocation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/ObjectStorageLocation.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.objects; + +import com.oracle.truffle.ruby.runtime.*; + +/** + * A storage location for any object. + */ +public class ObjectStorageLocation extends StorageLocation { + + private final int index; + + public ObjectStorageLocation(ObjectLayout objectLayout, int index) { + super(objectLayout); + this.index = index; + } + + @Override + public boolean isSet(RubyBasicObject object) { + return object.objectStorageLocations[index] != null; + } + + @Override + public Object read(RubyBasicObject object, boolean condition) { + final Object result = object.objectStorageLocations[index]; + + if (result == null) { + return NilPlaceholder.INSTANCE; + } else { + return result; + } + } + + @Override + public void write(RubyBasicObject object, Object value) { + object.objectStorageLocations[index] = value; + } + + @Override + public Class getStoredClass() { + return Object.class; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/PrimitiveStorageLocation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/PrimitiveStorageLocation.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.objects; + +/** + * A storage location that uses one of the primitive fields in {@code RubyBasObject}. + */ +public abstract class PrimitiveStorageLocation extends StorageLocation { + + protected final long offset; + protected final int mask; + + protected PrimitiveStorageLocation(ObjectLayout objectLayout, long offset, int mask) { + super(objectLayout); + this.offset = offset; + this.mask = mask; + } + + @Override + public boolean isSet(RubyBasicObject object) { + return (object.primitiveSetMap & mask) != 0; + } + + protected void markAsSet(RubyBasicObject object) { + object.primitiveSetMap |= mask; + } + + protected void markAsUnset(RubyBasicObject object) { + object.primitiveSetMap &= ~mask; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/RubyBasicObject.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/RubyBasicObject.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.objects; + +import java.util.*; +import java.util.Map.Entry; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.lookup.*; +import com.oracle.truffle.ruby.runtime.methods.*; + +/** + * Represents the Ruby {@code BasicObject} class - the root of the Ruby class hierarchy. + */ +public class RubyBasicObject { + + @CompilationFinal protected RubyClass rubyClass; + protected RubyClass rubySingletonClass; + + protected LookupNode lookupNode; + + protected long objectID = -1; + + public boolean hasPrivateLayout = false; + private ObjectLayout objectLayout; + + public static final int PRIMITIVE_STORAGE_LOCATIONS_COUNT = 14; + protected int primitiveStorageLocation01; + protected int primitiveStorageLocation02; + protected int primitiveStorageLocation03; + protected int primitiveStorageLocation04; + protected int primitiveStorageLocation05; + protected int primitiveStorageLocation06; + protected int primitiveStorageLocation07; + protected int primitiveStorageLocation08; + protected int primitiveStorageLocation09; + protected int primitiveStorageLocation10; + protected int primitiveStorageLocation11; + protected int primitiveStorageLocation12; + protected int primitiveStorageLocation13; + protected int primitiveStorageLocation14; + + // A bit map to indicate which primitives are set, so that they can be Nil + protected int primitiveSetMap; + + protected Object[] objectStorageLocations; + + public RubyBasicObject(RubyClass rubyClass) { + if (rubyClass != null) { + unsafeSetRubyClass(rubyClass); + + if (rubyClass.getContext().getConfiguration().getFullObjectSpace()) { + rubyClass.getContext().getObjectSpaceManager().add(this); + } + } + } + + public void initialize() { + } + + public LookupNode getLookupNode() { + return lookupNode; + } + + public RubyClass getRubyClass() { + assert rubyClass != null; + return rubyClass; + } + + public boolean hasPrivateLayout() { + return hasPrivateLayout; + } + + public ObjectLayout getObjectLayout() { + return objectLayout; + } + + public ObjectLayout getUpdatedObjectLayout() { + updateLayout(); + return objectLayout; + } + + /** + * Does this object have an instance variable defined? + */ + public boolean isInstanceVariableDefined(String name) { + if (!hasPrivateLayout && objectLayout != rubyClass.getObjectLayoutForInstances()) { + updateLayout(); + } + + return objectLayout.findStorageLocation(name) != null; + } + + /** + * Set an instance variable to be a value. Slow path. + */ + public void setInstanceVariable(String name, Object value) { + CompilerAsserts.neverPartOfCompilation(); + + // If the object's layout doesn't match the class, update + + if (!hasPrivateLayout && objectLayout != rubyClass.getObjectLayoutForInstances()) { + updateLayout(); + } + + // Find the storage location + + StorageLocation storageLocation = objectLayout.findStorageLocation(name); + + if (storageLocation == null) { + /* + * It doesn't exist, so create a new layout for the class that includes it and update + * the layout of this object. + */ + + rubyClass.setObjectLayoutForInstances(rubyClass.getObjectLayoutForInstances().withNewVariable(rubyClass.getContext(), name, value.getClass())); + updateLayout(); + + storageLocation = objectLayout.findStorageLocation(name); + } + + // Try to write to that storage location + + try { + storageLocation.write(this, value); + } catch (GeneralizeStorageLocationException e) { + /* + * It might not be able to store the type that we passed, if not generalize the class's + * layout and update the layout of this object. + */ + + rubyClass.setObjectLayoutForInstances(rubyClass.getObjectLayoutForInstances().withGeneralisedVariable(rubyClass.getContext(), name)); + updateLayout(); + + storageLocation = objectLayout.findStorageLocation(name); + + // Try to write to the generalized storage location + + try { + storageLocation.write(this, value); + } catch (GeneralizeStorageLocationException e1) { + // We know that we just generalized it, so this should not happen + throw new RuntimeException("Generalised an instance variable, but it still rejected the value"); + } + } + } + + /** + * Get the value of an instance variable, or Nil if it isn't defined. Slow path. + */ + public Object getInstanceVariable(String name) { + CompilerAsserts.neverPartOfCompilation(); + + // If the object's layout doesn't match the class, update + + if (!hasPrivateLayout && objectLayout != rubyClass.getObjectLayoutForInstances()) { + updateLayout(); + } + + // Find the storage location + + final StorageLocation storageLocation = objectLayout.findStorageLocation(name); + + // Get the value + + if (storageLocation == null) { + return NilPlaceholder.INSTANCE; + } + + return storageLocation.read(this, true); + } + + public String[] getInstanceVariableNames() { + final Set instanceVariableNames = getInstanceVariables().keySet(); + return instanceVariableNames.toArray(new String[instanceVariableNames.size()]); + } + + public RubyClass getSingletonClass() { + if (rubySingletonClass == null) { + /* + * The object a of class A has a singleton class a' of class Class, with name + * #>, and with superclass that is A. + * + * irb(main):001:0> class A; end + * + * => nil + * + * irb(main):002:0> a = A.new + * + * => # + * + * irb(main):003:0> a.singleton_class + * + * => #> + * + * irb(main):004:0> a.singleton_class.class + * + * => Class + * + * irb(main):005:0> a.singleton_class.superclass + * + * => A + */ + + rubySingletonClass = new RubyClass(rubyClass.getParentModule(), rubyClass, String.format("#>", rubyClass.getName(), getObjectID()), true); + + lookupNode = new LookupFork(rubySingletonClass, rubyClass); + } + + return rubySingletonClass; + } + + public long getObjectID() { + if (objectID == -1) { + objectID = rubyClass.getContext().getNextObjectID(); + } + + return objectID; + } + + public String inspect() { + return toString(); + } + + /** + * Get a map of all instance variables. + */ + protected Map getInstanceVariables() { + if (objectLayout == null) { + return Collections.emptyMap(); + } + + final Map instanceVariableMap = new HashMap<>(); + + for (Entry entry : objectLayout.getAllStorageLocations().entrySet()) { + final String name = entry.getKey(); + final StorageLocation storageLocation = entry.getValue(); + + if (storageLocation.isSet(this)) { + instanceVariableMap.put(name, storageLocation.read(this, true)); + } + } + + return instanceVariableMap; + } + + /** + * Set instance variables from a map. + */ + protected void setInstanceVariables(Map instanceVariables) { + assert instanceVariables != null; + + if (objectLayout == null) { + updateLayout(); + } + + for (Entry entry : instanceVariables.entrySet()) { + final StorageLocation storageLocation = objectLayout.findStorageLocation(entry.getKey()); + assert storageLocation != null; + + try { + storageLocation.write(this, entry.getValue()); + } catch (GeneralizeStorageLocationException e) { + throw new RuntimeException("Should not have to be generalising when setting instance variables - " + entry.getValue().getClass().getName() + ", " + + storageLocation.getStoredClass().getName()); + } + } + } + + /** + * Update the layout of this object to match that of its class. + */ + @CompilerDirectives.SlowPath + public void updateLayout() { + // Get the current values of instance variables + + final Map instanceVariableMap = getInstanceVariables(); + + // Use the layout of the class + + objectLayout = rubyClass.getObjectLayoutForInstances(); + + // Make all primitives as unset + + primitiveSetMap = 0; + + // Create a new array for objects + + allocateObjectStorageLocations(); + + // Restore values + + setInstanceVariables(instanceVariableMap); + } + + private void allocateObjectStorageLocations() { + final int objectStorageLocationsUsed = objectLayout.getObjectStorageLocationsUsed(); + + if (objectStorageLocationsUsed == 0) { + objectStorageLocations = null; + } else { + objectStorageLocations = new Object[objectStorageLocationsUsed]; + } + } + + public void switchToPrivateLayout() { + final RubyContext context = getRubyClass().getContext(); + + final Map instanceVariables = getInstanceVariables(); + + hasPrivateLayout = true; + objectLayout = ObjectLayout.EMPTY; + + for (Entry entry : instanceVariables.entrySet()) { + objectLayout = objectLayout.withNewVariable(context, entry.getKey(), entry.getValue().getClass()); + } + + setInstanceVariables(instanceVariables); + } + + public void extend(RubyModule module) { + getSingletonClass().include(module); + } + + @Override + public String toString() { + return "#<" + rubyClass.getName() + ":0x" + Long.toHexString(getObjectID()) + ">"; + } + + public boolean hasSingletonClass() { + return rubySingletonClass != null; + } + + public Object send(String name, RubyProc block, Object... args) { + final RubyMethod method = getLookupNode().lookupMethod(name); + + if (method == null || method.isUndefined()) { + throw new RaiseException(getRubyClass().getContext().getCoreLibrary().noMethodError(name, toString())); + } + + return method.call(null, this, block, args); + } + + public void unsafeSetRubyClass(RubyClass newRubyClass) { + assert rubyClass == null; + + rubyClass = newRubyClass; + lookupNode = rubyClass; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/StorageLocation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/StorageLocation.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.objects; + +/** + * A storage location that abstracts the method for reading and writing values. + */ +public abstract class StorageLocation { + + private ObjectLayout objectLayout; + + protected StorageLocation(ObjectLayout objectLayout) { + this.objectLayout = objectLayout; + } + + public abstract boolean isSet(RubyBasicObject object); + + public abstract Object read(RubyBasicObject object, boolean condition); + + public abstract void write(RubyBasicObject object, Object value) throws GeneralizeStorageLocationException; + + public abstract Class getStoredClass(); + + public ObjectLayout getObjectLayout() { + return objectLayout; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/Unboxable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/objects/Unboxable.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.objects; + +/** + * An object that can be losslessly unboxed back to a more primitive value. + */ +public interface Unboxable { + + Object unbox(); + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/AtExitManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/AtExitManager.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.subsystems; + +import java.util.*; + +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Manages at_exit callbacks. + */ +public class AtExitManager { + + private final List blocks = new ArrayList<>(); + + public void add(RubyProc block) { + blocks.add(block); + } + + public void run() { + final ListIterator iterator = blocks.listIterator(blocks.size()); + + while (iterator.hasPrevious()) { + iterator.previous().call(null); + } + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/FeatureManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/FeatureManager.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.subsystems; + +import java.io.*; +import java.net.*; +import java.util.*; + +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * Manages the features loaded into Ruby. This basically means which library files are loaded, but + * Ruby often talks about requiring features, not files. + * + */ +public class FeatureManager { + + private RubyContext context; + + private final Set requiredFiles = new HashSet<>(); + + public FeatureManager(RubyContext context) { + this.context = context; + } + + public boolean require(String feature) throws IOException { + // Some features are handled specially + + if (context.getConfiguration().getRubyVersion().is19OrLater() && feature.equals("continuation")) { + // We always load continuations + return true; + } + + if (feature.equals("stringio")) { + context.implementationMessage("stringio not yet implemented"); + return true; + } + + if (feature.equals("rbconfig")) { + // Kernel#rbconfig is always there + return true; + } + + if (feature.equals("pp")) { + // Kernel#pretty_inspect is always there + return true; + } + + // Get the load path + + final Object loadPathObject = context.getCoreLibrary().getGlobalVariablesObject().getInstanceVariable("$:"); + + if (!(loadPathObject instanceof RubyArray)) { + throw new RuntimeException("$: is not an array"); + } + + final List loadPath = ((RubyArray) loadPathObject).asList(); + + // Try as a full path + + if (requireInPath("", feature)) { + return true; + } + + // Try each load path in turn + + for (Object pathObject : loadPath) { + final String path = pathObject.toString(); + + if (requireInPath(path, feature)) { + return true; + } + } + + // Didn't find the feature + + throw new RaiseException(context.getCoreLibrary().loadErrorCannotLoad(feature)); + } + + public boolean requireInPath(String path, String feature) throws IOException { + if (requireFile(feature)) { + return true; + } + + if (requireFile(feature + ".rb")) { + return true; + } + + if (requireFile(path + File.separator + feature)) { + return true; + } + + if (requireFile(path + File.separator + feature + ".rb")) { + return true; + } + + return false; + } + + private boolean requireFile(String fileName) throws IOException { + if (requiredFiles.contains(fileName)) { + return true; + } + + /* + * There is unfortunately no way to check if a string is a file path, or a URL. file:foo.txt + * is a valid file name, as well as a valid URL. We try as a file path first. + */ + + if (new File(fileName).isFile()) { + context.loadFile(fileName); + requiredFiles.add(fileName); + return true; + } else { + URL url; + + try { + url = new URL(fileName); + } catch (MalformedURLException e) { + return false; + } + + InputStream inputStream; + + try { + inputStream = url.openConnection().getInputStream(); + } catch (IOException e) { + return false; + } + + context.load(context.getSourceManager().get(url.toString(), inputStream)); + requiredFiles.add(fileName); + return true; + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/FiberManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/FiberManager.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.subsystems; + +import java.util.*; +import java.util.concurrent.*; + +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Manages Ruby {@code Fiber} objects. + */ +public class FiberManager { + + private final RubyFiber rootFiber; + private RubyFiber currentFiber; + + private final Set runningFibers = Collections.newSetFromMap(new ConcurrentHashMap()); + + public FiberManager(RubyContext context) { + rootFiber = new RubyFiber(context.getCoreLibrary().getFiberClass(), this, context.getThreadManager()); + currentFiber = rootFiber; + } + + public RubyFiber getCurrentFiber() { + return currentFiber; + } + + public void setCurrentFiber(RubyFiber fiber) { + currentFiber = fiber; + } + + public void registerFiber(RubyFiber fiber) { + runningFibers.add(fiber); + } + + public void unregisterFiber(RubyFiber fiber) { + runningFibers.remove(fiber); + } + + public void shutdown() { + for (RubyFiber fiber : runningFibers) { + fiber.shutdown(); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/ObjectSpaceManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/ObjectSpaceManager.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.subsystems; + +import java.lang.ref.*; +import java.util.*; +import java.util.concurrent.*; + +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +/** + * Supports the Ruby {@code ObjectSpace} module. Object IDs are lazily allocated {@code long} + * values, mapped to objects with a weak hash map. Finalizers are implemented with weak references + * and reference queues, and are run in a dedicated Ruby thread (but not a dedicated Java thread). + */ +public class ObjectSpaceManager { + + private class FinalizerReference extends WeakReference { + + public List finalizers = new LinkedList<>(); + + public FinalizerReference(RubyBasicObject object, ReferenceQueue queue) { + super(object, queue); + } + + public void addFinalizer(RubyProc proc) { + finalizers.add(proc); + } + + public List getFinalizers() { + return finalizers; + } + + public void clearFinalizers() { + finalizers = new LinkedList<>(); + } + + } + + private final RubyContext context; + + // TODO(cs): this is wrong - WeakHashMap is not weak in the value + private final WeakHashMap objects = new WeakHashMap<>(); + + private final Map finalizerReferences = new WeakHashMap<>(); + private final ReferenceQueue finalizerQueue = new ReferenceQueue<>(); + private RubyThread finalizerThread; + private Thread finalizerJavaThread; + private boolean stop; + private CountDownLatch finished = new CountDownLatch(1); + + public ObjectSpaceManager(RubyContext context) { + this.context = context; + } + + public void add(RubyBasicObject object) { + objects.put(object.getObjectID(), object); + } + + public RubyBasicObject lookupId(long id) { + return objects.get(id); + } + + public void defineFinalizer(RubyBasicObject object, RubyProc proc) { + // Record the finalizer against the object + + FinalizerReference finalizerReference = finalizerReferences.get(object); + + if (finalizerReference == null) { + finalizerReference = new FinalizerReference(object, finalizerQueue); + finalizerReferences.put(object, finalizerReference); + } + + finalizerReference.addFinalizer(proc); + + // If there is no finalizer thread, start one + + if (finalizerThread == null) { + finalizerThread = new RubyThread(context.getCoreLibrary().getThreadClass(), context.getThreadManager()); + + finalizerThread.initialize(new Runnable() { + + @Override + public void run() { + runFinalizers(); + } + + }); + } + } + + public void undefineFinalizer(RubyBasicObject object) { + final FinalizerReference finalizerReference = finalizerReferences.get(object); + + if (finalizerReference != null) { + finalizerReference.clearFinalizers(); + } + } + + private void runFinalizers() { + // Run in a loop + + while (true) { + // Is there a finalizer ready to immediately run? + + FinalizerReference finalizerReference = (FinalizerReference) finalizerQueue.poll(); + + if (finalizerReference != null) { + runFinalizers(finalizerReference); + continue; + } + + // Check if we've been asked to stop + + if (stop) { + break; + } + + // Leave the global lock and wait on the finalizer queue + + final RubyThread runningThread = context.getThreadManager().leaveGlobalLock(); + finalizerJavaThread = Thread.currentThread(); + + try { + finalizerReference = (FinalizerReference) finalizerQueue.remove(); + } catch (InterruptedException e) { + continue; + } finally { + context.getThreadManager().enterGlobalLock(runningThread); + } + + runFinalizers(finalizerReference); + } + + finished.countDown(); + } + + private static void runFinalizers(FinalizerReference finalizerReference) { + try { + for (RubyProc proc : finalizerReference.getFinalizers()) { + proc.call(null); + } + } catch (Exception e) { + // MRI seems to silently ignore exceptions in finalizers + } + } + + public void shutdown() { + context.getThreadManager().enterGlobalLock(finalizerThread); + + try { + // Tell the finalizer thread to stop and wait for it to do so + + if (finalizerThread != null) { + stop = true; + + if (finalizerJavaThread != null) { + finalizerJavaThread.interrupt(); + } + + context.getThreadManager().leaveGlobalLock(); + + try { + finished.await(); + } catch (InterruptedException e) { + } finally { + context.getThreadManager().enterGlobalLock(finalizerThread); + } + } + + // Run any finalizers for objects that are still live + + for (FinalizerReference finalizerReference : finalizerReferences.values()) { + runFinalizers(finalizerReference); + } + } finally { + context.getThreadManager().leaveGlobalLock(); + } + } + + public Collection getObjects() { + return objects.values(); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/ThreadManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/ThreadManager.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.subsystems; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.locks.*; + +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Manages Ruby {@code Thread} objects. + */ +public class ThreadManager { + + private final ReentrantLock globalLock = new ReentrantLock(); + + private final RubyThread rootThread; + private RubyThread currentThread; + + private final Set runningThreads = Collections.newSetFromMap(new ConcurrentHashMap()); + + public ThreadManager(RubyContext context) { + rootThread = new RubyThread(context.getCoreLibrary().getFiberClass(), this); + runningThreads.add(rootThread); + enterGlobalLock(rootThread); + } + + /** + * Enters the global lock. Reentrant, but be aware that Ruby threads are not one-to-one with + * Java threads. Needs to be told which Ruby thread is becoming active as it can't work this out + * from the current Java thread. Remember to call {@link #leaveGlobalLock} again before + * blocking. + */ + public void enterGlobalLock(RubyThread thread) { + globalLock.lock(); + currentThread = thread; + } + + /** + * Leaves the global lock, returning the Ruby thread which has just stopped being the current + * thread. Remember to call {@link #enterGlobalLock} again with that returned thread before + * executing any Ruby code. You probably want to use this with a {@code finally} statement to + * make sure that happens + */ + public RubyThread leaveGlobalLock() { + if (!globalLock.isHeldByCurrentThread()) { + throw new RuntimeException("You don't own this lock!"); + } + + final RubyThread result = currentThread; + globalLock.unlock(); + return result; + } + + public RubyThread getCurrentThread() { + return currentThread; + } + + public void registerThread(RubyThread thread) { + runningThreads.add(thread); + } + + public void unregisterThread(RubyThread thread) { + runningThreads.remove(thread); + } + + public void shutdown() { + for (RubyThread thread : runningThreads) { + thread.shutdown(); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/TraceManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/subsystems/TraceManager.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.runtime.subsystems; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; + +/** + * Manages trace events and calls the user's trace method if one is set. + *

+ * Once tracing has been enabled via {@link #setTraceProc(RubyProc)}, the underlying instrumentation + * remains in effect, along with performance impact. + */ +public final class TraceManager { + + private RubyContext context; + + private final AssumedValue traceProc = new AssumedValue<>("trace-proc", null); + private boolean suspended; + + private String lastFile; + private int lastLine; + + private final Assumption notTracingAssumption = Truffle.getRuntime().createAssumption("tracing-disabled"); + + public TraceManager(RubyContext context) { + this.context = context; + } + + /** + * Produce a trace; it is a runtime error if {@link #hasTraceProc()}{@code == false}. + */ + @CompilerDirectives.SlowPath + public void trace(String event, String file, int line, long objectId, RubyBinding binding, String className) { + // If tracing is suspended, stop here + + if (suspended) { + return; + } + + // If the file and line haven't changed since the last trace, stop here + + if (file.equals(lastFile) && line == lastLine) { + return; + } + + final RubyClass stringClass = context.getCoreLibrary().getStringClass(); + + // Suspend tracing while we run the trace proc + + suspended = true; + + try { + // Exceptions from within the proc propagate normally + + traceProc.get().call(null, new RubyString(stringClass, event), // + new RubyString(stringClass, file), // + line, // + GeneralConversions.fixnumOrBignum(objectId), // + GeneralConversions.instanceOrNil(binding), // + GeneralConversions.instanceOrNil(className)); + } finally { + // Resume tracing + + suspended = false; + } + + // Remember the last trace event file and line + + lastFile = file; + lastLine = line; + } + + /** + * Is there a "trace proc" in effect? + */ + public boolean hasTraceProc() { + return traceProc.get() != null; + } + + /** + * Gets the assumption that there has never yet been tracing enabled. Once the assumption is + * invalidated, tracing is presumed permanently enabled even if {@link #hasTraceProc()} returns + * {@code false}. + */ + public Assumption getNotTracingAssumption() { + return notTracingAssumption; + } + + public void setTraceProc(RubyProc newTraceProc) { + if (!context.getConfiguration().getTrace()) { + throw new RuntimeException("You need the --trace option to use tracing"); + } + + traceProc.set(newTraceProc); + lastFile = null; + lastLine = -1; + + notTracingAssumption.invalidate(); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.shell/src/com/oracle/truffle/ruby/shell/CommandLineOptions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.shell/src/com/oracle/truffle/ruby/shell/CommandLineOptions.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.shell; + +import java.util.*; + +import com.oracle.truffle.ruby.runtime.configuration.*; + +/** + * Options resulting from parsing a command line by {@link CommandLineParser}. + */ +public class CommandLineOptions { + + private final List preRequires; + private final String preChangeDirectory; + + private final List extraLoadPath; + + private final String programFile; + private final List commandLineScripts; + private final List programArgs; + private final List switchArgs; + + private final int recordSeparator; + private final boolean autosplit; + private final String autosplitPattern; + private final boolean checkSyntaxOnly; + private final boolean lineEndingProcessing; + private final boolean inPlaceEdit; + private final String inPlaceBackupExtension; + private final boolean implicitLoop; + private final boolean implicitSedLoop; + private final boolean importFromPath; + private final boolean stripMessage; + private final boolean useJLine; + + private final Configuration configuration; + + public CommandLineOptions(List preRequires, String preChangeDirectory, List extraLoadPath, String programFile, List commandLineScripts, List programArgs, + List switchArgs, int recordSeparator, boolean autosplit, String autosplitPattern, boolean checkSyntaxOnly, boolean lineEndingProcessing, boolean inPlaceEdit, + String inPlaceBackupExtension, boolean implicitLoop, boolean implicitSedLoop, boolean importFromPath, boolean stripMessage, boolean useJLine, Configuration configuration) { + this.preRequires = preRequires; + this.preChangeDirectory = preChangeDirectory; + this.extraLoadPath = extraLoadPath; + this.programFile = programFile; + this.commandLineScripts = commandLineScripts; + this.programArgs = programArgs; + this.switchArgs = switchArgs; + this.recordSeparator = recordSeparator; + this.autosplit = autosplit; + this.autosplitPattern = autosplitPattern; + this.checkSyntaxOnly = checkSyntaxOnly; + this.lineEndingProcessing = lineEndingProcessing; + this.inPlaceEdit = inPlaceEdit; + this.inPlaceBackupExtension = inPlaceBackupExtension; + this.implicitLoop = implicitLoop; + this.implicitSedLoop = implicitSedLoop; + this.importFromPath = importFromPath; + this.stripMessage = stripMessage; + this.useJLine = useJLine; + this.configuration = configuration; + } + + public List getPreRequires() { + return preRequires; + } + + public String getPreChangeDirectory() { + return preChangeDirectory; + } + + public List getExtraLoadPath() { + return extraLoadPath; + } + + public String getProgramFile() { + return programFile; + } + + public List getCommandLineScripts() { + return commandLineScripts; + } + + public List getProgramArgs() { + return programArgs; + } + + public List getSwitchArgs() { + return switchArgs; + } + + public int getRecordSeparator() { + return recordSeparator; + } + + public String getAutosplitPattern() { + return autosplitPattern; + } + + public boolean isAutosplit() { + return autosplit; + } + + public boolean isCheckSyntaxOnly() { + return checkSyntaxOnly; + } + + public boolean isLineEndingProcessing() { + return lineEndingProcessing; + } + + public boolean isInPlaceEdit() { + return inPlaceEdit; + } + + public String getInPlaceBackupExtension() { + return inPlaceBackupExtension; + } + + public boolean isImplicitLoop() { + return implicitLoop; + } + + public boolean isImplicitSedLoop() { + return implicitSedLoop; + } + + public boolean isImportFromPath() { + return importFromPath; + } + + public boolean isStripMessage() { + return stripMessage; + } + + public boolean useJLine() { + return useJLine; + } + + public Configuration getConfiguration() { + return configuration; + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.shell/src/com/oracle/truffle/ruby/shell/CommandLineParser.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.shell/src/com/oracle/truffle/ruby/shell/CommandLineParser.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.shell; + +import java.io.*; +import java.util.*; + +import com.oracle.truffle.ruby.runtime.configuration.*; + +/** + * MRI-compatible command line parser, producing {@link CommandLineOptions}. + */ +public abstract class CommandLineParser { + + /** + * Parse an MRI-compatible command line. + */ + public static CommandLineOptions parse(String[] args) { + assert args != null; + + final List preRequires = new ArrayList<>(); + String preChangeDirectory = null; + + final List extraLoadPath = new ArrayList<>(); + + String programFile = null; + final List commandLineScripts = new ArrayList<>(); + final List programArgs = new ArrayList<>(); + final List switchArgs = new ArrayList<>(); + + int recordSeparator = -1; + boolean autosplit = false; + String autosplitPattern = null; + boolean checkSyntaxOnly = false; + boolean lineEndingProcessing = false; + boolean inPlaceEdit = false; + String inPlaceBackupExtension = null; + boolean implicitLoop = false; + boolean implicitSedLoop = false; + boolean importFromPath = false; + boolean messageStrip = false; + boolean useJLine = true; + + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + + final List normalizedArgs = normalizeArgs(args); + + boolean stillRubyArgs = true; + boolean collectingSwitchArgs = false; + int n = 0; + + while (n < normalizedArgs.size()) { + final String arg = normalizedArgs.get(n); + + if (stillRubyArgs && arg.startsWith("-")) { + if (collectingSwitchArgs) { + switchArgs.add(arg); + } else if (arg.startsWith("-0")) { + if (arg.length() == 2) { + recordSeparator = 0; + } else { + recordSeparator = Integer.parseInt(arg.substring(2), 8); + } + } else if (arg.startsWith("-i")) { + inPlaceEdit = true; + + if (arg.length() > 2) { + inPlaceBackupExtension = arg.substring(2); + } + } else if (arg.startsWith("-W")) { + if (arg.length() == 2) { + configurationBuilder.setWarningLevel(2); + } else if (arg.startsWith("-Wlevel=")) { + configurationBuilder.setWarningLevel(Integer.parseInt(arg.substring("-Wlevel=".length()))); + } else { + throw new IllegalArgumentException("bad flag " + arg); + } + } else if (arg.startsWith("-T")) { + if (arg.length() == 2) { + configurationBuilder.setTaintCheckLevel(1); + } else if (arg.startsWith("-Tlevel=")) { + configurationBuilder.setTaintCheckLevel(Integer.parseInt(arg.substring("-Tlevel=".length()))); + } else { + throw new IllegalArgumentException("bad flag " + arg); + } + } else if (arg.startsWith("-x")) { + messageStrip = true; + + if (arg.length() > 2) { + preChangeDirectory = arg.substring(2); + } + } else { + switch (arg) { + case "-C": + case "-e": + case "-E": + case "-F": + case "-I": + case "-r": + case "--home": + if (n + 1 >= normalizedArgs.size()) { + throw new RuntimeException("Expecting value after " + arg); + } + } + + switch (arg) { + case "--": + stillRubyArgs = false; + break; + case "-a": + autosplit = true; + break; + case "-c": + checkSyntaxOnly = true; + break; + case "-C": + if (n + 1 >= normalizedArgs.size()) { + throw new RuntimeException("Need a directory path after -C"); + } + preChangeDirectory = normalizedArgs.get(n + 1); + n++; + break; + case "-d": + case "--debug": + configurationBuilder.setDebug(true); + break; + case "--no-debug": + configurationBuilder.setDebug(false); + break; + case "--trace": + configurationBuilder.setTrace(true); + break; + case "--no-trace": + configurationBuilder.setTrace(false); + break; + case "-e": + commandLineScripts.add(normalizedArgs.get(n + 1)); + n++; + break; + case "-E": + final String[] encodings = normalizedArgs.get(n + 1).split(":"); + configurationBuilder.setDefaultExternalEncoding(encodings[0]); + + if (encodings.length == 2) { + configurationBuilder.setDefaultInternalEncoding(encodings[1]); + } else { + throw new IllegalArgumentException("bad flag " + arg); + } + + n++; + break; + case "-F": + autosplitPattern = normalizedArgs.get(n + 1); + n++; + break; + case "-I": + extraLoadPath.add(normalizedArgs.get(n + 1)); + n++; + break; + case "-l": + lineEndingProcessing = true; + break; + case "-n": + implicitLoop = true; + break; + case "-r": + preRequires.add(normalizedArgs.get(n + 1)); + n++; + break; + case "-s": + collectingSwitchArgs = true; + break; + case "-p": + implicitSedLoop = true; + break; + case "-S": + importFromPath = true; + break; + case "-w": + configurationBuilder.setWarningLevel(1); + break; + case "-h": + case "--help": + help(System.out); + return null; + case "-v": + version(System.out); + configurationBuilder.setVerbose(true); + break; + case "--version": + version(System.out); + return null; + case "--copyright": + copyright(System.out); + return null; + case "--home": + configurationBuilder.setStandardLibrary(normalizedArgs.get(n + 1) + "/" + ConfigurationBuilder.JRUBY_STDLIB_JAR); + n++; + break; + case "--stdlib": + configurationBuilder.setStandardLibrary(normalizedArgs.get(n + 1)); + n++; + break; + case "--1.8": + configurationBuilder.setRubyVersion(RubyVersion.RUBY_18); + break; + case "--1.9": + configurationBuilder.setRubyVersion(RubyVersion.RUBY_19); + break; + case "--2.0": + configurationBuilder.setRubyVersion(RubyVersion.RUBY_20); + break; + case "--full-object-space": + configurationBuilder.setFullObjectSpace(true); + break; + case "--no-jline": + useJLine = false; + break; + case "--print-parse-tree": + configurationBuilder.setPrintParseTree(true); + break; + case "--print-executed-files": + configurationBuilder.setPrintExecutedFiles(true); + break; + case "--print-spilt-instance-variables": + configurationBuilder.setPrintSpiltInstanceVariables(true); + break; + case "--print-uninitialized-calls": + configurationBuilder.setPrintUninitializedCalls(true); + break; + case "--print-java-exceptions": + configurationBuilder.setPrintJavaExceptions(true); + break; + case "--print-ruby-exceptions": + configurationBuilder.setPrintRubyExceptions(true); + break; + default: + throw new IllegalArgumentException("unknown flag " + arg); + } + } + } else if (programFile == null) { + programFile = arg; + stillRubyArgs = false; + } else { + programArgs.add(arg); + } + + n++; + } + + return new CommandLineOptions(preRequires, preChangeDirectory, extraLoadPath, programFile, commandLineScripts, programArgs, switchArgs, recordSeparator, autosplit, autosplitPattern, + checkSyntaxOnly, lineEndingProcessing, inPlaceEdit, inPlaceBackupExtension, implicitLoop, implicitSedLoop, importFromPath, messageStrip, useJLine, new Configuration( + configurationBuilder)); + } + + /** + * Produce a canonical set of arguments that includes {@code $RUBYOPT} and has contractions such + * as a {@code -rdir} replaced with separate arguments {@code -r} and {@code dir} for simpler + * processing. + */ + private static List normalizeArgs(String[] args) { + assert args != null; + + // Arguments come from the main method arguments parameter and $RUBYOPT + + final List inputArgs = new ArrayList<>(); + + final String rubyoptVar = System.getenv("RUBYOPT"); + + if (rubyoptVar != null) { + /* + * TODO(cs): what we've got here is a string that we are supposed to treat as if it was + * an extra part of the command line, including more Ruby options. However, we just get + * the string and have to split it into arguments ourselves. Normally the shell does + * that, including lots of fancy quoting styles. Are we supposed to re-implement all of + * that? Otherwise arguments in RUBYOPT will be parsed differently to if they were + * actually on the command line. JRuby just splits like we do. I also think that's what + * MRI does, but is this correct? + */ + + inputArgs.addAll(Arrays.asList(rubyoptVar.split("\\s+"))); + } + + inputArgs.addAll(Arrays.asList(args)); + + // Replace some contractions such as -rdir with -r dir + + final List outputArgs = new ArrayList<>(); + + for (String arg : inputArgs) { + if (arg.startsWith("-C") || arg.startsWith("-E") || arg.startsWith("-F") || arg.startsWith("-I") || arg.startsWith("-r")) { + outputArgs.add(arg.substring(0, 2)); + outputArgs.add(arg.substring(2)); + } else { + outputArgs.add(arg); + } + } + + return outputArgs; + } + + /** + * Print help information. + */ + private static void help(PrintStream out) { + out.println("Usage: ruby [switches] [--] [programfile] [arguments]"); + out.println(" -0[octal] specify record separator (\0, if no argument)"); + out.println(" -a autosplit mode with -n or -p (splits $_ into $F)"); + out.println(" -c check syntax only"); + out.println(" -Cdirectory cd to directory, before executing your script"); + out.println(" -d, --debug set debugging flags (set $DEBUG to true) and enable Debug module"); + out.println(" -e 'command' one line of script. Several -e's allowed. Omit [programfile]"); + out.println(" -Eex[:in] specify the default external and internal character encodings"); + out.println(" -Fpattern split() pattern for autosplit (-a)"); + out.println(" -i[extension] edit ARGV files in place (make backup if extension supplied)"); + out.println(" -Idirectory specify $LOAD_PATH directory (may be used more than once)"); + out.println(" -l enable line ending processing"); + out.println(" -n assume 'while gets(); ... end' loop around your script"); + out.println(" -p assume loop like -n but print line also like sed"); + out.println(" -rlibrary require the library, before executing your script"); + out.println(" -s enable some switch parsing for switches after script name"); + out.println(" -S look for the script using PATH environment variable"); + out.println(" -T[level=1] turn on tainting checks"); + out.println(" -v print version number, then turn on verbose mode"); + out.println(" -w turn warnings on for your script"); + out.println(" -W[level=2] set warning level; 0=silence, 1=medium, 2=verbose"); + out.println(" -x[directory] strip off text before #!ruby line and perhaps cd to directory"); + out.println(" --copyright print the copyright"); + out.println(" --version print the version"); + out.println("Extra rubytruffle switches:"); + out.println(" --home dir set the location of the Ruby Truffle installation (default . or $RUBY_TRUFFLE_HOME)"); + out.println(" --stdlib dir use a directory for the Ruby standard library"); + out.println(" --1.8 1.8 compatibility mode"); + out.println(" --1.9 1.9 compatibility mode (default)"); + out.println(" --2.0 2.0 compatibility mode"); + out.println(" --full-object-space enable full ObjectSpace#each_object and similar"); + out.println(" --no-debug disable debugging"); + out.println(" --no-trace disable tracing"); + out.println("Debugging rubytruffle switches:"); + out.println(" --no-cache-constant-lookup don't cache constant lookups"); + out.println(" --no-cache-method-calls don't cache method lookups"); + out.println(" --no-intrinsic-method-calls don't turn method calls into intrinsic nodes"); + out.println(" --no-jline don't use JLine"); + out.println(" --print-parse-tree print the result of parsing"); + out.println(" --print-executed-files print the name of files as they are executed"); + out.println(" --print-missing-intrinsics print method calls that don't have intrinsic nodes"); + out.println(" --print-spilt-instance-variables print each time a native-typed instance variable is spilt to the boxed array"); + out.println(" --print-uninitialized-calls print each time a method call is uninitialized"); + out.println(" --print-java-exceptions print Java exception back traces at the point of translating them to Ruby exceptions"); + out.println(" --print-ruby-exceptions print the Java exception back traces at the point of raising Ruby exceptions"); + out.println("Relevant environment variables:"); + out.println(" RUBYHOME location of the Ruby Truffle installation"); + out.println(" RUBYOPT extra command line arguments"); + out.println(" RUBYLIB list of colon separated paths to add to $LOAD_PATH"); + out.println(" PATH as RUBYLIB, if -S is used"); + } + + /** + * Print version information. + */ + private static void version(PrintStream out) { + out.printf("ruby (rubytruffle) dev [JVM %s]\n", System.getProperty("java.version")); + } + + /** + * Print copyright information. + */ + private static void copyright(PrintStream out) { + out.println("Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved."); + out.println("ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms."); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.shell/src/com/oracle/truffle/ruby/shell/Shell.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.shell/src/com/oracle/truffle/ruby/shell/Shell.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.shell; + +import java.io.*; + +import jline.console.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.ruby.nodes.core.*; +import com.oracle.truffle.ruby.parser.*; +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.configuration.*; +import com.oracle.truffle.ruby.runtime.core.array.*; + +/** + * The entry point class for RubyTruffle. Implements the MRI command line interface. + */ +public class Shell { + + /** + * Entry point method for Ruby both in batch and interactive mode. + */ + public static void main(String[] args) throws IOException { + // Parse the command line + + final CommandLineOptions options = CommandLineParser.parse(args); + + if (options == null) { + return; + } + + // Setup JLine + + ConsoleReader console = null; + + if (options.useJLine()) { + System.setProperty("jline.shutdownhook", "true"); + console = new ConsoleReader(); + console.setExpandEvents(false); + } + + // Override the home directory if RUBYHOME is set + + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(options.getConfiguration()); + + if (System.getenv("RUBYHOME") != null) { + configurationBuilder.setStandardLibrary(System.getenv("RUBYHOME") + "/" + ConfigurationBuilder.JRUBY_STDLIB_JAR); + } + + // Use JLine for console input + + final ConsoleReader finalConsole = console; + + if (options.useJLine()) { + configurationBuilder.setInputReader(new InputReader() { + + @Override + public String readLine(String prompt) throws IOException { + return finalConsole.readLine(prompt); + } + + }); + } + + // Set up a context + + final RubyContext context = new RubyContext(new Configuration(configurationBuilder), new JRubyParser()); + + // Bring in core method nodes + + CoreMethodNodeManager.addMethods(context.getCoreLibrary().getObjectClass()); + + // Give the core library manager a chance to tweak some of those methods + + context.getCoreLibrary().initializeAfterMethodsAdded(); + + // Set program arguments + + for (String arg : options.getProgramArgs()) { + context.getCoreLibrary().getArgv().push(context.makeString(arg)); + } + + if (!options.getSwitchArgs().isEmpty()) { + context.implementationMessage("can't set -s switch arguments yet"); + } + + // Set the load path + + final RubyArray loadPath = (RubyArray) context.getCoreLibrary().getGlobalVariablesObject().getInstanceVariable("$:"); + + final String pathVar = System.getenv("PATH"); + + if (options.isImportFromPath() && pathVar != null) { + for (String path : pathVar.split(File.pathSeparator)) { + loadPath.push(context.makeString(path)); + } + } + + for (String path : options.getExtraLoadPath()) { + loadPath.push(context.makeString(path)); + } + + final String rubylibVar = System.getenv("RUBYLIB"); + + if (rubylibVar != null) { + for (String path : rubylibVar.split(File.pathSeparator)) { + loadPath.push(context.makeString(path)); + } + } + + if (context.getConfiguration().getStandardLibrary().endsWith(".jar")) { + String version = null; + + switch (configurationBuilder.getRubyVersion()) { + case RUBY_18: + version = "1.8"; + break; + case RUBY_19: + version = "1.9"; + break; + case RUBY_20: + version = "2.0"; + break; + case RUBY_21: + version = "2.0"; + break; + } + + loadPath.push(context.makeString("jar:file:" + context.getConfiguration().getStandardLibrary() + "!/META-INF/jruby.home/lib/ruby/" + version)); + + } else { + loadPath.push(context.makeString(context.getConfiguration().getStandardLibrary())); + } + + // Pre-required modules + + for (String feature : options.getPreRequires()) { + context.getFeatureManager().require(feature); + } + + // Check for other options that are not implemented yet + + if (options.getRecordSeparator() != -1) { + context.implementationMessage("record separator not implemented"); + } + + if (options.isAutosplit()) { + context.implementationMessage("autosplit not implemented"); + } + + if (options.getPreChangeDirectory() != null) { + context.implementationMessage("not able to change directory"); + } + + if (options.isLineEndingProcessing()) { + context.implementationMessage("line end processing not implemented"); + } + + if (options.isInPlaceEdit()) { + context.implementationMessage("in place editing not implemented"); + } + + if (options.isImplicitLoop() || options.isImplicitSedLoop()) { + context.implementationMessage("implicit loops not implemented"); + } + + if (options.isStripMessage()) { + context.implementationMessage("strip message -x option not implemented"); + } + + // Run the scripts, program file, or run the temporary version of IRB + + try { + if (!options.getCommandLineScripts().isEmpty()) { + final StringBuilder combinedScript = new StringBuilder(); + + for (String script : options.getCommandLineScripts()) { + combinedScript.append(script); + combinedScript.append("\n"); + } + + try { + final Source source = context.getSourceManager().get("-e", combinedScript.toString()); + if (options.isCheckSyntaxOnly()) { + context.getParser().parse(context, source, RubyParser.ParserContext.TOP_LEVEL, null); + System.out.println("Syntax OK"); + } else { + context.execute(context, source, RubyParser.ParserContext.TOP_LEVEL, context.getCoreLibrary().getMainObject(), null); + } + } catch (Exception e) { + e.printStackTrace(); + } + } else if (options.getProgramFile() != null) { + try { + if (options.isCheckSyntaxOnly()) { + final Source source = context.getSourceManager().get(options.getProgramFile()); + context.getParser().parse(context, source, RubyParser.ParserContext.TOP_LEVEL, null); + System.out.println("Syntax OK"); + } else { + context.loadFile(options.getProgramFile()); + } + } catch (Exception e) { + e.printStackTrace(); + } + } else { + if (options.isCheckSyntaxOnly()) { + System.err.println("Can't check syntax in IRB mode"); + return; + } + + context.runShell(null, null); + } + } finally { + context.shutdown(); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/README Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,21 @@ +This is a configuration of the RubySpec set of specifications - +http://rubyspec.org. RubySpec is the specifications, and MSpec is the tool to +run them. + +Thanks to Brian Shirai and others who have worked on RubySpec. + +At the moment we have only configured the version 1.9 specs, and we only run +language and command line specs. + +In the `specs` directory (everything we do here will be in the `specs` +directory), you need to clone the MSpec and RubySpec Git repositories: + + $ git clone https://github.com/rubyspec/mspec.git + $ git --git-dir=mspec/.git --work-tree=mspec checkout 1343524a06466b7aa0c90798b7894454c8abce0f + $ git clone https://github.com/rubyspec/rubyspec.git + $ git --git-dir=rubyspec/.git --work-tree=rubyspec checkout 926e356b268fe32c67bec43a21565d727360a89b + +Then you can run MSpec to run RubySpec. We can run all of the harness in our +implementation, not just as an executable under test. + + $ ./rubytruffle mspec/bin/mspec run -t ./rubytruffle --config rubytruffle.mspec --excl-tag fails diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/rubytruffle --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/rubytruffle Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,20 @@ +#!/bin/bash + +# The command this file executes is what mx would execute, just taken out of +# the mx wrapper because it appears to interfere with MSpec, and we need a +# 'executable' (a shell script will do) to run. +# +# If this file isn't working for you try generating one for your setup. You +# will need to do this for example if your Java isn't 1.7.0u45. You can use +# the command below - the -v flag gives you the Java command, then just append +# "$@". +# +# $TRUFFLE_DIR/mxtool/mx -v --vm server-nograal ruby -ea -- --home $TRUFFLE_DIR "$@" + +TRUFFLE_DIR=../../.. + +$TRUFFLE_DIR/jdk1.7.0_45/product/bin/java \ + -server-nograal -d64 -ea \ + -cp $TRUFFLE_DIR/lib/jline-2.10.jar:$TRUFFLE_DIR/lib/jrubyparser-0.5.0.jar:$TRUFFLE_DIR/lib/jruby-stdlib-1.7.4.jar:$TRUFFLE_DIR/lib/jnr-posix-3.0.0.jar:$TRUFFLE_DIR/lib/jnr-constants-0.8.4.jar:$TRUFFLE_DIR/lib/jnr-ffi-1.0.4.jar:$TRUFFLE_DIR/lib/jffi-1.2.1.jar:$TRUFFLE_DIR/lib/jffi-1.2.1-native.jar:$TRUFFLE_DIR/lib/jnr-x86asm-1.0.2.jar:$TRUFFLE_DIR/lib/asm-4.0.jar:$TRUFFLE_DIR/lib/asm-analysis-4.0.jar:$TRUFFLE_DIR/lib/asm-commons-4.0.jar:$TRUFFLE_DIR/lib/asm-tree-4.0.jar:$TRUFFLE_DIR/lib/asm-util-4.0.jar:$TRUFFLE_DIR/graal/com.oracle.truffle.api/bin:$TRUFFLE_DIR/graal/com.oracle.truffle.ruby.runtime/bin:$TRUFFLE_DIR/graal/com.oracle.truffle.api.dsl/bin:$TRUFFLE_DIR/graal/com.oracle.truffle.ruby.nodes/bin:$TRUFFLE_DIR/graal/com.oracle.truffle.ruby.parser/bin:$TRUFFLE_DIR/graal/com.oracle.truffle.ruby.shell/bin \ + com.oracle.truffle.ruby.shell.Shell \ + --home $TRUFFLE_DIR "$@" diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/rubytruffle.mspec --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/rubytruffle.mspec Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,26 @@ +class MSpecScript + + Rubyspec = "rubyspec" + + set :command_line, [ + "rubyspec/command_line" + ] + + set :language, [ + "rubyspec/language" + ] + + set :tags_patterns, [ + [/rubyspec/, "tags"], + [/_spec.rb$/, "_tags.txt"] + ] + + MSpec.enable_feature :encoding + MSpec.enable_feature :continuation + MSpec.enable_feature :fiber + MSpec.enable_feature :fork + MSpec.enable_feature :generator + + set :files, get(:command_line) + get(:language) + +end diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_a_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_a_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,2 @@ +fails:The -a command line option runs the code in loop conditional on Kernel.gets() +fails:The -a command line option sets $-a diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_d_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_d_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,3 @@ +fails:The -d command line option sets $DEBUG to true +fails:The -d command line option sets $VERBOSE to true +fails:The -d command line option sets $-d to true diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_e_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_e_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,2 @@ +fails:The -e command line option with -n and a Fixnum range mimics an awk conditional by comparing an inclusive-end range with $. +fails:The -e command line option with -n and a Fixnum range mimics a sed conditional by comparing an exclusive-end range with $. diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_n_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_n_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,4 @@ +fails:The -n command line option runs the code in loop conditional on Kernel.gets() +fails:The -n command line option only evaluates BEGIN blocks once +fails:The -n command line option only evaluates END blocks once +fails:The -n command line option allows summing over a whole file diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_p_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_p_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,2 @@ +fails:The -p command line option runs the code in loop conditional on Kernel.gets() and prints $_ +fails:The -p command line option sets $-p diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_r_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_r_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:The -r command line option requires the specified file diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_s_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_s_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,8 @@ +fails:The -s command line option when using -- to stop parsing sets the value to true without an explicit value +fails:The -s command line option when using -- to stop parsing parses single letter args into globals +fails:The -s command line option when using -- to stop parsing parses long args into globals +fails:The -s command line option when using -- to stop parsing converts extra dashes into underscores +fails:The -s command line option when running a script sets the value to true without an explicit value +fails:The -s command line option when running a script parses single letter args into globals +fails:The -s command line option when running a script parses long args into globals +fails:The -s command line option when running a script converts extra dashes into underscores diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_upper_e_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_upper_e_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:ruby -E raises a RuntimeError if used with -U diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_upper_f_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_upper_f_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:the -F command line option specifies the field separator pattern for -a diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_upper_i_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_upper_i_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:The -I command line option adds the path to the load path ($:) diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_upper_u_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_upper_u_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,7 @@ +fails:ruby -U sets Encoding.default_internal to UTF-8 +fails:ruby -U does nothing different if specified multiple times +fails:ruby -U is overruled by Encoding.default_internal= +fails:ruby -U does not affect the default external encoding +fails:ruby -U does not affect the source encoding +fails:ruby -U raises a RuntimeError if used with -Eext:int +fails:ruby -U raises a RuntimeError if used with -E:int diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_upper_w_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_upper_w_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,3 @@ +fails:The -W command line option with 0 sets $VERBOSE to nil +fails:The -W command line option with 1 sets $VERBOSE to false +fails:The -W command line option with 2 sets $VERBOSE to true diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_v_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_v_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:The -v command line option sets $VERBOSE to true diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_w_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_w_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:The -w command line option sets $VERBOSE to true diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_x_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/dash_x_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,2 @@ +fails:The -x command line option runs code after the first /#!.*ruby.*/-ish line in target file +fails:The -x command line option needs to be reviewed for spec completeness diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/command_line/error_message_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/command_line/error_message_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:The error message caused by an exception is not printed to stdout diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/BEGIN_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/BEGIN_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,4 @@ +fails:The BEGIN keyword runs first in a given code unit +fails:The BEGIN keyword runs multiple begins in FIFO order +fails:The BEGIN keyword runs in a shared scope +fails:The BEGIN keyword accesses variables outside the eval scope diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/alias_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/alias_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,11 @@ +fails:The alias keyword creates a new name for an existing method +fails:The alias keyword adds the new method to the list of methods +fails:The alias keyword adds the new method to the list of public methods +fails:The alias keyword overwrites an existing method with the target name +fails:The alias keyword is reversible +fails:The alias keyword operates on the object's metaclass when used in instance_eval +fails:The alias keyword operates on methods with splat arguments +fails:The alias keyword operates on methods with splat arguments on eigenclasses +fails:The alias keyword operates on methods with splat arguments defined in a superclass +fails:The alias keyword operates on methods with splat arguments defined in a superclass using text block for class eval +fails:The alias keyword is not allowed against Fixnum or String instances diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/array_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/array_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,4 @@ +fails:The unpacking splat operator (*) returns a new array containing the same values when applied to an array inside an empty array +fails:The unpacking splat operator (*) unpacks the start and count arguments in an array slice assignment +fails:Array literals [] treats splatted nil as no element +fails:The unpacking splat operator (*) when applied to a non-Array value attempts to coerce it to Array if the object respond_to?(:to_a) diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/block_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/block_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,67 @@ +fails:A block allows to define a block variable with the same name as the enclosing block +fails:A block taking |a| arguments assigns nil to the argument when no values are yielded +fails:A block taking |a| arguments does not call #to_ary to convert a single yielded object to an Array +fails:A block taking |a| arguments assigns the first value yielded to the argument +fails:A block taking |a, b| arguments assgins nil to the arguments when no values are yielded +fails:A block taking |a, b| arguments assigns one value yielded to the first argument +fails:A block taking |a, b| arguments destructures a splatted Array +fails:A block taking |a, b| arguments calls #to_ary to convert a single yielded object to an Array +fails:A block taking |a, b| arguments does not call #to_ary if the single yielded object is an Array +fails:A block taking |a, b| arguments does not call #to_ary if the object does not respond to #to_ary +fails:A block taking |a, b| arguments raises an TypeError if #to_ary does not return an Array +fails:A block taking |a, *b| arguments assigns 'nil' and '[]' to the arguments when no values are yielded +fails:A block taking |a, *b| arguments assigns all yielded values after the first to the rest argument +fails:A block taking |a, *b| arguments assigns 'nil' and '[]' to the arguments when a single, empty Array is yielded +fails:A block taking |a, *b| arguments assigns the element of a single element Array to the first argument +fails:A block taking |a, *b| arguments destructures a splatted Array +fails:A block taking |a, *b| arguments destructures a single Array value assigning the remaining values to the rest argument +fails:A block taking |a, *b| arguments calls #to_ary to convert a single yielded object to an Array +fails:A block taking |a, *b| arguments does not call #to_ary if the single yielded object is an Array +fails:A block taking |a, *b| arguments raises an TypeError if #to_ary does not return an Array +fails:A block taking |*| arguments does not call #to_ary if the single yielded object is an Array +fails:A block taking |*| arguments does not call #to_ary to convert a single yielded object to an Array +fails:A block taking |*a| arguments assigns all the values passed to the argument as an Array +fails:A block taking |*a| arguments assigns '[[]]' to the argument when passed an empty Array +fails:A block taking |*a| arguments assigns a single Array value passed to the argument by wrapping it in an Array +fails:A block taking |*a| arguments does not call #to_ary if the single yielded object is an Array +fails:A block taking |*a| arguments does not call #to_ary to convert a single yielded object to an Array +fails:A block taking |a, | arguments assigns nil to the argument when no values are yielded +fails:A block taking |a, | arguments assigns the argument the first value yielded +fails:A block taking |a, | arguments assigns the argument the first of several values yielded when it is an Array +fails:A block taking |a, | arguments assigns nil to the argument when passed an empty Array +fails:A block taking |a, | arguments assigns the argument the first element of the Array when passed a single Array +fails:A block taking |a, | arguments calls #to_ary to convert a single yielded object to an Array +fails:A block taking |a, | arguments does not call #to_ary if the single yielded object is an Array +fails:A block taking |a, | arguments raises an TypeError if #to_ary does not return an Array +fails:A block taking |(a, b)| arguments assigns nil to the arguments when yielded no values +fails:A block taking |(a, b)| arguments calls #to_ary to convert a single yielded object to an Array +fails:A block taking |(a, b)| arguments does not call #to_ary if the single yielded object is an Array +fails:A block taking |(a, b)| arguments does not call #to_ary if the object does not respond to #to_ary +fails:A block taking |(a, b)| arguments raises an TypeError if #to_ary does not return an Array +fails:A block taking |(a, b), c| arguments assigns nil to the arguments when yielded no values +fails:A block taking |(a, b), c| arguments destructures a single one-level Array value yielded +fails:A block taking |(a, b), c| arguments destructures a single multi-level Array value yielded +fails:A block taking |(a, b), c| arguments calls #to_ary to convert a single yielded object to an Array +fails:A block taking |(a, b), c| arguments does not call #to_ary if the single yielded object is an Array +fails:A block taking |(a, b), c| arguments does not call #to_ary if the object does not respond to #to_ary +fails:A block taking |(a, b), c| arguments raises an TypeError if #to_ary does not return an Array +fails:A block taking nested |a, (b, (c, d))| destructures a single multi-level Array value yielded +fails:A block taking nested |a, (b, (c, d))| destructures a single multi-level Array value yielded +fails:A block taking nested |a, ((b, c), d)| destructures a single multi-level Array value yielded +fails:A block taking nested |a, ((b, c), d)| destructures a single multi-level Array value yielded +fails:A block arguments with _ extracts arguments with _ +fails:Block-local variables override shadowed variables from the outer scope +fails:Post-args appear after a splat +fails:Post-args are required +fails:Post-args with required args gathers remaining args in the splat +fails:Post-args with required args has an empty splat when there are no remaining args +fails:Post-args with optional args gathers remaining args in the splat +fails:Post-args with optional args overrides the optional arg before gathering in the splat +fails:Post-args with optional args uses the required arg before the optional and the splat +fails:Post-args with optional args overrides the optional args from left to right before gathering the splat +fails:A block taking |(a, b)| arguments destructures a single Array value yielded +fails:A block taking |(a, b)| arguments destructures a single Array value yielded when shadowing an outer variable +fails:A block taking nested |a, (b, (c, d))| assigns nil to the arguments when yielded no values +fails:A block taking nested |a, (b, (c, d))| destructures separate yielded values +fails:A block taking nested |a, ((b, c), d)| assigns nil to the arguments when yielded no values +fails:A block taking nested |a, ((b, c), d)| destructures separate yielded values diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/break_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/break_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,3 @@ +fails:The break statement in a lambda created at the toplevel returns a value when invoking from the toplevel +fails:The break statement in a lambda created at the toplevel returns a value when invoking from a method +fails:The break statement in a lambda created at the toplevel returns a value when invoking from a block diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/case_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/case_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,12 @@ +fails:The 'case'-construct evaluates the body of the when clause matching the case target expression +fails:The 'case'-construct evaluates the body of the when clause whose array expression includes the case target expression +fails:The 'case'-construct evaluates the body of the when clause in left-to-right order if it's an array expression +fails:The 'case'-construct evaluates the body of the when clause whose range expression includes the case target expression +fails:The 'case'-construct expands arrays to lists of values +fails:The 'case'-construct concats arrays before expanding them +fails:The 'case'-construct never matches when clauses with no values +fails:The 'case'-construct works even if there's only one when statement +fails:The 'case'-construct with no target expression evaluates true as only 'true' when true is the first clause +fails:The 'case'-construct with no target expression evaluates false as only 'false' when false is the first clause +fails:The 'case'-construct with no target expression treats a literal array as its own when argument, rather than a list of arguments +fails:The 'case'-construct takes multiple expanded arrays diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/class_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/class_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,18 @@ +fails:A class definition has no class variables +fails:A class definition raises TypeError if the constant qualifying the class is nil +fails:A class definition raises TypeError if any constant qualifying the class is not a Module +fails:A class definition allows using self as the superclass if self is a class +fails:A class definition raises a TypeError if inheriting from a metaclass +fails:A class definition allows the declaration of class variables in the body +fails:A class definition stores instance variables defined in the class body in the class object +fails:A class definition allows the declaration of class variables in a class method +fails:A class definition allows the definition of class-level instance variables in a class method +fails:A class definition allows the declaration of class variables in an instance method +fails:A class definition returns the value of the last statement in the body +fails:An outer class definition contains the inner classes +fails:A class definition extending an object (sclass) raises a TypeError when trying to extend numbers +fails:A class definition extending an object (sclass) allows accessing the block of the original scope +fails:A class definition extending an object (sclass) can use return to cause the enclosing method to return +fails:Reopening a class raises a TypeError when superclasses mismatch +fails:Reopening a class adds new methods to subclasses +fails:class provides hooks calls inherited when a class is created diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/class_variable_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/class_variable_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,10 @@ +fails:A class variable can be accessed from a subclass +fails:A class variable is set in the superclass +fails:A class variable defined in a module can be accessed from classes that extend the module +fails:A class variable defined in a module is not defined in these classes +fails:A class variable defined in a module is only updated in the module a method defined in the module is used +fails:A class variable defined in a module is updated in the class when a Method defined in the class is used +fails:A class variable defined in a module can be accessed from modules that extend the module +fails:A class variable defined in a module is defined in the extended module +fails:A class variable defined in a module is not defined in the extending module +fails:A class variable defined in a module can be accessed inside the class using the module methods diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/constants_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/constants_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,45 @@ +fails:Literal (A::X) constant resolution sends #const_missing to the original class or module scope +fails:Literal (A::X) constant resolution raises a TypeError if a non-class or non-module qualifier is given +fails:Literal (A::X) constant resolution with statically assigned constants does not search the singleton class of the class or module +fails:Literal (A::X) constant resolution with dynamically assigned constants does not search the singleton class of the class or module +fails:Literal (A::X) constant resolution with dynamically assigned constants evaluates the right hand side before evaluating a constant path +fails:Constant resolution within methods sends #const_missing to the original class or module scope +fails:Constant resolution within methods with statically assigned constants searches the lexical scope of the method not the receiver's immediate class +fails:Constant resolution within methods with statically assigned constants searches the lexical scope of a singleton method +fails:Constant resolution within methods with statically assigned constants searches Object as a lexical scope only if Object is explicitly opened +fails:Constant resolution within methods with dynamically assigned constants searches the immediate class or module scope first +fails:Constant resolution within methods with dynamically assigned constants searches the superclass before a module included in the superclass +fails:Constant resolution within methods with dynamically assigned constants searches the lexical scope of the method not the receiver's immediate class +fails:Constant resolution within methods with dynamically assigned constants searches the lexical scope of a singleton method +fails:Constant resolution within methods with dynamically assigned constants does not search the lexical scope of the caller +fails:Constant resolution within methods with dynamically assigned constants searches the lexical scope of a block +fails:Constant resolution within methods with dynamically assigned constants searches Object as a lexical scope only if Object is explicitly opened +fails:Constant resolution within methods with ||= assignes constant if previously undefined +fails:Module#private_constant marked constants remain private even when updated +fails:Module#private_constant marked constants in a module cannot be accessed from outside the module +fails:Module#private_constant marked constants in a module cannot be reopened as a module +fails:Module#private_constant marked constants in a module cannot be reopened as a class +fails:Module#private_constant marked constants in a module is not defined? with A::B form +fails:Module#private_constant marked constants in a module can be accessed from lexical scope +fails:Module#private_constant marked constants in a module is defined? from lexical scope +fails:Module#private_constant marked constants in a class cannot be accessed from outside the class +fails:Module#private_constant marked constants in a class cannot be reopened as a module +fails:Module#private_constant marked constants in a class cannot be reopened as a class +fails:Module#private_constant marked constants in a class is not defined? with A::B form +fails:Module#private_constant marked constants in a class can be accessed from lexical scope +fails:Module#private_constant marked constants in Object cannot be accessed using ::Const form +fails:Module#private_constant marked constants in Object is not defined? using ::Const form +fails:Module#public_constant marked constants in a module can be accessed from outside the module +fails:Module#public_constant marked constants in a module is defined? with A::B form +fails:Module#public_constant marked constants in a class can be accessed from outside the class +fails:Module#public_constant marked constants in a class is defined? with A::B form +fails:Module#public_constant marked constants in Object can be accessed using ::Const form +fails:Module#public_constant marked constants in Object is defined? using ::Const form +fails:Module#private_constant marked constants in a class is defined? from lexical scope +fails:Literal (A::X) constant resolution with statically assigned constants searches a module included in the superclass +fails:Constant resolution within methods with statically assigned constants searches a module included in the superclass +fails:Constant resolution within methods with statically assigned constants searches the superclass chain +fails:Literal (A::X) constant resolution with statically assigned constants searches Object if no class or module qualifier is given +fails:Literal (A::X) constant resolution with statically assigned constants searches Object after searching other scopes +fails:Constant resolution within methods with statically assigned constants searches the superclass chain +fails:Literal (A::X) constant resolution with statically assigned constants searches the superclass chain diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/def_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/def_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,28 @@ +fails:Defining an 'initialize' method sets the method's visibility to private +fails:Defining an 'initialize_copy' method sets the method's visibility to private +fails:An instance method with a default argument assigns an empty Array to an unused splat argument +fails:An instance method with a default argument prefers to assign to a default argument when there are no required arguments +fails:An instance method with a default argument does not evaluate the default when passed a value and a * argument +fails:A singleton method definition raises RuntimeError if frozen +fails:Redefining a singleton method does not inherit a previously set visibility +fails:Redefining a singleton method does not inherit a previously set visibility +fails:A method defined with extreme default arguments may use an fcall as a default +fails:A method defined with extreme default arguments may use preceding arguments as defaults +fails:A method defined with extreme default arguments may use a lambda as a default +fails:A singleton method defined with extreme default arguments may use an fcall as a default +fails:A singleton method defined with extreme default arguments may use preceding arguments as defaults +fails:A singleton method defined with extreme default arguments may use a lambda as a default +fails:A method definition inside a metaclass scope can create a class method +fails:A method definition inside a metaclass scope can create a singleton method +fails:A method definition inside a metaclass scope raises RuntimeError if frozen +fails:A nested method definition creates an instance method when evaluated in an instance method +fails:A nested method definition creates a class method when evaluated in a class method +fails:A nested method definition creates a singleton method when evaluated in the metaclass of an instance +fails:A method definition inside an instance_eval creates a singleton method +fails:A method definition inside an instance_eval creates a singleton method when evaluated inside a metaclass +fails:A method definition inside an instance_eval creates a class method when the receiver is a class +fails:A method definition in an eval creates an instance method +fails:A method definition in an eval creates a class method +fails:A method definition in an eval creates a singleton method +fails:a method definition that sets more than one default parameter all to the same value assigns them all the same object by default +fails:The def keyword within a closure looks outside the closure for the visibility diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/defined_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/defined_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,48 @@ +fails:The defined? keyword when called with a method name having a module as receiver returns nil if the method is private +fails:The defined? keyword when called with a method name having a module as receiver returns nil if the method is protected +fails:The defined? keyword when called with a method name having a local variable as receiver calls #respond_to_missing? +fails:The defined? keyword for an expression returns nil for an expression with !~ and an undefined method +fails:The defined? keyword for an expression returns 'method' for an expression with '!~' +fails:The defined? keyword for an expression with logical connectives returns nil for an expression with '!' and an unset class variable +fails:The defined? keyword for an expression with logical connectives returns nil for an expression with 'not' and an unset class variable +fails:The defined? keyword for an expression with logical connectives returns nil for an expression with '!' and an unset global variable +fails:The defined? keyword for an expression with logical connectives returns nil for an expression with '!' and an unset instance variable +fails:The defined? keyword for an expression with logical connectives returns nil for an expression with 'not' and an unset global variable +fails:The defined? keyword for an expression with logical connectives returns nil for an expression with 'not' and an unset instance variable +fails:The defined? keyword for variables returns nil for a global variable that has not been read +fails:The defined? keyword for variables returns nil for a global variable that has been read but not assigned to +fails:The defined? keyword for variables when a String does not match a Regexp returns nil for $& +fails:The defined? keyword for variables when a String does not match a Regexp returns nil for $` +fails:The defined? keyword for variables when a String does not match a Regexp returns nil for $' +fails:The defined? keyword for variables when a String does not match a Regexp returns nil for $+ +fails:The defined? keyword for variables when a String matches a Regexp returns 'global-variable' for $& +fails:The defined? keyword for variables when a String matches a Regexp returns 'global-variable' for $` +fails:The defined? keyword for variables when a String matches a Regexp returns 'global-variable' for $' +fails:The defined? keyword for variables when a String matches a Regexp returns 'global-variable' for $+ +fails:The defined? keyword for variables when a String matches a Regexp returns 'global-variable' for the capture references +fails:The defined? keyword for variables when a Regexp does not match a String returns nil for $& +fails:The defined? keyword for variables when a Regexp does not match a String returns nil for $` +fails:The defined? keyword for variables when a Regexp does not match a String returns nil for $' +fails:The defined? keyword for variables when a Regexp does not match a String returns nil for $+ +fails:The defined? keyword for variables when a Regexp matches a String returns 'global-variable' for $& +fails:The defined? keyword for variables when a Regexp matches a String returns 'global-variable' for $` +fails:The defined? keyword for variables when a Regexp matches a String returns 'global-variable' for $' +fails:The defined? keyword for variables when a Regexp matches a String returns 'global-variable' for $+ +fails:The defined? keyword for variables when a Regexp matches a String returns 'global-variable' for the capture references +fails:The defined? keyword for a scoped constant returns nil when an undefined constant is scoped to a defined constant +fails:The defined? keyword for a top-level scoped constant returns nil when an undefined constant is scoped to a defined constant +fails:The defined? keyword for super returns nil when a superclass undef's the method +fails:The defined? keyword for super for a method taking no arguments returns 'super' from a block when a superclass method exists +fails:The defined? keyword for super for a method taking no arguments returns 'super' from a #define_method when a superclass method exists +fails:The defined? keyword for super for a method taking no arguments returns 'super' from a block in a #define_method when a superclass method exists +fails:The defined? keyword for super for a method taking no arguments returns 'super' when the method exists in a supermodule +fails:The defined? keyword for super for a method taking arguments returns 'super' when a superclass method exists +fails:The defined? keyword for super for a method taking arguments returns 'super' from a block when a superclass method exists +fails:The defined? keyword for super for a method taking arguments returns 'super' from a #define_method when a superclass method exists +fails:The defined? keyword for super for a method taking arguments returns 'super' from a block in a #define_method when a superclass method exists +fails:The defined? keyword for super within an included module's method returns 'super' when a superclass method exists in the including hierarchy +fails:The defined? keyword for instance variables returns nil if not assigned +fails:The defined? keyword for variables when a String does not match a Regexp returns nil for $1-$9 +fails:The defined? keyword for variables when a String matches a Regexp returns nil for non-captures +fails:The defined? keyword for variables when a Regexp does not match a String returns nil for $1-$9 +fails:The defined? keyword for variables when a Regexp matches a String returns nil for non-captures diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/encoding_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/encoding_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,14 @@ +fails:The __ENCODING__ pseudo-variable is an instance of Encoding +fails:The __ENCODING__ pseudo-variable is US-ASCII by default +fails:The __ENCODING__ pseudo-variable is the evaluated strings's one inside an eval +fails:The __ENCODING__ pseudo-variable is the encoding specified by a magic comment inside an eval +fails:The __ENCODING__ pseudo-variable is the encoding specified by a magic comment in the file +fails:The __ENCODING__ pseudo-variable is Encoding::ASCII_8BIT when the interpreter is invoked with -Ka +fails:The __ENCODING__ pseudo-variable is Encoding::ASCII_8BIT when the interpreter is invoked with -KA +fails:The __ENCODING__ pseudo-variable is Encoding::EUC_JP when the interpreter is invoked with -Ke +fails:The __ENCODING__ pseudo-variable is Encoding::EUC_JP when the interpreter is invoked with -KE +fails:The __ENCODING__ pseudo-variable is Encoding::UTF_8 when the interpreter is invoked with -Ku +fails:The __ENCODING__ pseudo-variable is Encoding::UTF_8 when the interpreter is invoked with -KU +fails:The __ENCODING__ pseudo-variable is Encoding::Windows_31J when the interpreter is invoked with -Ks +fails:The __ENCODING__ pseudo-variable is Encoding::Windows_31J when the interpreter is invoked with -KS +fails:The __ENCODING__ pseudo-variable raises a SyntaxError if assigned to diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/ensure_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/ensure_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,2 @@ +fails:An ensure block inside a begin block is executed when an exception is raised in it's corresponding begin block +fails:An ensure block inside a method is executed when an exception is raised in the method diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/execution_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/execution_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,2 @@ +fails:`` returns the output of the executed sub-process +fails:%x is the same as `` diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/file_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/file_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,3 @@ +fails:The __FILE__ pseudo-variable equals (eval) inside an eval +fails:The __FILE__ pseudo-variable equals the absolute path of a file loaded by an absolute path +fails:The __FILE__ pseudo-variable equals the absolute path of a file loaded by a relative path diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/for_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/for_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,6 @@ +fails:The for expression iterates over an Hash passing each key-value pair to the block +fails:The for expression allows a class variable as an iterator name +fails:The for expression yields only as many values as there are arguments +fails:The for expression executes code in containing variable scope +fails:The for expression executes code in containing variable scope with 'do' +fails:The for expression returns expr diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/hash_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/hash_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:Hash literal freezes string keys on initialization diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/line_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/line_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:The __LINE__ pseudo-variable equals the line number of the text in a loaded file diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/literal_lambda_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/literal_lambda_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,4 @@ +fails:->(){} assigns the given block to the parameter prefixed with an ampersand if such a parameter exists +fails:->(){} sets parameters appropriately when a combination of parameter types is given between the parenthesis +fails:->(){} uses lambda's 'rigid' argument handling +fails:->(){} evaluates constants as normal blocks do diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/magic_comment_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/magic_comment_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,8 @@ +fails:Magic comment is optional +fails:Magic comment determines __ENCODING__ +fails:Magic comment is case-insensitive +fails:Magic comment must be at the first line +fails:Magic comment must be the first token of the line +fails:Magic comment can be after the shebang +fails:Magic comment can take Emacs style +fails:Magic comment can take vim style diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/match_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/match_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,2 @@ +fails:The !~ operator evaluates as a call to !~ +fails:The =~ operator calls the =~ method diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/metaclass_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/metaclass_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,16 @@ +fails:self in a metaclass body (class << obj) is TrueClass for true +fails:self in a metaclass body (class << obj) is FalseClass for false +fails:self in a metaclass body (class << obj) is NilClass for nil +fails:self in a metaclass body (class << obj) raises a TypeError for numbers +fails:self in a metaclass body (class << obj) raises a TypeError for symbols +fails:self in a metaclass body (class << obj) is a singleton Class instance +fails:A constant on a metaclass can be accessed via const_get +fails:A constant on a metaclass cannot be accessed via object::CONST +fails:A constant on a metaclass raises a NameError for anonymous_module::CONST +fails:A constant on a metaclass appears in the metaclass constant list +fails:A constant on a metaclass does not appear in the object's class constant list +fails:A constant on a metaclass is not preserved when the object is duped +fails:A constant on a metaclass is preserved when the object is cloned +fails:calling methods on the metaclass calls a method on the instance's metaclass +fails:calling methods on the metaclass calls a method in deeper chains of metaclasses +fails:calling methods on the metaclass calls a method defined on the metaclass of the metaclass diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/module_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/module_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,7 @@ +fails:The module keyword reopens a module included in Object +fails:The module keyword raises a TypeError if the constant is a Class +fails:The module keyword raises a TypeError if the constant is a String +fails:The module keyword raises a TypeError if the constant is a Fixnum +fails:The module keyword raises a TypeError if the constant is nil +fails:The module keyword raises a TypeError if the constant is true +fails:The module keyword raises a TypeError if the constant is false diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/next_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/next_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,9 @@ +fails:The next statement from within the block returns the argument passed +fails:The next statement from within the block returns to the invoking method, with the specified value +fails:The next statement from within the block returns to the currently yielding method in case of chained calls +fails:Assignment via next assigns objects +fails:Assignment via next assigns splatted objects +fails:Assignment via next assigns objects to a splatted reference +fails:Assignment via next assigns splatted objects to a splatted reference via a splatted yield +fails:Assignment via next assigns objects to multiple variables +fails:Assignment via next assigns splatted objects to multiple variables diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/precedence_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/precedence_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,2 @@ +fails:Operators + - have higher precedence than >> << +fails:Operators + - are left-associative diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/predefined/data_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/predefined/data_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,6 @@ +fails:The DATA constant exists when the main script contains __END__ +fails:The DATA constant does not exist when the main script contains no __END__ +fails:The DATA constant does not exist when an included file has a __END__ +fails:The DATA constant does not change when an included files also has a __END__ +fails:The DATA constant is included in an otherwise empty file +fails:The DATA constant succeeds in locking the file DATA came from diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/predefined_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/predefined_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,108 @@ +fails:Predefined global $~ is set to contain the MatchData object of the last match if successful +fails:Predefined global $~ is set to nil if the last match was unsuccessful +fails:Predefined global $~ is set at the method-scoped level rather than block-scoped +fails:Predefined global $~ raises an error if assigned an object not nil or instanceof MatchData +fails:Predefined global $~ changes the value of derived capture globals when assigned +fails:Predefined global $~ changes the value of the derived preceding match global +fails:Predefined global $~ changes the value of the derived following match global +fails:Predefined global $~ changes the value of the derived full match global +fails:Predefined global $& is equivalent to MatchData#[0] on the last match $~ +fails:Predefined global $& sets the encoding to the encoding of the source String +fails:Predefined global $` is equivalent to MatchData#pre_match on the last match $~ +fails:Predefined global $` sets the encoding to the encoding of the source String +fails:Predefined global $` sets an empty result to the encoding of the source String +fails:Predefined global $' is equivalent to MatchData#post_match on the last match $~ +fails:Predefined global $' sets the encoding to the encoding of the source String +fails:Predefined global $' sets an empty result to the encoding of the source String +fails:Predefined global $+ is equivalent to $~.captures.last +fails:Predefined global $+ captures the last non nil capture +fails:Predefined global $+ sets the encoding to the encoding of the source String +fails:Predefined globals $1..N are equivalent to $~[N] +fails:Predefined globals $1..N are nil unless a match group occurs +fails:Predefined globals $1..N sets the encoding to the encoding of the source String +fails:Predefined global $stdout is the same as $DEFAULT_OUTPUT from 'English' library +fails:Predefined global $stdout raises TypeError error if assigned to nil +fails:Predefined global $stdout raises TypeError error if assigned to object that doesn't respond to #write +fails:Predefined global $! remains nil after a failed core class "checked" coercion against a class that defines method_missing +fails:Predefined global $/ changes $-0 +fails:Predefined global $/ does not call #to_str to convert the object to a String +fails:Predefined global $/ raises a TypeError if assigned a Fixnum +fails:Predefined global $/ raises a TypeError if assigned a boolean +fails:Predefined global $-0 changes $/ +fails:Predefined global $-0 does not call #to_str to convert the object to a String +fails:Predefined global $-0 raises a TypeError if assigned a Fixnum +fails:Predefined global $-0 raises a TypeError if assigned a boolean +fails:Predefined global $, raises TypeError if assigned a non-String +fails:Predefined global $_ is set to the last line read by e.g. StringIO#gets +fails:Predefined global $_ is set at the method-scoped level rather than block-scoped +fails:Predefined global $_ is Thread-local +fails:Execution variable $: does not include '.' when the taint check level > 1 +fails:Execution variable $: is the same object as $LOAD_PATH and $-I +fails:Execution variable $: is read-only +fails:Global variable $" is read-only +fails:Global variable $< is read-only +fails:Global variable $FILENAME is read-only +fails:Global variable $? is read-only +fails:Global variable $? is thread-local +fails:Global variable $-a is read-only +fails:Global variable $-l is read-only +fails:Global variable $-p is read-only +fails:Global variable $-d is an alias of $DEBUG +fails:Global variable $-v is an alias of $VERBOSE +fails:Global variable $-w is an alias of $VERBOSE +fails:Global variable $0 raises a TypeError when not given an object that can be coerced to a String +fails:The predefined standard objects includes ARGF +fails:The predefined global constants includes TRUE +fails:The predefined global constants includes FALSE +fails:The predefined global constants includes NIL +fails:The predefined global constants includes STDIN +fails:The predefined global constants includes STDOUT +fails:The predefined global constants includes STDERR +fails:The predefined global constants includes RUBY_RELEASE_DATE +fails:The predefined global constants includes TOPLEVEL_BINDING +fails:Processing RUBYOPT adds the -I path to $LOAD_PATH +fails:Processing RUBYOPT sets $DEBUG to true for '-d' +fails:Processing RUBYOPT prints the version number for '-v' +fails:Processing RUBYOPT sets $VERBOSE to true for '-w' +fails:Processing RUBYOPT sets $VERBOSE to true for '-W' +fails:Processing RUBYOPT sets $VERBOSE to nil for '-W0' +fails:Processing RUBYOPT sets $VERBOSE to false for '-W1' +fails:Processing RUBYOPT sets $VERBOSE to true for '-W2' +fails:Processing RUBYOPT requires the file for '-r' +fails:Processing RUBYOPT raises a RuntimeError for '-a' +fails:Processing RUBYOPT raises a RuntimeError for '-p' +fails:Processing RUBYOPT raises a RuntimeError for '-n' +fails:Processing RUBYOPT raises a RuntimeError for '-y' +fails:Processing RUBYOPT raises a RuntimeError for '-c' +fails:Processing RUBYOPT raises a RuntimeError for '-s' +fails:Processing RUBYOPT raises a RuntimeError for '-h' +fails:Processing RUBYOPT raises a RuntimeError for '--help' +fails:Processing RUBYOPT raises a RuntimeError for '-l' +fails:Processing RUBYOPT raises a RuntimeError for '-S' +fails:Processing RUBYOPT raises a RuntimeError for '-e' +fails:Processing RUBYOPT raises a RuntimeError for '-i' +fails:Processing RUBYOPT raises a RuntimeError for '-x' +fails:Processing RUBYOPT raises a RuntimeError for '-C' +fails:Processing RUBYOPT raises a RuntimeError for '-X' +fails:Processing RUBYOPT raises a RuntimeError for '-F' +fails:Processing RUBYOPT raises a RuntimeError for '-0' +fails:Processing RUBYOPT raises a RuntimeError for '--copyright' +fails:Processing RUBYOPT raises a RuntimeError for '--version' +fails:Processing RUBYOPT raises a RuntimeError for '--yydebug' +fails:The predefined global constant STDERR has nil for the external encoding despite Encoding.default_external being changed +fails:The predefined global constant STDERR has the encodings set by #set_encoding +fails:The predefined global constant ARGV contains Strings encoded in locale Encoding +fails:The predefined global constant STDERR has nil for the internal encoding despite Encoding.default_internal being changed +fails:The predefined global constant STDERR has nil for the internal encoding +fails:The predefined global constant STDERR has nil for the external encoding +fails:The predefined global constant STDOUT has nil for the internal encoding despite Encoding.default_internal being changed +fails:The predefined global constant STDOUT has nil for the internal encoding +fails:The predefined global constant STDOUT has the encodings set by #set_encoding +fails:The predefined global constant STDOUT has nil for the external encoding despite Encoding.default_external being changed +fails:The predefined global constant STDOUT has nil for the external encoding +fails:The predefined global constant STDIN has nil for the internal encoding despite Encoding.default_internal being changed +fails:The predefined global constant STDIN has nil for the internal encoding +fails:The predefined global constant STDIN retains the encoding set by #set_encoding when Encoding.default_external is changed +fails:The predefined global constant STDIN has the encodings set by #set_encoding +fails:The predefined global constant STDIN has the same external encoding as Encoding.default_external when that encoding is changed +fails:The predefined global constant STDIN has the same external encoding as Encoding.default_external diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/private_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/private_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:The private keyword is overridden when a new class is opened diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/proc_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/proc_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,18 @@ +fails:A Proc taking zero arguments raises an ArgumentErro if a value is passed +fails:A Proc taking || arguments raises an ArgumentError if a value is passed +fails:A Proc taking |a| arguments does not call #to_ary to convert a single passed object to an Array +fails:A Proc taking |a| arguments raises an ArgumentError if no value is passed +fails:A Proc taking |a, b| arguments raises an ArgumentError if passed no values +fails:A Proc taking |a, b| arguments raises an ArgumentError if passed one value +fails:A Proc taking |a, b| arguments does not call #to_ary to convert a single passed object to an Array +fails:A Proc taking |a, *b| arguments raises an ArgumentError if passed no values +fails:A Proc taking |a, *b| arguments does not call #to_ary to convert a single passed object to an Array +fails:A Proc taking |*| arguments does not call #to_ary to convert a single passed object to an Array +fails:A Proc taking |*a| arguments does not call #to_ary to convert a single passed object to an Array +fails:A Proc taking |a, | arguments raises an ArgumentError when passed no values +fails:A Proc taking |a, | arguments raises an ArgumentError when passed more than one value +fails:A Proc taking |a, | arguments does not call #to_ary to convert a single passed object to an Array +fails:A Proc taking |(a, b)| arguments raises an ArgumentError when passed no values +fails:A Proc taking |(a, b)| arguments calls #to_ary to convert a single passed object to an Array +fails:A Proc taking |(a, b)| arguments raises an TypeError if #to_ary does not return an Array +fails:A Proc taking |(a, b)| arguments destructures a single Array value yielded diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/redo_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/redo_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,3 @@ +fails:The redo statement restarts block execution if used within block +fails:The redo statement re-executes the closest loop +fails:The redo statement re-executes the last step in enumeration diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/anchors_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/anchors_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:Regexps with anchors supports B (non-word-boundary) diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/back-references_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/back-references_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,4 @@ +fails:Regexps with back-references saves match data in the $~ pseudo-global variable +fails:Regexps with back-references saves captures in numbered $[1-9] variables +fails:Regexps with back-references will not clobber capture variables across threads +fails:Regexps with back-references resets nested backreference before match of outer subexpression diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/character_classes_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/character_classes_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,68 @@ +fails:Regexp with character classes supports [] (character class) +fails:Regexp with character classes supports [[:alpha:][:digit:][:etc:]] (predefined character classes) +fails:Regexp with character classes matches ASCII characters with [[:ascii:]] +fails:Regexp with character classes matches Unicode letter characters with [[:alnum:]] +fails:Regexp with character classes matches Unicode digits with [[:alnum:]] +fails:Regexp with character classes doesn't match Unicode control characters with [[:alnum:]] +fails:Regexp with character classes doesn't match Unicode punctuation characters with [[:alnum:]] +fails:Regexp with character classes matches Unicode letter characters with [[:alpha:]] +fails:Regexp with character classes doesn't match Unicode digits with [[:alpha:]] +fails:Regexp with character classes doesn't match Unicode control characters with [[:alpha:]] +fails:Regexp with character classes doesn't match Unicode punctuation characters with [[:alpha:]] +fails:Regexp with character classes matches Unicode space characters with [[:blank:]] +fails:Regexp with character classes matches Unicode control characters with [[:cntrl:]] +fails:Regexp with character classes matches Unicode digits with [[:digit:]] +fails:Regexp with character classes matches Unicode letter characters with [[:graph:]] +fails:Regexp with character classes matches Unicode digits with [[:graph:]] +fails:Regexp with character classes matches Unicode marks with [[:graph:]] +fails:Regexp with character classes matches Unicode punctuation characters with [[:graph:]] +fails:Regexp with character classes match Unicode format characters with [[:graph:]] +fails:Regexp with character classes match Unicode private-use characters with [[:graph:]] +fails:Regexp with character classes matches Unicode lowercase letter characters with [[:lower:]] +fails:Regexp with character classes matches Unicode lowercase letter characters with [[:print:]] +fails:Regexp with character classes matches Unicode uppercase letter characters with [[:print:]] +fails:Regexp with character classes matches Unicode title-case characters with [[:print:]] +fails:Regexp with character classes matches Unicode digits with [[:print:]] +fails:Regexp with character classes matches Unicode marks with [[:print:]] +fails:Regexp with character classes matches Unicode punctuation characters with [[:print:]] +fails:Regexp with character classes match Unicode format characters with [[:print:]] +fails:Regexp with character classes match Unicode private-use characters with [[:print:]] +fails:Regexp with character classes matches Unicode Pc characters with [[:punct:]] +fails:Regexp with character classes matches Unicode Pd characters with [[:punct:]] +fails:Regexp with character classes matches Unicode Ps characters with [[:punct:]] +fails:Regexp with character classes matches Unicode Pe characters with [[:punct:]] +fails:Regexp with character classes matches Unicode Pi characters with [[:punct:]] +fails:Regexp with character classes matches Unicode Pf characters with [[:punct:]] +fails:Regexp with character classes matches Unicode Pf characters with [[:punct:]] +fails:Regexp with character classes matches Unicode Po characters with [[:punct:]] +fails:Regexp with character classes matches Unicode Zs characters with [[:space:]] +fails:Regexp with character classes matches Unicode Zl characters with [[:space:]] +fails:Regexp with character classes matches Unicode Zp characters with [[:space:]] +fails:Regexp with character classes matches Unicode uppercase characters with [[:upper:]] +fails:Regexp with character classes doesn't match Unicode letter characters [^a-fA-F] with [[:xdigit:]] +fails:Regexp with character classes matches Unicode letter characters [a-fA-F] with [[:xdigit:]] +fails:Regexp with character classes matches Unicode lowercase characters with [[:word:]] +fails:Regexp with character classes matches Unicode uppercase characters with [[:word:]] +fails:Regexp with character classes matches Unicode title-case characters with [[:word:]] +fails:Regexp with character classes matches Unicode decimal digits with [[:word:]] +fails:Regexp with character classes matches Unicode marks with [[:word:]] +fails:Regexp with character classes match Unicode Nl characters with [[:word:]] +fails:Regexps with anchors supports ^ (line start anchor) +fails:Regexp with character classes doesn't matches Unicode marks with [[:alnum:]] +fails:Regexp with character classes doesn't match Unicode lowercase letter characters with [[:punct:]] +fails:Regexp with character classes doesn't match Unicode uppercase letter characters with [[:punct:]] +fails:Regexp with character classes doesn't match Unicode title-case characters with [[:punct:]] +fails:Regexp with character classes doesn't match Unicode digits with [[:punct:]] +fails:Regexp with character classes doesn't match Unicode marks with [[:punct:]] +fails:Regexp with character classes doesn't match Unicode format characters with [[:punct:]] +fails:Regexp with character classes doesn't match Unicode private-use characters with [[:punct:]] +fails:Regexp with character classes doesn't match Unicode lowercase characters with [[:upper:]] +fails:Regexp with character classes doesn't match Unicode title-case characters with [[:upper:]] +fails:Regexp with character classes doesn't match Unicode digits with [[:upper:]] +fails:Regexp with character classes doesn't match Unicode marks with [[:upper:]] +fails:Regexp with character classes doesn't match Unicode punctuation characters with [[:upper:]] +fails:Regexp with character classes doesn't match Unicode control characters with [[:upper:]] +fails:Regexp with character classes doesn't match Unicode format characters with [[:upper:]] +fails:Regexp with character classes doesn't match Unicode private-use characters with [[:upper:]] +fails:Regexps with escape characters support \x (hex characters) +fails:Regexps with escape characters support \c (control characters) diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/encoding_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/encoding_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,23 @@ +fails:Regexps with encoding modifiers supports /e (EUC encoding) +fails:Regexps with encoding modifiers supports /e (EUC encoding) with interpolation +fails:Regexps with encoding modifiers supports /e (EUC encoding) with interpolation /o +fails:Regexps with encoding modifiers uses EUC-JP as /e encoding +fails:Regexps with encoding modifiers preserves EUC-JP as /e encoding through interpolation +fails:Regexps with encoding modifiers supports /n (No encoding) +fails:Regexps with encoding modifiers supports /n (No encoding) with interpolation +fails:Regexps with encoding modifiers supports /n (No encoding) with interpolation /o +fails:Regexps with encoding modifiers uses US-ASCII as /n encoding if all chars are 7-bit +fails:Regexps with encoding modifiers uses ASCII-8BIT as /n encoding if not all chars are 7-bit +fails:Regexps with encoding modifiers preserves US-ASCII as /n encoding through interpolation if all chars are 7-bit +fails:Regexps with encoding modifiers preserves ASCII-8BIT as /n encoding through interpolation if all chars are 7-bit +fails:Regexps with encoding modifiers supports /s (Windows_31J encoding) +fails:Regexps with encoding modifiers supports /s (Windows_31J encoding) with interpolation +fails:Regexps with encoding modifiers supports /s (Windows_31J encoding) with interpolation and /o +fails:Regexps with encoding modifiers uses Windows-31J as /s encoding +fails:Regexps with encoding modifiers preserves Windows-31J as /s encoding through interpolation +fails:Regexps with encoding modifiers supports /u (UTF8 encoding) +fails:Regexps with encoding modifiers supports /u (UTF8 encoding) with interpolation +fails:Regexps with encoding modifiers supports /u (UTF8 encoding) with interpolation and /o +fails:Regexps with encoding modifiers uses UTF-8 as /u encoding +fails:Regexps with encoding modifiers preserves UTF-8 as /u encoding through interpolation +fails:Regexps with encoding modifiers selects last of multiple encoding specifiers diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/escapes_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/escapes_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,2 @@ +fails:Regexps with escape characters support \x (hex characters) +fails:Regexps with escape characters support \c (control characters) diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/grouping_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/grouping_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:Regexps with grouping raise a SyntaxError when parentheses aren't balanced diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/interpolation_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/interpolation_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,7 @@ +fails:Regexps with interpolation allows interpolation of literal regexps +fails:Regexps with interpolation allows interpolation of any class that responds to to_s +fails:Regexps with interpolation allows interpolation which mixes modifiers +fails:Regexps with interpolation gives precedence to escape sequences over substitution +fails:Regexps with interpolation throws RegexpError for malformed interpolation +fails:Regexps with interpolation allows interpolation in extended mode +fails:Regexps with interpolation allows escape sequences in interpolated regexps diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/modifiers_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/modifiers_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,9 @@ +fails:Regexps with modifers supports /m (multiline) +fails:Regexps with modifers supports /x (extended syntax) +fails:Regexps with modifers supports /o (once) +fails:Regexps with modifers invokes substitutions for /o only once +fails:Regexps with modifers supports (?imx-imx) (inline modifiers) +fails:Regexps with modifers supports (?imx-imx:expr) (scoped inline modifiers) +fails:Regexps with modifers supports . with /m +fails:Regexps with modifers supports ASII/Unicode modifiers +fails:Regexps with modifers raises SyntaxError for ASII/Unicode modifiers diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/repetition_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp/repetition_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,1 @@ +fails:Regexps with repetition does not treat {m,n}+ as possessive diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp_tags.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/specs/tags/language/regexp_tags.txt Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,16 @@ +fails:Literal Regexps matches against $_ (last input) in a conditional if no explicit matchee provided +fails:Literal Regexps throws SyntaxError for malformed literals +fails:Literal Regexps supports paired delimiters with %r +fails:Literal Regexps supports grouping constructs that are also paired delimiters +fails:Literal Regexps allows second part of paired delimiters to be used as non-paired delimiters +fails:Literal Regexps supports non-paired delimiters delimiters with %r +fails:Literal Regexps allows unescaped / to be used with %r +fails:Literal Regexps supports . (any character except line terminator) +fails:Literal Regexps supports | (alternations) +fails:Literal Regexps supports (?> ) (embedded subexpression) +fails:Literal Regexps supports (?# ) +fails:Literal Regexps supports (?<= ) (positive lookbehind) +fails:Literal Regexps supports (? 2 }; puts bar.to_s"); + } + + @Test + public void testInclude() { + assertPrints("true\nfalse\n", "foo = [1, 2, 3, 4]; puts foo.include? 2; puts foo.include? 5"); + } + + @Test + public void testSub() { + assertPrints("[1, 4]\n", "puts ([1, 2, 2, 3, 4] - [2, 3]).to_s"); + } + + @Test + public void testJoin() { + assertPrints("1.2.3\n", "puts [1, 2, 3].join('.')"); + } + + @Test + public void testShift() { + assertPrints("1\n2\n3\n", "a = [1, 2, 3]; while b = a.shift; puts b; end"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/BignumTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/BignumTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Bignum} class. + */ +public class BignumTests extends RubyTests { + + @Test + public void testImmediate() { + assertPrints("123456789123456789\n", "puts 123456789123456789"); + assertPrints("574658776654483828\n", "puts 574658776654483828"); + } + + @Test + public void testAddImmediate() { + assertPrints("821572354901397406\n", "puts 123456789123456789+698115565777940617"); + assertPrints("7675735362615108\n", "puts 867676857675 + 7674867685757433"); + assertPrints("8792416214481\n", "puts 8785647643454 + (6768571027)"); + assertPrints("8089240320234\n", "puts (7132953783486) + ((956286536748))"); + } + + @Test + public void testSubImmediate() { + assertPrints("574658776654483828\n", "puts 698115565777940617-123456789123456789"); + assertPrints("7674000008899758\n", "puts 7674867685757433 - 867676857675"); + assertPrints("8778879072427\n", "puts 8785647643454 - (6768571027)"); + assertPrints("6176667246738\n", "puts (7132953783486) - ((956286536748))"); + } + + @Test + public void testLessImmediate() { + assertPrints("false\n", "puts 698115565777940617 < 123456789123456789"); + assertPrints("true\n", "puts 867676857675 < 7674867685757433"); + assertPrints("false\n", "puts 8785647643454 < (6768571027)"); + assertPrints("true\n", "puts (956286536748) < ((7132953783486))"); + } + + @Test + public void testDivmod() { + assertPrints("100\n2342\n", "puts 1000000000000000000000002342.divmod(10000000000000000000000000)"); + assertPrints("Fixnum\n", "puts 1000000000000000000000002342.divmod(10000000000000000000000000)[0].class"); + assertPrints("Fixnum\n", "puts 1000000000000000000000002342.divmod(10000000000000000000000000)[1].class"); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/BoolTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/BoolTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code true} and {@code false}. Note that there is no {@code Bool} class or type. There is + * {@code TrueClass}, {@code FalseClass}, and instances of them {@code true} and {@code false}. + */ +public class BoolTests extends RubyTests { + + @Test + public void testImmediate() { + assertPrints("true\n", "puts true"); + assertPrints("false\n", "puts false"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ContinuationTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ContinuationTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.runtime.configuration.*; +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Continuation} class. + */ +public class ContinuationTests extends RubyTests { + + @Test + public void testRequired18() { + assertPrints(RubyVersion.RUBY_18, "", "callcc { |c| c.call }"); + } + + @Test + public void testRequired19() { + assertPrints(RubyVersion.RUBY_19, "", "require \"continuation\"; callcc { |c| c.call }"); + } + + @Test + public void testRequired20() { + assertPrints(RubyVersion.RUBY_20, "", "require \"continuation\"; callcc { |c| c.call }"); + } + + @Test + public void testOneShotGoingUpCallstack() { + assertPrints(RubyVersion.RUBY_18, "1\n3\n", "callcc { |c| puts 1; c.call; puts 2 }; puts 3"); + } + + @Test + public void testOneShotGoingUpCallstackReturnValue() { + assertPrints(RubyVersion.RUBY_18, "14\n", "puts callcc { |c| c.call 14 }"); + } + + @Test + public void testNestedOneShotGoingUpCallstack() { + assertPrints(RubyVersion.RUBY_18, "1\n2\n4\n5\n", "callcc { |c1| puts 1; callcc { |c2| puts 2; c2.call; puts 3 }; puts 4 }; puts 5"); + assertPrints(RubyVersion.RUBY_18, "1\n2\n5\n", "callcc { |c1| puts 1; callcc { |c2| puts 2; c1.call; puts 3 }; puts 4 }; puts 5"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/FiberTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/FiberTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Fiber} class. + */ +public class FiberTests extends RubyTests { + + @Test + public void testResume() { + assertPrints("14\n", "f = Fiber.new { |x| puts x }; f.resume 14"); + } + + @Test + public void testYield() { + assertPrints("14\n", "f = Fiber.new { |x| Fiber.yield x }; puts f.resume(14)"); + } + + @Test + public void testCountdown() { + assertPrints("", "f = Fiber.new do |n|\n" + // + " loop do\n" + // + " n = Fiber.yield n - 1\n" + // + " end\n" + // + "end\n" + // + "\n" + // + "n = 1000\n" + // + "\n" + // + "while n > 0\n" + // + " n = f.resume n\n" + // + "end\n"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/FixnumTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/FixnumTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Fixnum} class. + */ +public class FixnumTests extends RubyTests { + + @Test + public void testImmediate() { + assertPrints("2\n", "puts 2"); + assertPrints("14\n", "puts 14"); + assertPrints("-14\n", "puts -14"); + } + + @Test + public void testNegate() { + assertPrints("-1\n", "x = 1; puts -x"); + assertPrints("1\n", "x = -1; puts -x"); + } + + @Test + public void testAddImmediate() { + assertPrints("16\n", "puts 14+2"); + assertPrints("14\n", "puts 12 + 2"); + assertPrints("17\n", "puts 9 + (8)"); + assertPrints("10\n", "puts (6) + ((4))"); + } + + @Test + public void testSubImmediate() { + assertPrints("12\n", "puts 14-2"); + assertPrints("10\n", "puts 12 - 2"); + assertPrints("1\n", "puts 9 - (8)"); + assertPrints("2\n", "puts (6) - ((4))"); + } + + @Test + public void testMulImmediate() { + assertPrints("28\n", "puts 14 * 2"); + assertPrints("20\n", "puts 2 * 10"); + assertPrints("72\n", "puts 9 * (8)"); + assertPrints("24\n", "puts (4) * ((6))"); + } + + @Test + public void testDivImmediate() { + assertPrints("7\n", "puts 14 / 2"); + assertPrints("0\n", "puts 2 / 10"); + assertPrints("1\n", "puts 9 / (8)"); + assertPrints("0\n", "puts (4) / ((6))"); + } + + @Test + public void testEqualImmediate() { + assertPrints("true\n", "puts 14 == 14"); + assertPrints("true\n", "puts 2 == 2"); + assertPrints("false\n", "puts 9 == (8)"); + assertPrints("false\n", "puts (4) == ((6))"); + } + + @Test + public void testNotEqualImmediate() { + assertPrints("false\n", "puts 14 != 14"); + assertPrints("false\n", "puts 2 != 2"); + assertPrints("true\n", "puts 9 != (8)"); + assertPrints("true\n", "puts (4) != ((6))"); + } + + @Test + public void testLessImmediate() { + assertPrints("false\n", "puts 14 < 2"); + assertPrints("true\n", "puts 2 < 10"); + assertPrints("false\n", "puts 9 < (8)"); + assertPrints("true\n", "puts (4) < ((6))"); + } + + @Test + public void testGreaterEqualImmediate() { + assertPrints("true\n", "puts 14 >= 14"); + assertPrints("true\n", "puts 14 >= 2"); + assertPrints("false\n", "puts 2 >= 10"); + assertPrints("true\n", "puts 9 >= (8)"); + assertPrints("false\n", "puts (4) >= ((6))"); + } + + @Test + public void testLeftShift() { + assertPrints("0\n", "puts 0 << 0"); + assertPrints("0\n", "puts 0 << 1"); + assertPrints("1\n", "puts 1 << 0"); + assertPrints("0\n", "puts 1 << -1"); + assertPrints("0\n", "puts 1 << -2"); + assertPrints("28\n", "puts 14 << 1"); + assertPrints("7\n", "puts 14 << -1"); + assertPrints("4294967296\n", "puts 1 << 32"); + assertPrints("340282366920938463463374607431768211456\n", "puts 1 << 128"); + assertPrints("0\n", "puts 1 << -32"); + assertPrints("Fixnum\n", "puts (14 << 1).class"); + assertPrints("Bignum\n", "puts (1 << 32).class"); + } + + @Test + public void testDivmod() { + // @formatter:off + final int[][] tests = new int[][]{ + new int[]{13, 4, 3, 1}, + new int[]{13, -4, -4, -3}, + new int[]{-13, 4, -4, 3}, + new int[]{-13, -4, 3, -1}}; + // @formatter:on + + for (int[] test : tests) { + final int a = test[0]; + final int b = test[1]; + final int q = test[2]; + final int r = test[3]; + assertPrints(String.format("%d\n%d\n", q, r), String.format("puts %d.divmod(%d)", a, b)); + } + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/FloatTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/FloatTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Float} class. + */ +public class FloatTests extends RubyTests { + + @Test + public void testImmediate() { + assertPrints("2.5\n", "puts 2.5"); + assertPrints("14.33\n", "puts 14.33"); + } + + @Test + public void testAddImmediate() { + assertPrints("16.2\n", "puts 14.1+2.1"); + assertPrints("14.2\n", "puts 12.1 + 2.1"); + assertPrints("17.2\n", "puts 9.1 + (8.1)"); + assertPrints("10.2\n", "puts (6.1) + ((4.1))"); + } + + @Test + public void testSubImmediate() { + assertPrints("12.1\n", "puts 14.2-2.1"); + assertPrints("10.1\n", "puts 12.2 - 2.1"); + assertPrints("1.0999999999999996\n", "puts 9.2 - (8.1)"); + assertPrints("2.1000000000000005\n", "puts (6.2) - ((4.1))"); + } + + @Test + public void testMulImmediate() { + assertPrints("29.82\n", "puts 14.2*2.1"); + assertPrints("25.62\n", "puts 12.2 * 2.1"); + assertPrints("74.52\n", "puts 9.2 * (8.1)"); + assertPrints("25.419999999999998\n", "puts (6.2) * ((4.1))"); + assertPrints("7.5\n", "puts 2.5 * 3"); + assertPrints("7.5\n", "puts 3 * 2.5"); + } + + @Test + public void testDivImmediate() { + assertPrints("6.761904761904761\n", "puts 14.2/2.1"); + assertPrints("5.809523809523809\n", "puts 12.2 / 2.1"); + assertPrints("1.1358024691358024\n", "puts 9.2 / (8.1)"); + assertPrints("1.5121951219512197\n", "puts (6.2) / ((4.1))"); + assertPrints("0.8333333333333334\n", "puts 2.5 / 3"); + assertPrints("1.2\n", "puts 3 / 2.5"); + } + + @Test + public void testLessImmediate() { + assertPrints("false\n", "puts 14.2<2.2"); + assertPrints("true\n", "puts 2.2 < 10.2"); + assertPrints("false\n", "puts 9.2 < (8.2)"); + assertPrints("true\n", "puts (4.2) < ((6.2))"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/HashTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/HashTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Hash} class. + */ +public class HashTests extends RubyTests { + + @Test + public void testLiteral() { + assertPrints("Hash\n", "puts ({}).class"); + assertPrints("Hash\n", "puts ({1 => 2, 3 => 4}).class"); + assertPrints("Hash\n", "puts ({key1: 2, key3: 4}).class"); + } + + @Test + public void testIndex() { + assertPrints("4\n", "foo = {1 => 2, 3 => 4}; puts foo[3]"); + } + + @Test + public void testIndexSet() { + assertPrints("6\n", "foo = {1 => 2, 3 => 4}; foo[5] = 6; puts foo[5]"); + } + + @Test + public void testKeys() { + assertPrints("[1, 3]\n", "foo = {1 => 2, 3 => 4}; puts foo.keys.to_s"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/IntegerTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/IntegerTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Integer} class. + */ +public class IntegerTests extends RubyTests { + + @Test + public void testTimes() { + assertPrints("0\n", "1.times { |i| puts i }"); + assertPrints("0\n1\n2\n", "3.times { |i| puts i }"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/KernelTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/KernelTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.runtime.configuration.*; +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code Kernel}. + */ +public class KernelTests extends RubyTests { + + @Test + public void testPutsEmpty() { + assertPrints("\n", "puts"); + } + + @Test + public void testPutsString() { + assertPrints("1\n", "puts 1"); + } + + @Test + public void testPrintfNoFormatting() { + assertPrints("", "printf"); + assertPrints("foo", "printf \"foo\""); + assertPrints("foo\n", "printf \"foo\\n\""); + } + + @Test + public void testPrintfDecimal() { + assertPrints("foo14bar", "printf \"foo%dbar\", 14"); + } + + @Test + public void testGets() { + assertPrintsWithInput("test\n", "puts gets", "test\n"); + } + + @Test + public void testInteger() { + assertPrints("14\n", "puts Integer(\"14\")"); + } + + @Test + public void testEval() { + assertPrints("16\n", "puts eval(\"14 + 2\")"); + } + + @Test + public void testBindingLocalVariables() { + // Use the current binding for eval + assertPrints("16\n", "x = 14; y = 2; puts eval(\"x + y\", binding)"); + + // Use the binding returned from a method for eval + assertPrints("16\n", "def foo; x = 14; y = 2; binding; end; puts eval(\"x + y\", foo)"); + } + + @Test + public void testBindingInstanceVariables() { + // Use the binding returned from a method in an object for eval + assertPrints("16\n", "class Foo; def foo; @x = 14; @y = 2; binding; end; end; puts eval(\"@x + @y\", Foo.new.foo)"); + } + + @Test + public void testSetTraceFuncLine() { + final ConfigurationBuilder configuration = new ConfigurationBuilder(); + configuration.setTrace(true); + + final String code = "def foo\n" + // + " a = 14\n" + // + " b = 2\n" + // + " a + b\n" + // + "end\n" + // + "\n" + // + "set_trace_func proc { |event, file, line, id, binding, classname|\n" + // + " if event == \"line\"\n" + // + " puts file + \":\" + line.to_s\n" + // + " end\n" + // + "}\n" + // + "\n" + // + "foo"; + final String input = ""; + final String expected = "(test):13\n(test):2\n(test):3\n(test):4\n"; + assertPrints(new Configuration(configuration), expected, "(test)", code, input); + } + + @Test + public void testBlockGiven() { + assertPrints("false\n", "def foo; puts block_given?; end; foo"); + assertPrints("true\n", "def foo; puts block_given?; end; foo do; end"); + assertPrints("true\n", "def foo; puts block_given?; end; foo {}"); + assertPrints("true\n", "def foo; puts block_given?; end; foo &:+"); + } + + @Test + public void testLoop() { + assertPrints("14\n", "loop do; break; end; puts 14"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/MathTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/MathTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Math} class. + */ +public class MathTests extends RubyTests { + + @Test + public void testPI() { + assertPrints("3.141592653589793\n", "puts Math::PI"); + } + + @Test + public void testSqrt() { + assertPrints("1.0\n", "puts Math.sqrt(1)"); + assertPrints("1.0\n", "puts Math::sqrt(1)"); + assertPrints("3.7416573867739413\n", "puts Math::sqrt(14)"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ModuleTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ModuleTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Module} class. + */ +public class ModuleTests extends RubyTests { + + @Test + public void testAttrAccessor() { + assertPrints("14\n", "class Foo; attr_accessor :x end; foo=Foo.new; foo.x=14; puts foo.x"); + assertPrints("14\n", "class Foo; attr_accessor(:x) end; foo=Foo.new; foo.x=14; puts foo.x"); + assertPrints("16\n", "class Foo; attr_accessor(:x, :y) end; foo=Foo.new; foo.x=14; foo.y=2; puts foo.x + foo.y"); + assertPrints("16\n", "class Foo; attr_accessor :x end; foo=Foo.new; foo.x=2; foo.x+=14; puts foo.x"); + } + + @Test + public void testDefineMethod() { + /* + * We use Object#send instead of calling define_method directly because that method is + * private. + */ + + assertPrints("14\n", "class Foo; end; Foo.send(:define_method, :foo) do; puts 14; end; Foo.new.foo"); + } + + @Test + public void testDefinedBeforeScopeRun() { + assertPrints("Module\n", "module Foo; puts Foo.class; end"); + } + + @Test + public void testDefinedInRootScopeBeforeScopeRun() { + assertPrints("Module\n", "module Foo; puts ::Foo.class; end"); + } + + @Test + public void testModuleEval() { + assertPrints("14\n", "class Foo; puts 14; end; Foo.module_eval('def foo; end'); Foo.new.foo"); + } + + @Test + public void testNextInBlock() { + assertPrints("1\n1\n1\n", "3.times do; puts 1; next; puts 2; end"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ObjectSpaceTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ObjectSpaceTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.runtime.configuration.*; +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code ObjectSpace} module. + */ +public class ObjectSpaceTests extends RubyTests { + + @Test + public void testEachObjectClass() { + assertPrints("true\n", "found_string = false; ObjectSpace.each_object(Class) { |o| if o == String; found_string = true; end }; puts found_string"); + } + + @Test + public void testId2RefClass() { + assertPrints("true\n", "puts ObjectSpace._id2ref(String.object_id) == String"); + } + + @Test + public void testEachObjectString() { + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.setFullObjectSpace(true); + final String code = "foo = \"foo\"; found_foo = false; ObjectSpace.each_object(String) { |o| if o == foo; found_foo= true; end }; puts found_foo"; + final String input = ""; + final String expected = "true\n"; + assertPrints(new Configuration(configurationBuilder), expected, "(test)", code, input); + } + + @Test + public void testId2RefString() { + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.setFullObjectSpace(true); + final String code = "foo = \"foo\"; puts ObjectSpace._id2ref(foo.object_id) == foo"; + final String input = ""; + final String expected = "true\n"; + assertPrints(new Configuration(configurationBuilder), expected, "(test)", code, input); + } + + @Test + public void testGarbageCollect() { + assertPrints("", "ObjectSpace.garbage_collect"); + assertPrints("", "ObjectSpace.start"); + } +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ObjectTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ObjectTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Object} class. + */ +public class ObjectTests extends RubyTests { + + @Test + public void testARGV() { + assertPrints("1\n2\n3\n", "puts ARGV", "1", "2", "3"); + } + + @Test + public void testInstanceVariableDefined() { + assertPrints("true\n", "class Foo; def initialize; @bar=14; end; end; puts Foo.new.instance_variable_defined?(:@bar)"); + assertPrints("true\n", "class Foo; def initialize; @bar=14; end; end; puts Foo.new.instance_variable_defined?(\"@bar\")"); + assertPrints("true\n", "class Foo; def initialize; instance_variable_set(:@bar, 14); end; end; puts Foo.new.instance_variable_defined?(\"@bar\")"); + assertPrints("false\n", "class Foo; def initialize; @foo=14; end; end; puts Foo.new.instance_variable_defined?(:@bar)"); + assertPrints("false\n", "class Foo; def initialize; @foo=14; end; end; puts Foo.new.instance_variable_defined?(\"@bar\")"); + assertPrints("false\n", "class Foo; def initialize; instance_variable_set(:@foo, 14); end; end; puts Foo.new.instance_variable_defined?(\"@bar\")"); + } + + @Test + public void testInstanceVariableGet() { + assertPrints("14\n", "class Foo; def initialize; @bar=14; end; end; puts Foo.new.instance_variable_get(:@bar)"); + assertPrints("14\n", "class Foo; def initialize; @bar=14; end; end; puts Foo.new.instance_variable_get(\"@bar\")"); + } + + @Test + public void testInstanceVariableSet() { + assertPrints("14\n", "class Foo; attr_accessor :bar; end; foo = Foo.new; foo.instance_variable_set(:@bar, 14); puts foo.bar"); + assertPrints("14\n", "class Foo; attr_accessor :bar; end; foo = Foo.new; foo.instance_variable_set(\"@bar\", 14); puts foo.bar"); + } + + @Test + public void testInstanceVariables() { + assertPrints("a\nb\n", "class Foo; def initialize; @a=2; @b=14; end; end; puts Foo.new.instance_variables"); + assertPrints("a\nb\n", "class Foo; def initialize; @a=2; instance_variable_set(:@b, 14); end; end; puts Foo.new.instance_variables"); + } + + @Test + public void testSend() { + assertPrints("14\n", "self.send(:puts, 14)"); + assertPrints("14\n", "self.send(\"puts\", 14)"); + } + + @Test + public void testExtend() { + assertPrints("14\n", "class Foo; end; module Bar; def bar; puts 14; end; end; foo = Foo.new; foo.extend(Bar); foo.bar"); + } + + @Test + public void testSingletonMethods() { + assertPrints("[:baz]\n", "class Foo; def bar; end; end; foo = Foo.new; def foo.baz; end; puts foo.singleton_methods.to_s"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ProcTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ProcTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.runtime.configuration.*; +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Proc} class. + */ +public class ProcTests extends RubyTests { + + @Test + public void testKernelProc() { + assertPrints("1\n", "x = proc { puts 1 }; x.call"); + assertPrints("1\n", "x = proc { 1 }; puts x.call"); + } + + @Test + public void testKernelLambda() { + assertPrints("1\n", "x = lambda { puts 1 }; x.call"); + assertPrints("1\n", "x = lambda { 1 }; puts x.call"); + } + + @Test + public void testKernelProcNew() { + assertPrints("1\n", "x = Proc.new { puts 1 }; x.call"); + assertPrints("1\n", "x = Proc.new { 1 }; puts x.call"); + } + + @Test + public void testProcReturn18() { + assertPrints(RubyVersion.RUBY_18, "2\n", "def foo; x = proc { return 1 }; x.call; return 2; end; puts foo"); + } + + @Test + public void testProcReturn19() { + assertPrints(RubyVersion.RUBY_19, "1\n", "def foo; x = proc { return 1 }; x.call; return 2; end; puts foo"); + } + + @Test + public void testProcReturn20() { + assertPrints(RubyVersion.RUBY_20, "1\n", "def foo; x = proc { return 1 }; x.call; return 2; end; puts foo"); + } + + @Test + public void testLambdaReturn() { + assertPrints("2\n", "def foo; x = lambda { return 1 }; x.call; return 2; end; puts foo"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/RangeTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/RangeTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Range} class. + */ +public class RangeTests extends RubyTests { + + @Test + public void testImmediate() { + assertPrints("1..2\n", "puts 1..2"); + assertPrints("1..2\n", "puts (1..2)"); + assertPrints("1..2\n", "puts ((1)..2)"); + assertPrints("1...2\n", "puts 1...2"); + } + + @Test + public void testVariables() { + assertPrints("1..2\n", "x = 1; puts x..2"); + assertPrints("1..2\n", "y = 2; puts 1..y"); + assertPrints("1..2\n", "x = 1; y = 2; puts x..y"); + } + + @Test + public void testToArray() { + assertPrints("0\n1\n2\n", "puts (0..2).to_a"); + assertPrints("1\n2\n", "puts (1..2).to_a"); + assertPrints("1\n2\n", "puts (1...3).to_a"); + assertPrints("1\n2\n3\n", "puts (1..3).to_a"); + } + + @Test + public void testEach() { + assertPrints("", "(1...1).each { |n| puts n }"); + assertPrints("1\n", "(1...2).each { |n| puts n }"); + assertPrints("1\n2\n", "(1...3).each { |n| puts n }"); + assertPrints("1\n2\n3\n", "(1...4).each { |n| puts n }"); + assertPrints("1\n", "(1..1).each { |n| puts n }"); + assertPrints("1\n2\n", "(1..2).each { |n| puts n }"); + assertPrints("1\n2\n3\n", "(1..3).each { |n| puts n }"); + assertPrints("1\n2\n3\n4\n", "(1..4).each { |n| puts n }"); + assertPrints("0\n1\n", "(0..1).each { |n| puts n }"); + assertPrints("", "(4..-2).each { |n| puts n }"); + assertPrints("-4\n-3\n-2\n", "(-4..-2).each { |n| puts n }"); + assertPrints("-1\n0\n1\n", "(-1..1).each { |n| puts n }"); + } + + @Test + public void testMap() { + assertPrints("2\n4\n6\n", "puts (1..3).map { |n| n*2 }"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/RegexpTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/RegexpTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Regexp} class. + */ +public class RegexpTests extends RubyTests { + + @Test + public void testLiteral() { + assertPrints("Regexp\n", "puts /foo/.class"); + } + + @Test + public void testInterpolatedLiteral() { + assertPrints("Regexp\n", "puts /foo#{1}/.class"); + } + + @Test + public void testMatch() { + assertPrints("0\n", "puts(/foo/ =~ \"foo\")"); + assertPrints("0\n", "puts(\"foo\" =~ /foo/)"); + assertPrints("3\n", "puts(/foo/ =~ \"abcfoo\")"); + assertPrints("3\n", "puts(\"abcfoo\" =~ /foo/)"); + assertPrints("\n", "puts(/foo/ =~ \"abc\")"); + assertPrints("\n", "puts(\"abc\" =~ /foo/)"); + } + + @Test + public void testFrameLocalVariableResults() { + assertPrints("foo\nbar\nbaz\n", "/(foo)(bar)(baz)/ =~ 'foobarbaz'; puts $1; puts $2; puts $3"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/StringTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/StringTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code String} class. + */ +public class StringTests extends RubyTests { + + @Test + public void testToI() { + assertPrints("2\n", "puts \"2\".to_i"); + assertPrints("-2\n", "puts \"-2\".to_i"); + assertPrints("123456789123456789\n", "puts \"123456789123456789\".to_i"); + } + + @Test + public void testFormat() { + assertPrints("a\n", "puts \"a\""); + assertPrints("1\n", "puts \"%d\" % 1"); + assertPrints("a1b\n", "puts \"a%db\" % 1"); + assertPrints("a1.00000b\n", "puts \"a%fb\" % 1"); + assertPrints("a2.50000b\n", "puts \"a%fb\" % 2.5"); + assertPrints("3.400000000\n", "puts \"%.9f\" % 3.4"); + assertPrints("3.400000000\n", "puts \"%0.9f\" % 3.4"); + } + + @Test + public void testThreequal() { + assertPrints("false\n", "puts \"a\" === \"b\""); + assertPrints("true\n", "puts \"a\" === \"a\""); + } + + @Test + public void testIndex() { + assertPrints("a\n", "puts 'a'[0]"); + assertPrints("config\n", "puts 'foo.config'[-6..-1]"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/SymbolTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/SymbolTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Symbol} class. + */ +public class SymbolTests extends RubyTests { + + @Test + public void testParses() { + assertPrints("", ":foo"); + assertPrints("", ":\"foo\""); + } + + @Test + public void testPuts() { + assertPrints("foo\n", "puts :foo"); + } + + @Test + public void testInterpolated() { + assertPrints("foo123\n", "puts :\"foo1#{2}3\""); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ThreadTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/core/ThreadTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.core; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test the {@code Thread} class. + */ +public class ThreadTests extends RubyTests { + + @Test + public void testCreateJoin() { + assertPrints("1\n2\n", "t = Thread.new { puts 1 }; t.join; puts 2"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/debug/DebugTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/debug/DebugTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.debug; + +import org.junit.*; + +import com.oracle.truffle.ruby.runtime.configuration.*; +import com.oracle.truffle.ruby.test.*; + +/** + * Test the debugger. + */ +public class DebugTests extends RubyTests { + + @Test + public void testBreakContinue() { + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.setDebug(true); + final Configuration configuration = new Configuration(configurationBuilder); + + final String fakeFileName = "test.rb"; + final String code = "Debug.break; puts 2"; + final String input = "puts 1 \n Debug.continue \n puts 'error' \n puts 'error'"; + final String expected = "1\n=> \n2\n"; + + assertPrints(configuration, expected, fakeFileName, code, input, new String[]{}); + } + + @Test + public void testLineBreak() { + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.setDebug(true); + final Configuration configuration = new Configuration(configurationBuilder); + + final String fakeFileName = "test.rb"; + final String code = "Debug.break(\"test.rb\", 2) \n puts 2 \n puts 3"; + final String input = "puts 1 \n Debug.continue \n puts 'error' \n puts 'error'"; + final String expected = "1\n=> \n2\n3\n"; + + assertPrints(configuration, expected, fakeFileName, code, input, new String[]{}); + } + + @Test + public void testLineCustomBreak() { + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.setDebug(true); + final Configuration configuration = new Configuration(configurationBuilder); + + final String fakeFileName = "test.rb"; + final String code = "Debug.break('test.rb', 2) { puts 1; Debug.break }\nputs 3\nputs 4"; + final String input = "puts 2 \n Debug.continue \n puts 'error' \n puts 'error'"; + final String expected = "1\n2\n=> \n3\n4\n"; + + assertPrints(configuration, expected, fakeFileName, code, input, new String[]{}); + } + + @Test + public void testLocalBreak() { + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.setDebug(true); + final Configuration configuration = new Configuration(configurationBuilder); + + final String fakeFileName = "test.rb"; + final String code = "def foo \n puts 0 \n x = 14 \n end \n Debug.break(:foo, :x) \n foo \n puts 2"; + final String input = "puts 1 \n Debug.continue \n puts 'error' \n puts 'error'"; + final String expected = "0\n1\n=> \n2\n"; + + assertPrints(configuration, expected, fakeFileName, code, input, new String[]{}); + } + + @Test + public void testLocalCustomBreak() { + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.setDebug(true); + final Configuration configuration = new Configuration(configurationBuilder); + + final String fakeFileName = "test.rb"; + final String code = "def foo \n puts 0 \n x = 14 \n end \n Debug.break(:foo, :x) { |v| puts v; Debug.break } \n foo \n puts 2"; + final String input = "puts 1 \n Debug.continue \n puts 'error' \n puts 'error'"; + final String expected = "0\n14\n1\n=> \n2\n"; + + assertPrints(configuration, expected, fakeFileName, code, input, new String[]{}); + } + + @Test + public void testRemoveLineBreak() { + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.setDebug(true); + final Configuration configuration = new Configuration(configurationBuilder); + + final String fakeFileName = "test.rb"; + final String code = "Debug.break('test.rb', 3) \n Debug.remove('test.rb', 3) \n puts 2 \n puts 3"; + final String input = "puts 1 \n Debug.continue \n puts 'error' \n puts 'error'"; + final String expected = "2\n3\n"; + + assertPrints(configuration, expected, fakeFileName, code, input, new String[]{}); + } + + @Test + public void testRemoveLocalBreak() { + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.setDebug(true); + final Configuration configuration = new Configuration(configurationBuilder); + + final String fakeFileName = "test.rb"; + final String code = "def foo \n puts 0 \n x = 14 \n end \n Debug.break(:foo, :x) \n foo \n Debug.remove(:foo, :x) \n foo \n puts 2"; + final String input = "puts 1 \n Debug.continue \n puts 'error' \n puts 'error'"; + final String expected = "0\n1\n=> \n0\n2\n"; + + assertPrints(configuration, expected, fakeFileName, code, input, new String[]{}); + } + + @Test + public void testWhere() { + final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.setDebug(true); + final Configuration configuration = new Configuration(configurationBuilder); + + final String fakeFileName = "test.rb"; + final String code = "puts 1 \n Debug.where \n puts 2"; + final String expected = "1\ntest.rb:2\n2\n"; + + assertPrints(configuration, expected, fakeFileName, code, "", new String[]{}); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/AndTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/AndTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code and} expressions, which unusually for Ruby are not methods. This is because with + * Ruby's eager evaluation there would be no way to implement the short circuit semantics. + */ +public class AndTests extends RubyTests { + + @Test + public void testImmediate() { + assertPrints("false\n", "puts false && false"); + assertPrints("false\n", "puts true && false"); + assertPrints("false\n", "puts false && true"); + assertPrints("true\n", "puts true && true"); + assertPrints("false\n", "puts (false and false)"); + assertPrints("false\n", "puts (true and false)"); + assertPrints("false\n", "puts (false and true)"); + assertPrints("true\n", "puts (true and true)"); + } + + @Test + public void testShortCircuits() { + assertPrints("false\nfalse\nfalse\n", "x = y = false; puts (x = false) && (y = false); puts x, y"); + assertPrints("false\ntrue\nfalse\n", "x = y = false; puts (x = true) && (y = false); puts x, y"); + assertPrints("false\nfalse\nfalse\n", "x = y = false; puts (x = false) && (y = true); puts x, y"); + assertPrints("true\ntrue\ntrue\n", "x = y = false; puts (x = true) && (y = true); puts x, y"); + assertPrints("false\nfalse\nfalse\n", "x = y = false; puts ((x = false) and (y = false)); puts x, y"); + assertPrints("false\ntrue\nfalse\n", "x = y = false; puts ((x = true) and (y = false)); puts x, y"); + assertPrints("false\nfalse\nfalse\n", "x = y = false; puts ((x = false) and (y = true)); puts x, y"); + assertPrints("true\ntrue\ntrue\n", "x = y = false; puts ((x = true) and (y = true)); puts x, y"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/BlockTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/BlockTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test blocks. + */ +public class BlockTests extends RubyTests { + + @Test + public void testSimpleYield() { + assertPrints("1\n", "def foo; yield; end; foo { puts 1 };"); + assertPrints("1\n", "def foo; yield; end; foo do; puts 1; end;"); + } + + @Test + public void testYieldOneParameter() { + assertPrints("1\n", "def foo; yield 1; end; foo { |x| puts x };"); + assertPrints("1\n", "def foo; yield 1; end; foo do |x|; puts x; end;"); + } + + @Test + public void testYieldTwoParameters() { + assertPrints("1\n2\n", "def foo; yield 1, 2; end; foo { |x, y| puts x; puts y; };"); + assertPrints("1\n2\n", "def foo; yield 1, 2; end; foo do |x, y|; puts x; puts y; end;"); + } + + @Test + public void testSelfCapturedInBlock() { + assertPrints("main\n", "[1].each { |n| puts self.to_s }"); + } + + @Test + public void testBlockArgumentsDestructure() { + /* + * This really subtle. If you pass an array to a block with more than one parameters, it + * will be destructured. Any other type won't be destructured. There's no annotation to + * indicate that, like there would be with a method with the * operator. + */ + + assertPrints("1\n", "[1].each { |x| puts x.to_s }"); + assertPrints("[1, 2]\n", "[[1, 2]].each { |xy| puts xy.to_s }"); + assertPrints("1\n2\n", "[[1, 2]].each { |x, y| puts x, y }"); + } + + @Test + public void testBlocksHaveTheirOwnScopeForAssignment() { + assertPrints("\n", "1.times { x = 14 }; puts defined? x"); + assertPrints("\n", "1.times do; x = 14; end; puts defined? x"); + } + + @Test + public void testImplicitForBlocksDoNotHaveTheirOwnScopeForAssignment() { + assertPrints("local-variable\n", "for n in [1]; x = 14; end; puts defined? x"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/CaseTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/CaseTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code case} expressions. + */ +public class CaseTests extends RubyTests { + + @Test + public void testSingle() { + assertPrints("1\n4\n", "case 'a'; when 'a'; puts 1; when 'b'; puts 2; else; puts 3; end; puts 4"); + assertPrints("2\n4\n", "case 'b'; when 'a'; puts 1; when 'b'; puts 2; else; puts 3; end; puts 4"); + assertPrints("3\n4\n", "case 'c'; when 'a'; puts 1; when 'b'; puts 2; else; puts 3; end; puts 4"); + } + + @Test + public void testMultiple() { + assertPrints("1\n4\n", "case 'a'; when 'a', 'b'; puts 1; when 'c'; puts 2; else; puts 3; end; puts 4"); + assertPrints("1\n4\n", "case 'b'; when 'a', 'b'; puts 1; when 'c'; puts 2; else; puts 3; end; puts 4"); + assertPrints("2\n4\n", "case 'c'; when 'a', 'b'; puts 1; when 'c'; puts 2; else; puts 3; end; puts 4"); + assertPrints("3\n4\n", "case 'd'; when 'a', 'b'; puts 1; when 'c'; puts 2; else; puts 3; end; puts 4"); + } + + @Test + public void testSimpleConditions() { + assertPrints("1\n", "case; when true; puts 1; when false; puts 2; end"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ClassLocalTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ClassLocalTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test @@ class variables. There is a lot of counter-intuitive behavior here - they aren't instance + * variables in class objects. The books describe them as being in a 'class hierarchy', rather than + * in a class. Also, the object they are defined it is not consistent - sometimes it is the class of + * self, sometimes it's just self. + */ +public class ClassLocalTests extends RubyTests { + + @Test + public void testBasic() { + assertPrints("14\n", "@@x = 14; puts @@x"); + } + + /* + * Test that they are defined for a hierarchy, not a class. + */ + + @Test + public void testHeirarchyNotClassVariable() { + assertPrints("2\n", "class Foo; @@value = 14; end; class Bar < Foo; @@value = 2; end; class Foo; puts @@value; end"); + } + + /* + * Test that they take the correct class at different times. In a method, they use the class of + * self. In a class definition they use that class, not the class of self, which is Class. + */ + + @Test + public void testCorrectClass() { + assertPrints("1\n", "class Foo; @@x = 1; def foo; puts @@x; end; end; Foo.new.foo"); + } + + @Test + public void testWithinSelfMethod() { + assertPrints("14\n", "class Foo; @@foo = 14; def self.foo; @@foo; end; end; puts Foo.foo"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ClassTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ClassTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code class} expressions. + */ +public class ClassTests extends RubyTests { + + @Test + public void testDefine() { + assertPrints("Foo\n", "class Foo; end; puts Foo"); + } + + @Test + public void testMethods() { + assertPrints("14\n", "class Foo; def test; return 14; end; end; foo=Foo.new; puts foo.test"); + assertPrints("14\n", "class Foo; def test(x); return x; end; end; foo=Foo.new; puts foo.test(14)"); + } + + @Test + public void testDefineHasScope() { + assertPrints("14\n", "x=14; class Foo; x=0; end; puts x"); + assertPrints("14\n", "class Foo; x=14; puts x; end"); + } + + @Test + public void testInitialise() { + assertPrints("14\n", "class Foo; def initialize; puts 14; end; end; Foo.new"); + assertPrints("14\n", "class Foo; def initialize(x); puts x; end; end; Foo.new 14"); + assertPrints("14\n", "class Foo; def initialize(x); puts x; end; end; Foo.new(14)"); + } + + @Test + public void testInstanceVariables() { + assertPrints("14\n", "class Foo; def a; @x=14; end; def b; @x; end; end; foo=Foo.new; foo.a; puts foo.b"); + } + + @Test + public void testMissingVariables() { + assertPrints("NilClass\n", "class Foo; def a; @x; end; end; foo=Foo.new; puts foo.a.class"); + } + + @Test + public void testClassConstants() { + assertPrints("14\n", "class Foo; X=14; def foo; puts X; end; end; foo=Foo.new; foo.foo"); + } + + @Test + public void testReopeningSingletonClass() { + assertPrints("1\n", "foo = Object.new; class << foo; def bar; puts 1; end; end; foo.bar"); + } + + @Test + public void testInheritance() { + assertPrints("14\n", "class Foo; def foo; puts 14; end; end; class Bar < Foo; end; Bar.new.foo"); + } + + @Test + public void testSingletonInheritance() { + assertPrints("14\n", "class Foo; def self.foo; puts 14; end; end; class Bar < Foo; end; Bar.foo"); + } + + @Test + public void testNestedClass() { + assertPrints("14\n", "class Foo; class Bar; def bar; 14; end; end; def foo; Bar.new; end; end; puts Foo.new.foo.bar"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ConstantTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ConstantTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +public class ConstantTests extends RubyTests { + + @Test + public void testTopLevelConstants() { + assertPrints("14\n", "X=14; class Foo; def foo; puts X; end; end; f=Foo.new; f.foo"); + } + + @Test + public void testNestedConstants() { + assertPrints("", "module X; class A; end; class B; class C < A; end; end; end"); + } + + @Test + public void testSearchInParentModules() { + assertPrints("14\n", "module A; C = 14; module B; def self.test; puts C; end; end; end; A::B.test"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ForTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ForTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code for} expressions. + */ +public class ForTests extends RubyTests { + + @Test + public void testArray() { + assertPrints("1\n2\n3\n", "for x in [1, 2, 3]; puts x; end"); + assertPrints("1\n2\n3\n", "y = [1, 2, 3]; for x in y; puts x; end"); + } + + @Test + public void testRange() { + assertPrints("1\n2\n3\n", "for x in 1..3; puts x; end"); + assertPrints("1\n2\n3\n", "y = 1..3; for x in y; puts x; end"); + assertPrints("1\n2\n", "for x in 1...3; puts x; end"); + } + + @Test + public void testScopeRO() { + assertPrints("14\n", "x=14; for n in [1]; puts x; end"); + } + + @Test + public void testScopeRW() { + assertPrints("14\n", "x=0; for n in [1]; x=x+14; puts x; end"); + } + + @Test + public void testNoNewScope() { + assertPrints("14\n", "for i in [1]; v = 14; end; puts v"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/GlobalVariableTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/GlobalVariableTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test global variables. + */ +public class GlobalVariableTests extends RubyTests { + + @Test + public void testMinimal() { + assertPrints("14\n", "$foo = 14; puts $foo"); + } + + @Test + public void testScope() { + assertPrints("14\n", "def foo; $bar = 14; end; foo; puts $bar"); + assertPrints("14\n", "$bar = 14; def foo; puts $bar; end; foo"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/IfTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/IfTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code if} expressions. + */ +public class IfTests extends RubyTests { + + @Test + public void testImmediateIf() { + assertPrints("1\n", "if true; puts 1 end"); + assertPrints("", "if false; puts 1 end"); + } + + @Test + public void testImmediateTrailingIf() { + assertPrints("1\n", "puts 1 if true"); + assertPrints("", "puts 1 if false"); + } + + @Test + public void testImmediateIfElse() { + assertPrints("1\n", "if true; puts 1 else puts 2 end"); + assertPrints("2\n", "if false; puts 1 else puts 2 end"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/InterpolatedStringTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/InterpolatedStringTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test interpolated strings - that is string literals with #{...} sections in them. Within those + * you can have arbitrary Ruby expressions. + */ +public class InterpolatedStringTests extends RubyTests { + + @Test + public void testBasic() { + assertPrints("123\n", "puts \"1#{2}3\""); + } + + @Test + public void testMethodCall() { + assertPrints("123\n", "def foo; 2; end; puts \"1#{foo}3\""); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/LocalTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/LocalTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test local variables. + */ +public class LocalTests extends RubyTests { + + @Test + public void testAssignmentTopLevel() { + assertPrints("1\n", "x = 1; puts x"); + } + + @Test + public void testAssignmentWithinMethod() { + assertPrints("1\n", "def foo; x = 1; puts x; end; foo"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/MethodTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/MethodTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.test.*; + +/** + * Test method definitions and calls. + */ +public class MethodTests extends RubyTests { + + @Test + public void testDefineCallNoArguments() { + assertPrints("1\n", "def foo; puts 1; end; foo()"); + assertPrints("1\n", "def foo; puts 1; end; foo"); + } + + @Test + public void testDefineCallOnePreArgument() { + assertPrints("1\n", "def foo(a); puts a; end; foo(1)"); + assertPrints("1\n", "def foo(a); puts a; end; foo 1"); + } + + @Test + public void testDefineCallTwoPreArguments() { + assertPrints("1\n2\n", "def foo(a, b); puts a; puts b; end; foo(1, 2)"); + assertPrints("1\n2\n", "def foo(a, b); puts a; puts b; end; foo 1, 2"); + } + + @Test + public void testSingleReturn() { + assertPrints("1\n", "def foo; return 1; end; puts foo"); + assertPrints("1\n", "def foo(n); return n; end; puts foo(1)"); + } + + @Test + public void testImplicitReturn() { + assertPrints("1\n", "def foo; 1; end; puts foo"); + assertPrints("3\n", "def foo; 1+2; end; puts foo"); + assertPrints("14\n", "def foo; x=14; end; puts foo"); + } + + @Test + public void testNestedCall() { + assertPrints("1\n", "def foo(n); return n; end; def bar(n); return foo(n); end; puts bar(1)"); + assertPrints("1\n1\n1\n", "def foo(n); puts n; return n; end; def bar(n); puts n; return foo(n); end; puts bar(1)"); + assertPrints("1\n1\n", "def foo(a, b); puts a; puts b; end; def bar(n); foo(n, n); end; bar(1)"); + } + + @Test + public void testNestedOperatorCall() { + assertPrints("3\n", "def foo(a, b); puts a + b; end; foo(1, 2)"); + } + + /** + * Tests that arguments are evaluated before method dispatch takes place, by putting an action + * in one of the arguments that modifies the method that we should find in dispatch. + */ + @Test + public void testArgumentsExecutedBeforeDispatch() { + /* + * We have to use Object#send instead of calling define_method directly because that method + * is private. + */ + + assertPrints("12\n", "def foo\n" + // + " Fixnum.send(:define_method, :+) do |other|\n" + // + " self - other\n" + // + " end \n" + // + " 2\n" + // + "end\n" + // + "puts 14 + foo"); + } + + @Test(expected = RaiseException.class) + public void testTooFewArguments1() { + assertPrints("", "def foo(a); end; foo()"); + } + + @Test(expected = RaiseException.class) + public void testTooFewArguments2() { + assertPrints("", "def foo(a, b); end; foo(1)"); + } + + @Test(expected = RaiseException.class) + public void testTooFewArguments3() { + assertPrints("", "def foo(a, b, c); end; foo(1, 2)"); + } + + @Test(expected = RaiseException.class) + public void testTooManyArguments1() { + assertPrints("", "def foo(); end; foo(1)"); + } + + @Test(expected = RaiseException.class) + public void testTooManyArguments2() { + assertPrints("", "def foo(a); end; foo(1, 2)"); + } + + @Test(expected = RaiseException.class) + public void testTooManyArguments3() { + assertPrints("", "def foo(a, b); end; foo(1, 2, 3)"); + } + + @Test + public void testPolymophicMethod() { + assertPrints("A\nB\n", "class A\n" + // + " def foo\n" + // + " puts \"A\"\n" + // + " end\n" + // + "end\n" + // + "\n" + // + "class B\n" + // + " def foo\n" + // + " puts \"B\"\n" + // + " end\n" + // + "end\n" + // + "\n" + // + "def bar(x)\n" + // + " x.foo\n" + // + "end\n" + // + "\n" + // + "a = A.new\n" + // + "b = B.new\n" + // + "\n" + // + "bar(a)\n" + // + "bar(b)\n"); + } + + @Test + public void testOneDefaultValue() { + assertPrints("1\n2\n", "def foo(a=1); puts a; end; foo; foo(2)"); + } + + @Test + public void testTwoDefaultValues() { + assertPrints("1\n2\n3\n2\n3\n4\n", "def foo(a=1,b=2); puts a; puts b; end; foo; foo(3); foo(3, 4)"); + } + + @Test + public void testOneDefaultValueAfterNonDefault() { + assertPrints("2\n1\n2\n3\n", "def foo(a, b=1); puts a; puts b; end; foo(2); foo(2, 3)"); + } + + @Test + public void testOneDefaultValueBeforeNonDefault() { + assertPrints("1\n2\n2\n3\n", "def foo(a=1, b); puts a; puts b; end; foo(2); foo(2, 3)"); + } + + @Test + public void testBlockArgument() { + assertPrints("1\n2\n3\n", "def foo(&block); block.call(14); end; puts 1; foo { |n| puts 2 }; puts 3"); + } + + @Test + public void testBlockArgumentWithOthers() { + assertPrints("1\n2\n3\n4\n5\n", "def foo(a, b, &block); puts a; block.call(14); puts b; end; puts 1; foo(2, 4) { |n| puts 3 }; puts 5"); + } + + @Test + public void testBlockPass() { + assertPrints("1\n2\n3\n", "def bar; yield; end; def foo(&block); bar(&block); end; puts 1; foo { puts 2 }; puts 3"); + } + + @Test + public void testBlockPassWithOthers() { + assertPrints("1\n2\n3\n4\n5\n", "def bar(a, b); puts a; yield; puts b; end; def foo(a, b, &block); bar(a, b, &block); end; puts 1; foo(2, 4) { puts 3 }; puts 5"); + } + + @Test + public void testSplatWhole() { + assertPrints("1\n2\n3\n", "def foo(a, b, c); puts a; puts b; puts c; end; d = [1, 2, 3]; foo(*d)"); + } + + @Test + public void testSplatSome() { + assertPrints("1\n2\n3\n", "def foo(a, b, c); puts a; puts b; puts c; end; d = [2, 3]; foo(1, *d)"); + } + + @Test + public void testSplatParam() { + assertPrints("[1, 2, 3]\n", "def foo(*bar); puts bar.to_s; end; foo(1, 2, 3)"); + } + + @Test + public void testSplatParamWithOther() { + assertPrints("1\n[2]\n", "def foo(a, *b); puts a.to_s; puts b.to_s; end; foo(1, 2)"); + } + + @Test + public void testSplatParamWithBlockPass() { + assertPrints("[1, 2, 3]\n", "def foo(*bar, &block); block.call(bar); end; foo(1, 2, 3) { |x| puts x.to_s }"); + } + + @Test + public void testSingletonMethod() { + assertPrints("1\n", "foo = Object.new; def foo.bar; puts 1; end; foo.bar"); + } + + @Test + public void testAlias() { + assertPrints("1\n", "def foo; puts 1; end; alias bar foo; bar"); + assertPrints("1\n", "class Foo; def foo; puts 1; end; alias bar foo; end; Foo.new.bar"); + } + + @Test + public void testAliasMethod() { + assertPrints("1\n", "class Foo; def foo; puts 1; end; alias_method :bar, :foo; end; Foo.new.bar"); + } + + @Test + public void testSymbolAsBlock() { + assertPrints("6\n", "puts [1, 2, 3].inject(&:+)"); + } + + @Test + public void testSuper() { + assertPrints("1\n2\n3\n", "class Foo; def foo; puts 2; end; end; class Bar < Foo; def foo; puts 1; super; puts 3; end; end; Bar.new.foo"); + } + + @Test + public void testSuperWithArgs() { + assertPrints("1\n2\n3\n", "class Foo; def foo(n); puts n; end; end; class Bar < Foo; def foo(n); puts 1; super(n); puts 3; end; end; Bar.new.foo(2)"); + } + + @Test + public void testPushSplat() { + assertPrints("[1, 0, 4]\n", "a = [1, 2, 3, 4]; b = [1, 2]; a[*b] = 0; puts a.to_s"); + } + + @Test + public void testBlocksPassedIntoBlocks() { + assertPrints("14\n", "def foo; 1.times do; yield; end; end; foo do; puts 14; end"); + } + + @Test + public void testBlocksNotPassedIntoFullMethods() { + assertPrints("no block\n", "def foo(&block); if block; puts 'block'; else; puts 'no block'; end; end; def bar; foo; end; bar do; end"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ModuleTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ModuleTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code module} expressions. + */ +public class ModuleTests extends RubyTests { + + @Test + public void testDefine() { + assertPrints("Foo\n", "module Foo; end; puts Foo"); + } + + @Test + public void testWithConstantDefinition() { + assertPrints("14\n", "module Foo; FOO=14; end; puts Foo::FOO"); + } + + @Test + public void testWithConstantDefinitionAndAccessInDeclaration() { + assertPrints("14\n", "module Foo; FOO=14; puts FOO; end"); + } + + @Test + public void testCanAccessObjectConstantsInModuleDefinition() { + assertPrints("", "class Foo; end; module Bar; foo = Foo.new; end"); + } + + @Test + public void testInclude() { + assertPrints("14\n", "module Foo; FOO=14; end; module Bar; include Foo; end; puts Bar::FOO"); + } + + @Test + public void testModuleFunction() { + assertPrints("1\n", "module Foo; def bar; puts 1; end; module_function :bar; end; Foo::bar"); + } + + @Test + public void testDistinctModule() { + assertPrints("true\n", "module A; module X; end; end; module B; module X; end; end; puts A::X.object_id != B::X.object_id"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/MultipleAssignmentTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/MultipleAssignmentTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test multiple assignment, which is sort of like pattern matching for arrays. The arrays can be + * implicit on the RHS. + */ +public class MultipleAssignmentTests extends RubyTests { + + @Test + public void testToLocal() { + assertPrints("1\n2\n", "x, y = [1, 2]; puts x, y"); + assertPrints("1\n2\n", "x, y = 1, 2; puts x, y"); + assertPrints("1\n2\n", "x = [1, 2]; y, z = x; puts y, z"); + } + + @Test + public void testToArrayIndex() { + assertPrints("1\n2\n", "x = []; x[0], x[1] = 1, 2; puts x"); + } + + @Test + public void testSwap() { + assertPrints("2\n1\n", "a = [1]; b = [2]; a[0], b[0] = b[0], a[0]; puts a, b"); + } + + @Test + public void testProducesArray() { + assertPrints("1\n2\n", "puts((a, b = 1, 2))"); + } + + @Test + public void testWithSplat() { + assertPrints("1\n[2, 3]\n", "a, *b = [1, 2, 3]; puts a; puts b.to_s"); + } + + @Test + public void testWithSplatEmpty() { + assertPrints("1\n[]\n", "a, *b = [1]; puts a.to_s; puts b.to_s"); + } + + @Test + public void testWithSingleValueRHS() { + assertPrints("14\n\n\n", "a, b, c = 14; puts a; puts b; puts c"); + assertPrints("\n\n\n", "a, b, c = nil; puts a; puts b; puts c"); + } + + @Test + public void testWithSingleSplatLHSAndSingleValueRHS() { + assertPrints("[14]\n", "a = *14; puts a.to_s"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/OrTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/OrTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code or} expressions, which unusually for Ruby are not methods. This is because with + * Ruby's eager evaluation there would be no way to implement the short circuit semantics. + */ +public class OrTests extends RubyTests { + + @Test + public void testImmediate() { + assertPrints("false\n", "puts false || false"); + assertPrints("true\n", "puts true || false"); + assertPrints("true\n", "puts false || true"); + assertPrints("true\n", "puts true || true"); + assertPrints("false\n", "puts (false or false)"); + assertPrints("true\n", "puts (true or false)"); + assertPrints("true\n", "puts (false or true)"); + assertPrints("true\n", "puts (true or true)"); + } + + @Test + public void testShortCircuits() { + assertPrints("false\nfalse\nfalse\n", "x = y = false; puts (x = false) || (y = false); puts x, y"); + assertPrints("true\ntrue\nfalse\n", "x = y = false; puts (x = true) || (y = false); puts x, y"); + assertPrints("true\nfalse\ntrue\n", "x = y = false; puts (x = false) || (y = true); puts x, y"); + assertPrints("true\ntrue\nfalse\n", "x = y = false; puts (x = true) || (y = true); puts x, y"); + assertPrints("false\nfalse\nfalse\n", "x = y = false; puts ((x = false) or (y = false)); puts x, y"); + assertPrints("true\ntrue\nfalse\n", "x = y = false; puts ((x = true) or (y = false)); puts x, y"); + assertPrints("true\nfalse\ntrue\n", "x = y = false; puts ((x = false) or (y = true)); puts x, y"); + assertPrints("true\ntrue\nfalse\n", "x = y = false; puts ((x = true) or (y = true)); puts x, y"); + } + + @Test + public void testOrAssign() { + assertPrints("true\n", "def foo; puts 1; false; end; x = true; x ||= foo; puts x"); + assertPrints("1\nfalse\n", "def foo; puts 1; false; end; x = false; x ||= foo; puts x"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/PolymorphismTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/PolymorphismTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test characteristics of polymorphism. + */ +public class PolymorphismTests extends RubyTests { + + /** + * Test that a polymorphic method that will specialize to double will then not treat integers as + * doubles. + */ + @Test + public void testSimpleOperatorRedefinition() { + assertPrints("16.759999999999998\n16\n", // + "def add(x, y)\n" + // + " x + y\n" + // + "end\n" + // + "puts add(14.5, 2.26)\n" + // + "puts add(14, 2)\n"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/RaiseRescueTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/RaiseRescueTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.runtime.control.*; +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code raise} and {@code rescue}. + */ +public class RaiseRescueTests extends RubyTests { + + @Test(expected = RaiseException.class) + public void testZeroDivisionError() { + assertPrints("", "1/0"); + } + + @Test + public void testBeginRescue() { + assertPrints("", "begin; rescue; end"); + assertPrints("", "begin; rescue => e; end"); + assertPrints("", "begin; rescue ZeroDivisionError => e; end"); + assertPrints("", "begin; rescue; ensure; end"); + assertPrints("", "begin; rescue => e; ensure; end"); + assertPrints("", "begin; rescue ZeroDivisionError => e; ensure; end"); + assertPrints("", "begin; rescue; else; end"); + assertPrints("", "begin; rescue => e; else; end"); + assertPrints("", "begin; rescue ZeroDivisionError => e; else; end"); + assertPrints("", "def foo; rescue; end"); + assertPrints("", "def foo; rescue => e; end"); + assertPrints("", "def foo; rescue ZeroDivisionError => e; end"); + assertPrints("", "def foo; rescue; ensure; end"); + assertPrints("", "def foo; rescue => e; ensure; end"); + assertPrints("", "def foo; rescue ZeroDivisionError => e; ensure; end"); + assertPrints("", "def foo; rescue; else; end"); + assertPrints("", "def foo; rescue => e; else; end"); + assertPrints("", "def foo; rescue ZeroDivisionError => e; else; end"); + } + + @Test + public void testRescueZeroDivisionError() { + assertPrints("divided by 0\n3\n4\n", "begin; 1/0; puts 1; rescue => e; puts e; else puts 2; ensure puts 3; end; puts 4"); + assertPrints("divided by 0\n3\n4\n", "begin; 1/0; puts 1; rescue ZeroDivisionError => e; puts e; else puts 2; ensure puts 3; end; puts 4"); + assertPrints("1\n2\n3\n4\n", "begin; 1/1; puts 1; rescue ZeroDivisionError => e; puts e; else puts 2; ensure puts 3; end; puts 4"); + assertPrints("1\n2\n3\n4\n", "begin; 1/1; puts 1; rescue ZeroDivisionError => e; puts e; rescue NameError => e; puts e; else puts 2; ensure puts 3; end; puts 4"); + } + + @Test + public void testSplatRescue() { + assertPrints("2\n", "ERRORS=[ZeroDivisionError]; begin; 1/0; puts 1; rescue *ERRORS; puts 2; end"); + } + + @Test + public void testRetry() { + assertPrints("1\n3\n1\n2\n5\n", "x=0; begin; puts 1; 1/x; puts 2; rescue ZeroDivisionError; puts 3; x=1; retry; puts 4; end; puts 5"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/RedefinitionTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/RedefinitionTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test that methods can be redefined. + */ +public class RedefinitionTests extends RubyTests { + + /** + * Test that a method on Fixnum can be redefined and that we will use the new definition. The + * call that expects to find the redefinition needs to have already been specialized, which is + * why do it through a method. + */ + @Test + public void testSimpleOperatorRedefinition() { + assertPrints("3\n-1\n", // + "def add(x, y)\n" + // + " x + y\n" + // + "end\n" + // + "puts add(1, 2)\n" + // + "class Fixnum\n" + // + " def +(other)\n" + // + " self - other\n" + // + " end\n" + // + "end\n" + // + "puts add(1, 2)\n"); + } + + /** + * This is quite a subtle test. We redefine Float#+. We have a method which calls +. It is + * initially specialized to Fixnum, but then used with Float. The tricky bit is that Float has + * not been redefined since the operator has been specialized. The operator thinks it is up to + * date. When we emit a + node, we need to check if any class that that node could specialize to + * has been redefined. + */ + @Test + public void testSpecialisationConsidersRedefinitionInOtherClasses() { + assertPrints("16\n12.25\n", // + "def add(a, b)\n" + // + " a + b\n" + // + "end\n" + // + "class Float\n" + // + " def +(other)\n" + // + " self - other\n" + // + " end\n" + // + "end\n" + // + "puts add(14, 2)\n" + // + "puts add(14.5, 2.25)"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ShortcutTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/ShortcutTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test shortcut expressions (syntactic sugar) such as {@code +=} and {@code |=}. + */ +public class ShortcutTests extends RubyTests { + + @Test + public void testShortcutAddAssign() { + assertPrints("2\n", "x = 1; x += 1; puts x"); + } + + @Test + public void testShortcutSubAssign() { + assertPrints("0\n", "x = 1; x -= 1; puts x"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/SpecialVariableTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/SpecialVariableTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test variables with special semantics, such as the 'global variables', {@code $_}, {@code $~} + * etc. + */ +public class SpecialVariableTests extends RubyTests { + + @Test + public void testGetsResult() { + assertPrintsWithInput("test\n", "gets; puts $_", "test\n"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/UntilTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/UntilTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code until} expressions. + */ +public class UntilTests extends RubyTests { + + @Test + public void testSimpleWhile() { + assertPrints("0\n", "x = 10; until x == 0; x -= 1; end; puts x"); + assertPrints("10\n", "x = 0; until x == 10; x += 1; end; puts x"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/WhileTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/language/WhileTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.language; + +import org.junit.*; + +import com.oracle.truffle.ruby.test.*; + +/** + * Test {@code while} expressions. + */ +public class WhileTests extends RubyTests { + + @Test + public void testSimpleWhile() { + assertPrints("0\n", "x = 10; while x > 0; x -= 1; end; puts x"); + assertPrints("10\n", "x = 0; while x < 10; x += 1; end; puts x"); + } + + @Test + public void testBreak() { + assertPrints("1\n", "x = 0; while x < 10; x += 1; break; end; puts x"); + } + + @Test + public void testNext() { + assertPrints("10\n", "x = 0; while x < 10; x += 1; next; end; puts x"); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/runtime/ObjectLayoutTests.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.ruby.test/src/com/oracle/truffle/ruby/test/runtime/ObjectLayoutTests.java Mon Jan 06 17:12:09 2014 +0000 @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This + * code is released under a tri EPL/GPL/LGPL license. You can use it, + * redistribute it and/or modify it under the terms of the: + * + * Eclipse Public License version 1.0 + * GNU General Public License version 2 + * GNU Lesser General Public License version 2.1 + */ +package com.oracle.truffle.ruby.test.runtime; + +import org.junit.*; + +import com.oracle.truffle.ruby.runtime.*; +import com.oracle.truffle.ruby.runtime.core.*; +import com.oracle.truffle.ruby.runtime.objects.*; + +import static org.junit.Assert.*; + +/** + * Test the object layout classes. + */ +public class ObjectLayoutTests { + + @Test + public void testNewInstanceVariable() { + final RubyContext context = new RubyContext(null); + + // Create a class and an instance + + final RubyClass classA = new RubyClass(context, null, null, null, "A"); + final ObjectLayout layoutClassA = classA.getObjectLayoutForInstances(); + + final RubyBasicObject objectA = new RubyBasicObject(classA); + final ObjectLayout layoutObjectA = objectA.getObjectLayout(); + + // Add an instance variable to the instance + + objectA.setInstanceVariable("foo", 14); + + // That should have changed the layout of the class + + assertNotSame(layoutClassA, classA.getObjectLayoutForInstances()); + + // If we notify the object, it should also change the layout of that + + objectA.updateLayout(); + assertNotSame(layoutObjectA, objectA.getObjectLayout()); + + // We should be able to find that instance variable as a storage location in the class + + assertNotNull(classA.getObjectLayoutForInstances().findStorageLocation("foo")); + + // We should be able to read that value back out + + assertEquals(14, objectA.getInstanceVariable("foo")); + } + + @Test + public void testOverflowPrimitives() { + final RubyContext context = new RubyContext(null); + + // Create a class and an instance + + final RubyClass classA = new RubyClass(context, null, null, null, "A"); + final RubyBasicObject objectA = new RubyBasicObject(classA); + + // Add many more Fixnums that we have space for primitives + + final int count = 100; + + for (int n = 0; n < count; n++) { + objectA.setInstanceVariable("foo" + n, n); + } + + // We should be able to read them back out + + for (int n = 0; n < count; n++) { + assertEquals(n, objectA.getInstanceVariable("foo" + n)); + } + } + + @Test + public void testGeneralisation() { + final RubyContext context = new RubyContext(null); + + // Create a class and two instances + + final RubyClass classA = new RubyClass(context, null, null, null, "A"); + final RubyBasicObject object1 = new RubyBasicObject(classA); + final RubyBasicObject object2 = new RubyBasicObject(classA); + + // Set an instance variable to be a Fixnum in object 1 + + object1.setInstanceVariable("foo", 14); + + // We should be able to read that instance variable back, and it should still be a Fixnum + + assertEquals(14, object1.getInstanceVariable("foo")); + assertSame(Integer.class, object1.getInstanceVariable("foo").getClass()); + + // The underlying instance store should be Fixnum + + assertSame(FixnumStorageLocation.class, object1.getObjectLayout().findStorageLocation("foo").getClass()); + + /* + * The same instance variable in object 2 should be Nil. Note that this requires that we + * realise that even though the instance variable is known about in the layout of object 2, + * and we are using a primitive int to hold it, that it hasn't been set and is actually Nil. + * We don't want it to appear as 0. + */ + + assertSame(NilPlaceholder.INSTANCE, object2.getInstanceVariable("foo")); + + /* + * We should be able to set the same instance variable in object 2 to also be a Fixnum + * without changing the layout. + */ + + final ObjectLayout objectLayout2 = object2.getObjectLayout(); + object2.setInstanceVariable("foo", 2); + assertEquals(2, object2.getInstanceVariable("foo")); + assertSame(Integer.class, object2.getInstanceVariable("foo").getClass()); + assertSame(objectLayout2, object2.getObjectLayout()); + + // Set the instance variable in object 2 to be a Float + + object2.setInstanceVariable("foo", 2.25); + + // We should be able to read that instance variable back, and it should still be a Fixnum + + assertEquals(2.25, object2.getInstanceVariable("foo")); + assertSame(Double.class, object2.getInstanceVariable("foo").getClass()); + + // Object 1 should give still think the instance variable is a Fixnum + + assertEquals(14, object1.getInstanceVariable("foo")); + assertSame(Integer.class, object1.getInstanceVariable("foo").getClass()); + + // The underlying instance store in both objects should now be Object + + assertSame(ObjectStorageLocation.class, object1.getObjectLayout().findStorageLocation("foo").getClass()); + assertSame(ObjectStorageLocation.class, object2.getObjectLayout().findStorageLocation("foo").getClass()); + + } + + @Test + public void testSubclasses() { + final RubyContext context = new RubyContext(null); + + // Create two classes, A, and a subclass, B, and an instance of each + + final RubyClass classA = new RubyClass(context, null, null, null, "A"); + final RubyClass classB = new RubyClass(context, null, null, classA, "B"); + + ObjectLayout layoutClassA = classA.getObjectLayoutForInstances(); + ObjectLayout layoutClassB = classA.getObjectLayoutForInstances(); + + final RubyBasicObject objectA = new RubyBasicObject(classA); + final RubyBasicObject objectB = new RubyBasicObject(classB); + + ObjectLayout layoutObjectA = objectA.getObjectLayout(); + ObjectLayout layoutObjectB = objectB.getObjectLayout(); + + // Add an instance variable to the instance of A + + objectA.setInstanceVariable("foo", 14); + + // That should have changed the layout of both classes + + assertNotSame(layoutClassA, classA.getObjectLayoutForInstances()); + assertNotSame(layoutClassB, classB.getObjectLayoutForInstances()); + + layoutClassA = classA.getObjectLayoutForInstances(); + layoutClassB = classA.getObjectLayoutForInstances(); + + // If we notify the objects, both of them should have changed layouts + + objectA.updateLayout(); + objectB.updateLayout(); + assertNotSame(layoutObjectA, objectA.getObjectLayout()); + assertNotSame(layoutObjectB, objectB.getObjectLayout()); + + layoutObjectA = objectA.getObjectLayout(); + layoutObjectB = objectB.getObjectLayout(); + + // We should be able to find that instance variable as a storage location in both classes + + assertNotNull(classA.getObjectLayoutForInstances().findStorageLocation("foo")); + assertNotNull(classB.getObjectLayoutForInstances().findStorageLocation("foo")); + + // We should be able to read that value back out + + assertEquals(14, objectA.getInstanceVariable("foo")); + + // Add an instance variable to the instance of B + + objectB.setInstanceVariable("bar", 2); + + // This should not have changed the layout of A or the instance of A + + assertSame(layoutClassA, classA.getObjectLayoutForInstances()); + assertSame(layoutObjectA, objectA.getObjectLayout()); + + // But the layout of B and the instance of B should have changed + + assertNotSame(layoutClassB, classB.getObjectLayoutForInstances()); + + objectB.updateLayout(); + assertNotSame(layoutObjectB, objectB.getObjectLayout()); + + // We should be able to find the new instance variable in the instance of B but not A + + assertNull(classA.getObjectLayoutForInstances().findStorageLocation("bar")); + assertNotNull(classB.getObjectLayoutForInstances().findStorageLocation("bar")); + + // We should be able to read that value back out + + assertEquals(2, objectB.getInstanceVariable("bar")); + } + + @Test + public void testPerObjectInstanceVariables() { + final RubyContext context = new RubyContext(null); + + // Create a class and an instance + + final RubyClass classA = new RubyClass(context, context.getCoreLibrary().getClassClass(), null, null, "A"); + final RubyBasicObject objectA = new RubyBasicObject(classA); + + ObjectLayout layoutClassA = classA.getObjectLayoutForInstances(); + ObjectLayout layoutObjectA = objectA.getObjectLayout(); + + // Add an instance variable to the instance of A + + objectA.setInstanceVariable("foo", 2); + + // That should have changed the layout of the class and the object + + assertNotSame(layoutClassA, classA.getObjectLayoutForInstances()); + assertNotSame(layoutObjectA, objectA.getObjectLayout()); + layoutClassA = classA.getObjectLayoutForInstances(); + layoutObjectA = classA.getObjectLayout(); + + // We should be able to read the value back out + + assertEquals(2, objectA.getInstanceVariable("foo")); + + /* + * Switch object A to having a private object layout, as would be done by calls such as + * instance_variable_set. + */ + + objectA.switchToPrivateLayout(); + + // Set an instance variable on object A + + objectA.setInstanceVariable("bar", 14); + + // The layout of object A, however, should have changed + + // CS: it hasn't changed because it's still null + // assertNotSame(layoutObjectA, objectA.getObjectLayout()); + + // We should be able to read the value back out + + assertEquals(14, objectA.getInstanceVariable("bar")); + + /* + * We should also be able to read the first variable back out, even though we've switched to + * private layout since then. + */ + + assertEquals(2, objectA.getInstanceVariable("foo")); + } + +} diff -r 64a23ce736a0 -r 0fbee3eb71f0 mx/mx_graal.py --- a/mx/mx_graal.py Mon Jan 06 14:21:39 2014 +0100 +++ b/mx/mx_graal.py Mon Jan 06 17:12:09 2014 +0000 @@ -1363,6 +1363,17 @@ def isGraalEnabled(vm): return vm != 'original' and not vm.endswith('nograal') +def rubyShellCp(): + return mx.classpath("com.oracle.truffle.ruby.shell") + +def rubyShellClass(): + return "com.oracle.truffle.ruby.shell.Shell" + +def ruby(args): + """run a Ruby program or shell""" + vmArgs, rubyArgs = _extract_VM_args(args, useDoubleDash=True) + vm(vmArgs + ['-cp', rubyShellCp(), rubyShellClass()] + rubyArgs) + def site(args): """create a website containing javadoc and the project dependency graph""" @@ -1522,6 +1533,7 @@ 'longtests' : [longtests, ''], 'sl' : [sl, '[SL args|@VM options]'], 'trufflejar' : [trufflejar, ''], + 'ruby' : [ruby, '[Ruby args|@VM options]'] } mx.add_argument('--jacoco', help='instruments com.oracle.* classes using JaCoCo', default='off', choices=['off', 'on', 'append']) diff -r 64a23ce736a0 -r 0fbee3eb71f0 mx/projects --- a/mx/projects Mon Jan 06 14:21:39 2014 +0100 +++ b/mx/projects Mon Jan 06 17:12:09 2014 +0000 @@ -26,6 +26,47 @@ library@OKRA@path=lib/okra-1.2.jar library@OKRA@urls=http://cr.openjdk.java.net/~tdeneau/okra-1.2.jar +library@JRUBYPARSER@path=lib/jrubyparser-0.5.0.jar +library@JRUBYPARSER@urls=http://repo1.maven.org/maven2/org/jruby/jrubyparser/0.5.0/jrubyparser-0.5.0.jar + +library@JLINE@path=lib/jline-2.10.jar +library@JLINE@urls=http://repo1.maven.org/maven2/jline/jline/2.10/jline-2.10.jar + +library@JRUBYSTDLIB@path=lib/jruby-stdlib-1.7.4.jar +library@JRUBYSTDLIB@urls=http://repo1.maven.org/maven2/org/jruby/jruby-stdlib/1.7.4/jruby-stdlib-1.7.4.jar + +library@JNR_POSIX@path=lib/jnr-posix-3.0.0.jar +library@JNR_POSIX@urls=http://repo1.maven.org/maven2/com/github/jnr/jnr-posix/3.0.0/jnr-posix-3.0.0.jar + +library@JNR_CONSTANTS@path=lib/jnr-constants-0.8.4.jar +library@JNR_CONSTANTS@urls=http://repo1.maven.org/maven2/com/github/jnr/jnr-constants/0.8.4/jnr-constants-0.8.4.jar + +library@JNR_FFI@path=lib/jnr-ffi-1.0.4.jar +library@JNR_FFI@urls=http://repo1.maven.org/maven2/com/github/jnr/jnr-ffi/1.0.4/jnr-ffi-1.0.4.jar + +library@JFFI@path=lib/jffi-1.2.1.jar +library@JFFI@urls=http://repo1.maven.org/maven2/com/github/jnr/jffi/1.2.1/jffi-1.2.1.jar + +library@JFFI_NATIVE@path=lib/jffi-1.2.1-native.jar +library@JFFI_NATIVE@urls=http://search.maven.org/remotecontent?filepath=com/github/jnr/jffi/1.2.1/jffi-1.2.1-native.jar + +library@JNR_X86ASM@path=lib/jnr-x86asm-1.0.2.jar +library@JNR_X86ASM@urls=http://repo1.maven.org/maven2/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar + +library@ASM@path=lib/asm-4.0.jar +library@ASM@urls=http://repo1.maven.org/maven2/org/ow2/asm/asm/4.0/asm-4.0.jar + +library@ASM_ANALYSIS@path=lib/asm-analysis-4.0.jar +library@ASM_ANALYSIS@urls=http://repo1.maven.org/maven2/org/ow2/asm/asm-analysis/4.0/asm-analysis-4.0.jar + +library@ASM_COMMONS@path=lib/asm-commons-4.0.jar +library@ASM_COMMONS@urls=http://repo1.maven.org/maven2/org/ow2/asm/asm-commons/4.0/asm-commons-4.0.jar + +library@ASM_TREE@path=lib/asm-tree-4.0.jar +library@ASM_TREE@urls=http://repo1.maven.org/maven2/org/ow2/asm/asm-tree/4.0/asm-tree-4.0.jar + +library@ASM_UTIL@path=lib/asm-util-4.0.jar +library@ASM_UTIL@urls=http://repo1.maven.org/maven2/org/ow2/asm/asm-util/4.0/asm-util-4.0.jar distribution@GRAAL@path=graal.jar distribution@GRAAL@dependencies=\ @@ -691,3 +732,43 @@ project@com.oracle.graal.truffle.hotspot.amd64@javaCompliance=1.7 project@com.oracle.graal.truffle.hotspot.amd64@annotationProcessors=com.oracle.graal.service.processor project@com.oracle.graal.truffle.hotspot.amd64@workingSets=Graal,Truffle + +# truffle.ruby.runtime +project@com.oracle.truffle.ruby.runtime@subDir=graal +project@com.oracle.truffle.ruby.runtime@sourceDirs=src +project@com.oracle.truffle.ruby.runtime@dependencies=JRUBYSTDLIB,JNR_POSIX,JNR_CONSTANTS,JNR_FFI,JFFI,JFFI_NATIVE,JNR_X86ASM,ASM,ASM_ANALYSIS,ASM_COMMONS,ASM_TREE,ASM_UTIL,com.oracle.truffle.api +project@com.oracle.truffle.ruby.runtime@javaCompliance=1.7 +project@com.oracle.truffle.ruby.runtime@workingSets=Truffle,Ruby + +# truffle.ruby.nodes +project@com.oracle.truffle.ruby.nodes@subDir=graal +project@com.oracle.truffle.ruby.nodes@sourceDirs=src +project@com.oracle.truffle.ruby.nodes@dependencies=com.oracle.truffle.ruby.runtime,com.oracle.truffle.api.dsl +project@com.oracle.truffle.ruby.nodes@checkstyle=com.oracle.truffle.ruby.runtime +project@com.oracle.truffle.ruby.nodes@javaCompliance=1.7 +project@com.oracle.truffle.ruby.nodes@annotationProcessors=com.oracle.truffle.dsl.processor +project@com.oracle.truffle.ruby.nodes@workingSets=Truffle,Ruby + +# truffle.ruby.parser +project@com.oracle.truffle.ruby.parser@subDir=graal +project@com.oracle.truffle.ruby.parser@sourceDirs=src +project@com.oracle.truffle.ruby.parser@dependencies=JRUBYPARSER,com.oracle.truffle.ruby.nodes +project@com.oracle.truffle.ruby.parser@checkstyle=com.oracle.truffle.ruby.runtime +project@com.oracle.truffle.ruby.parser@javaCompliance=1.7 +project@com.oracle.truffle.ruby.parser@workingSets=Truffle,Ruby + +# truffle.ruby.shell +project@com.oracle.truffle.ruby.shell@subDir=graal +project@com.oracle.truffle.ruby.shell@sourceDirs=src +project@com.oracle.truffle.ruby.shell@dependencies=JLINE,com.oracle.truffle.ruby.parser +project@com.oracle.truffle.ruby.shell@checkstyle=com.oracle.truffle.ruby.runtime +project@com.oracle.truffle.ruby.shell@javaCompliance=1.7 +project@com.oracle.truffle.ruby.shell@workingSets=Truffle,Ruby + +# truffle.ruby.test +project@com.oracle.truffle.ruby.test@subDir=graal +project@com.oracle.truffle.ruby.test@sourceDirs=src +project@com.oracle.truffle.ruby.test@dependencies=com.oracle.truffle.ruby.parser,JUNIT +project@com.oracle.truffle.ruby.test@checkstyle=com.oracle.truffle.ruby.runtime +project@com.oracle.truffle.ruby.test@javaCompliance=1.7 +project@com.oracle.truffle.ruby.test@workingSets=Truffle,Ruby,Test