8005403: Open-source Nashorn

Co-authored-by: Akhil Arora <akhil.arora@oracle.com>
Co-authored-by: Andreas Woess <andreas.woess@jku.at>
Co-authored-by: Attila Szegedi <attila.szegedi@oracle.com>
Co-authored-by: Hannes Wallnoefer <hannes.wallnoefer@oracle.com>
Co-authored-by: Henry Jen <henry.jen@oracle.com>
Co-authored-by: Marcus Lagergren <marcus.lagergren@oracle.com>
Co-authored-by: Pavel Semenov <pavel.semenov@oracle.com>
Co-authored-by: Pavel Stepanov <pavel.stepanov@oracle.com>
Co-authored-by: Petr Hejl <petr.hejl@oracle.com>
Co-authored-by: Petr Pisl <petr.pisl@oracle.com>
Co-authored-by: Sundararajan Athijegannathan <sundararajan.athijegannathan@oracle.com>
Reviewed-by: attila, hannesw, lagergren, sundar
This commit is contained in:
Jim Laskey 2012-12-21 16:36:24 -04:00
parent 958099576a
commit 98762d6ee0
1176 changed files with 188153 additions and 1 deletions

View File

@ -1,4 +1,25 @@
syntax: glob
^build/
^dist/
/nbproject/private/
^.hgtip
/nbproject/private/
^webrev/
webrev.zip
*.class
*.log
*.orig
hotspot.log
private.properties
genfiles.properties
private.xml
.DS_Store*
TEST-*.xml
TESTS-*.xml
report.xml
CC/
jcov2/
buildtools/nasgen/nbproject/private/
buildtools/nasgen/dist/
buildtools/nasgen/build/
.idea/*

View File

@ -0,0 +1,27 @@
OPENJDK ASSEMBLY EXCEPTION
The OpenJDK source code made available by Sun at openjdk.java.net and
openjdk.dev.java.net ("OpenJDK Code") is distributed under the terms of the
GNU General Public License <http://www.gnu.org/copyleft/gpl.html> version 2
only ("GPL2"), with the following clarification and special exception.
Linking this OpenJDK Code statically or dynamically with other code
is making a combined work based on this library. Thus, the terms
and conditions of GPL2 cover the whole combination.
As a special exception, Sun gives you permission to link this
OpenJDK Code with certain code licensed by Sun as indicated at
http://openjdk.java.net/legal/exception-modules-2007-05-08.html
("Designated Exception Modules") to produce an executable,
regardless of the license terms of the Designated Exception Modules,
and to copy and distribute the resulting executable under GPL2,
provided that the Designated Exception Modules continue to be
governed by the licenses under which they were offered by Sun.
As such, it allows licensees and sublicensees of Sun's GPL2 OpenJDK Code to
build an executable that includes those portions of necessary code that Sun
could not provide under GPL2 (or that Sun has provided under GPL2 with the
Classpath exception). If you modify or add to the OpenJDK code, that new
GPL2 code may still be combined with Designated Exception Modules if the
new code is made subject to this exception by its copyright holder.

347
nashorn/LICENSE Normal file
View File

@ -0,0 +1,347 @@
The GNU General Public License (GPL)
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies of this license
document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your freedom to share
and change it. By contrast, the GNU General Public License is intended to
guarantee your freedom to share and change free software--to make sure the
software is free for all its users. This General Public License applies to
most of the Free Software Foundation's software and to any other program whose
authors commit to using it. (Some other Free Software Foundation software is
covered by the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not price. Our
General Public Licenses are designed to make sure that you have the freedom to
distribute copies of free software (and charge for this service if you wish),
that you receive source code or can get it if you want it, that you can change
the software or use pieces of it in new free programs; and that you know you
can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny
you these rights or to ask you to surrender the rights. These restrictions
translate to certain responsibilities for you if you distribute copies of the
software, or if you modify it.
For example, if you distribute copies of such a program, whether gratis or for
a fee, you must give the recipients all the rights that you have. You must
make sure that they, too, receive or can get the source code. And you must
show them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and (2)
offer you this license which gives you legal permission to copy, distribute
and/or modify the software.
Also, for each author's protection and ours, we want to make certain that
everyone understands that there is no warranty for this free software. If the
software is modified by someone else and passed on, we want its recipients to
know that what they have is not the original, so that any problems introduced
by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software patents. We
wish to avoid the danger that redistributors of a free program will
individually obtain patent licenses, in effect making the program proprietary.
To prevent this, we have made it clear that any patent must be licensed for
everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and modification
follow.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains a notice
placed by the copyright holder saying it may be distributed under the terms of
this General Public License. The "Program", below, refers to any such program
or work, and a "work based on the Program" means either the Program or any
derivative work under copyright law: that is to say, a work containing the
Program or a portion of it, either verbatim or with modifications and/or
translated into another language. (Hereinafter, translation is included
without limitation in the term "modification".) Each licensee is addressed as
"you".
Activities other than copying, distribution and modification are not covered by
this License; they are outside its scope. The act of running the Program is
not restricted, and the output from the Program is covered only if its contents
constitute a work based on the Program (independent of having been made by
running the Program). Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's source code as
you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this License
and to the absence of any warranty; and give any other recipients of the
Program a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and you may
at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion of it, thus
forming a work based on the Program, and copy and distribute such modifications
or work under the terms of Section 1 above, provided that you also meet all of
these conditions:
a) You must cause the modified files to carry prominent notices stating
that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in whole or
in part contains or is derived from the Program or any part thereof, to be
licensed as a whole at no charge to all third parties under the terms of
this License.
c) If the modified program normally reads commands interactively when run,
you must cause it, when started running for such interactive use in the
most ordinary way, to print or display an announcement including an
appropriate copyright notice and a notice that there is no warranty (or
else, saying that you provide a warranty) and that users may redistribute
the program under these conditions, and telling the user how to view a copy
of this License. (Exception: if the Program itself is interactive but does
not normally print such an announcement, your work based on the Program is
not required to print an announcement.)
These requirements apply to the modified work as a whole. If identifiable
sections of that work are not derived from the Program, and can be reasonably
considered independent and separate works in themselves, then this License, and
its terms, do not apply to those sections when you distribute them as separate
works. But when you distribute the same sections as part of a whole which is a
work based on the Program, the distribution of the whole must be on the terms
of this License, whose permissions for other licensees extend to the entire
whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your
rights to work written entirely by you; rather, the intent is to exercise the
right to control the distribution of derivative or collective works based on
the Program.
In addition, mere aggregation of another work not based on the Program with the
Program (or with a work based on the Program) on a volume of a storage or
distribution medium does not bring the other work under the scope of this
License.
3. You may copy and distribute the Program (or a work based on it, under
Section 2) in object code or executable form under the terms of Sections 1 and
2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source
code, which must be distributed under the terms of Sections 1 and 2 above
on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to
give any third party, for a charge no more than your cost of physically
performing source distribution, a complete machine-readable copy of the
corresponding source code, to be distributed under the terms of Sections 1
and 2 above on a medium customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to
distribute corresponding source code. (This alternative is allowed only
for noncommercial distribution and only if you received the program in
object code or executable form with such an offer, in accord with
Subsection b above.)
The source code for a work means the preferred form of the work for making
modifications to it. For an executable work, complete source code means all
the source code for all modules it contains, plus any associated interface
definition files, plus the scripts used to control compilation and installation
of the executable. However, as a special exception, the source code
distributed need not include anything that is normally distributed (in either
source or binary form) with the major components (compiler, kernel, and so on)
of the operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the source
code from the same place counts as distribution of the source code, even though
third parties are not compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program except as
expressly provided under this License. Any attempt otherwise to copy, modify,
sublicense or distribute the Program is void, and will automatically terminate
your rights under this License. However, parties who have received copies, or
rights, from you under this License will not have their licenses terminated so
long as such parties remain in full compliance.
5. You are not required to accept this License, since you have not signed it.
However, nothing else grants you permission to modify or distribute the Program
or its derivative works. These actions are prohibited by law if you do not
accept this License. Therefore, by modifying or distributing the Program (or
any work based on the Program), you indicate your acceptance of this License to
do so, and all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the Program),
the recipient automatically receives a license from the original licensor to
copy, distribute or modify the Program subject to these terms and conditions.
You may not impose any further restrictions on the recipients' exercise of the
rights granted herein. You are not responsible for enforcing compliance by
third parties to this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues), conditions
are imposed on you (whether by court order, agreement or otherwise) that
contradict the conditions of this License, they do not excuse you from the
conditions of this License. If you cannot distribute so as to satisfy
simultaneously your obligations under this License and any other pertinent
obligations, then as a consequence you may not distribute the Program at all.
For example, if a patent license would not permit royalty-free redistribution
of the Program by all those who receive copies directly or indirectly through
you, then the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply and
the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or
other property right claims or to contest validity of any such claims; this
section has the sole purpose of protecting the integrity of the free software
distribution system, which is implemented by public license practices. Many
people have made generous contributions to the wide range of software
distributed through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing to
distribute software through any other system and a licensee cannot impose that
choice.
This section is intended to make thoroughly clear what is believed to be a
consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain
countries either by patents or by copyrighted interfaces, the original
copyright holder who places the Program under this License may add an explicit
geographical distribution limitation excluding those countries, so that
distribution is permitted only in or among countries not thus excluded. In
such case, this License incorporates the limitation as if written in the body
of this License.
9. The Free Software Foundation may publish revised and/or new versions of the
General Public License from time to time. Such new versions will be similar in
spirit to the present version, but may differ in detail to address new problems
or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any later
version", you have the option of following the terms and conditions either of
that version or of any later version published by the Free Software Foundation.
If the Program does not specify a version number of this License, you may
choose any version ever published by the Free Software Foundation.
10. If you wish to incorporate parts of the Program into other free programs
whose distribution conditions are different, write to the author to ask for
permission. For software which is copyrighted by the Free Software Foundation,
write to the Free Software Foundation; we sometimes make exceptions for this.
Our decision will be guided by the two goals of preserving the free status of
all derivatives of our free software and of promoting the sharing and reuse of
software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR
THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE
PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL
ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE
PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR
INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA
BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER
OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest possible
use to the public, the best way to achieve this is to make it free software
which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest to attach
them to the start of each source file to most effectively convey the exclusion
of warranty; and each file should have at least the "copyright" line and a
pointer to where the full notice is found.
One line to give the program's name and a brief idea of what it does.
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc., 59
Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this when it
starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free
software, and you are welcome to redistribute it under certain conditions;
type 'show c' for details.
The hypothetical commands 'show w' and 'show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may be
called something other than 'show w' and 'show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your school,
if any, to sign a "copyright disclaimer" for the program, if necessary. Here
is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
'Gnomovision' (which makes passes at compilers) written by James Hacker.
signature of Ty Coon, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General Public
License instead of this License.
"CLASSPATH" EXCEPTION TO THE GPL
Certain source files distributed by Oracle America and/or its affiliates are
subject to the following clarification and special exception to the GPL, but
only where Oracle has expressly included in the particular source file's header
the words "Oracle designates this particular file as subject to the "Classpath"
exception as provided by Oracle in the LICENSE file that accompanied this code."
Linking this library statically or dynamically with other modules is making
a combined work based on this library. Thus, the terms and conditions of
the GNU General Public License cover the whole combination.
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules,
and to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent module,
the terms and conditions of the license of that module. An independent
module is a module which is not derived from or based on this library. If
you modify this library, you may extend this exception to your version of
the library, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version.

147
nashorn/README Normal file
View File

@ -0,0 +1,147 @@
- What is Nashorn?
Nashorn is a runtime environment for programs written in ECMAScript 5.1
that runs on top of JVM.
- How to find out more about ECMAScript 5.1?
The specification can be found at
http://www.ecma-international.org/publications/standards/Ecma-262.htm
- How to checkout sources of Nashorn project?
Nashorn project uses Mercurial source code control system. You can
download Mercurial from http://mercurial.selenic.com/wiki/Download
Information about the forest extension can be found at
http://mercurial.selenic.com/wiki/ForestExtension
and downlaoded using
hg clone https://bitbucket.org/gxti/hgforest
You can clone Nashorn Mercurial forest using this command:
hg fclone http://hg.openjdk.java.net/nashorn/jdk8 nashorn~jdk8
To update your copy of the forest (fwith the latest code:
(cd nashorn~jdk8 ; hg fpull)
Or just the nashorn subdirectory with
(cd nashorn~jdk8/nashorn ; hg pull -u)
To learn about Mercurial in detail, please visit http://hgbook.red-bean.com.
- How to build?
To build Nashorn, you need to install JDK 8. You may use the Nashorn
forest build (recommended) or down load from java.net. You will need to
set JAVA_HOME environmental variable to point to your JDK installation
directory.
cd nashorn~jdk8/nashorn/make
ant clean; ant
- How to run?
Use the jjs script (see RELESE_README):
cd nashorn~jdk8/nashorn
sh bin/jjs <your .js file>
Nashorn supports javax.script API. It is possible to drop nashorn.jar in
class path and request for "nashorn" script engine from
javax.script.ScriptEngineManager.
Look for samples under the directory test/src/jdk/nashorn/api/scripting/.
- Documentation
Comprehensive development documentation is found in the Nashorn JavaDoc. You can
build it using:
cd nashorn~jdk8/nashorn/make
ant javadoc
after which you can view the generated documentation at dist/javadoc/index.html.
- Running tests
Nashorn tests are TestNG based. Running tests requires downloading the
TestNG library and placing its jar file into the lib subdirectory:
# download and install TestNG
wget http://testng.org/testng-x.y.z.zip
unzip testng-x.y.z.zip
cp testng-x.y.z/testng-x.y.z.jar test/lib/testng.jar
After that, you can run the tests using:
cd make
ant test
You can also run the ECMA-262 test suite with Nashorn. In order to do
that, you will need to get a copy of it and put it in
test/script/external/test262 directory. A convenient way to do it is:
hg clone http://hg.ecmascript.org/tests/test262/ test/script/external/test262
Alternatively, you can check it out elsewhere and make
test/script/external/test262 a symbolic link to that directory. After
you've done this, you can run the ECMA-262 tests using:
cd nashorn~jdk8/nashorn/make
ant test262
These tests take time, so we have a parallelized runner for them that
takes advantage of all processor cores on the computer:
cd nashorn~jdk8/nashorn/make
ant test262parallel
- How to write your own test?
Nashorn uses it's own simple test framework. Any .js file dropped under
nashorn/test directory is considered as a test. A test file can
optionally have .js.EXPECTED (foo.js.EXPECTED for foo.js) associated
with it. The .EXPECTED file, if exists, should contain the output
expected from compiling and/or running the test file.
The test runner crawls these directories for .js files and looks for
JTReg-style @foo comments to identify tests.
* @test - A test is tagged with @test.
* @test/fail - Tests that are supposed to fail (compiling, see @run/fail
for runtime) are tagged with @test/fail.
* @test/compile-error - Test expects compilation to fail, compares
output.
* @test/warning - Test expects compiler warnings, compares output.
* @test/nocompare - Test expects to compile [and/or run?]
successfully(may be warnings), does not compare output.
* @subtest - denotes necessary file for a main test file; itself is not
a test.
* @run - A test that should be run is also tagged with @run (otherwise
the test runner only compiles the test).
* @run/fail - A test that should compile but fail with a runtime error.
* @run/ignore-std-error - script may produce output on stderr, ignore
this output.
* @argument - pass an argument to script.
* @option \ - pass option to engine, sample.
/**
* @option --dump-ir-graph
* @test
*/

20
nashorn/RELEASE_README Normal file
View File

@ -0,0 +1,20 @@
The Nashorn repo is in the process of being migrated to OpenJDK and as such is
incomplete in several areas.
- The build system is not fully integrated. When complete, Nashorn will be
installed in its proper location in the JRE.
- Once integrated, the correct version of the JDK will be wrapped around
Nashorn. In the meantime, ensure you use JDK8 b68 or later.
- The jjs tool has not been implemented in binary form yet. Use "sh bin/jjs"
(or bin/jjs.bat on windows) in the interm.
- The Dynalink component is not fully integrated into Nashorn as yet, but will
be when details are finalized.
- And, finally Nashorn is still in development. To stay up to date, subscribe
to nashorn-dev@openjdk.java.net at
http://mail.openjdk.java.net/mailman/listinfo/nashorn-dev.

View File

@ -0,0 +1,69 @@
DO NOT TRANSLATE OR LOCALIZE.
-----------------------------
%% This notice is provided with respect to ECMAScript Language
Specification ECMA-262 Edition 5.1 which is included with the Nashorn
technology.
--- begin of LICENSE ---
Copyright notice
Copyright © 2011 Ecma International
Ecma International
Rue du Rhone 114
CH-1204 Geneva
Tel: +41 22 849 6000
Fax: +41 22 849 6001
Web: http://www.ecma-international.org
This document and possible translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it or assist
in its implementation may be prepared, copied, published, and distributed, in
whole or in part, without restriction of any kind, provided that the above
copyright notice and this section are included on all such copies and derivative
works. However, this document itself may not be modified in any way, including
by removing the copyright notice or references to Ecma International, except as
needed for the purpose of developing any document or deliverable produced by
Ecma International (in which case the rules applied to copyrights must be
followed) or as required to translate it into languages other than English. The
limited permissions granted above are perpetual and will not be revoked by Ecma
International or its successors or assigns. This document and the information
contained herein is provided on an "AS IS" basis and ECMA INTERNATIONAL
DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY
WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY OWNERSHIP
RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
PURPOSE." Software License
All Software contained in this document ("Software)" is protected by copyright
and is being made available under the "BSD License", included below. This
Software may be subject to third party rights (rights from parties other than
Ecma International), including patent rights, and no licenses under such third
party rights are granted under this license even if the third party concerned is
a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS
AVAILABLE AT http://www.ecma-international.org/memento/codeofconduct.htm FOR
INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO
IMPLEMENT ECMA INTERNATIONAL STANDARDS*. Redistribution and use in source and
binary forms, with or without modification, are permitted provided that the
following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the authors nor Ecma International may be used to endorse
or promote products derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
--- end of LICENSE ---

266
nashorn/bin/checkintest.sh Normal file
View File

@ -0,0 +1,266 @@
#!/bin/bash
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
#best pass rate at test 262 known
TEST262_PASS_AT_LEAST=435
RUN_TEST="true"
RUN_TEST262="true"
RUN_NODE="true"
KEEP_OUTPUT="true"
CLEAN_AND_BUILD_NASHORN="true"
#the stable node version to sync against
NODE_LAST_STABLE=v0.6.18
#parse args
for arg in $*
do
if [ $arg = "--no-test" ]; then
RUN_TEST="false"
echo "**** WARNING - you have disabled 'ant test', which is a minimum checkin requirement..."
elif [ $arg = "--no-262" ]; then
RUN_TEST262="false"
elif [ $arg = "--no-node" ]; then
RUN_NODE="false"
elif [ $arg = "--no-build" ]; then
CLEAN_AND_BUILD_NASHORN="false"
elif [ $arg = "--no-logs" ]; then
KEEP_OUTPUT="false"
fi
done
function lastpart() {
arr=$(echo $1 | tr "/" "\n")
for x in $arr
do
_last=$x
done
echo $_last
}
function check_installed() {
which $1 >/dev/null
if [ $? -ne 0 ]; then
echo "Error $1 not installed: $?"
exit 2
fi
}
check_installed hg
check_installed git
check_installed mv
check_installed git
PWD=$(pwd);
while [ -z $NASHORN_ROOT ]
do
if [ -e $PWD/.hg ]; then
NASHORN_ROOT=${PWD}
break
fi
PWD=$(dirname ${PWD})
done
echo "Nashorn root detected at ${NASHORN_ROOT}"
COMMON_ROOT=$(dirname $NASHORN_ROOT)
echo "Common root is ${COMMON_ROOT}"
echo "Running checkintest..."
ABSOLUTE_NASHORN_HOME=$COMMON_ROOT/$(lastpart $NASHORN_ROOT)
if [ $CLEAN_AND_BUILD_NASHORN != "false" ]; then
echo "Cleaning and building nashorn at $ABSOLUTE_NASHORN_HOME/nashorn..."
$(cd $ABSOLUTE_NASHORN_HOME/nashorn; ant clean >/dev/null 2>/dev/null)
$(cd $ABSOLUTE_NASHORN_HOME/nashorn; ant jar >/dev/null 2>/dev/null)
echo "Done."
fi
function failure_check() {
while read line
do
LINE=$(echo $line | grep "Tests run")
if [ "${LINE}" != "" ]; then
RESULT=$(echo $line | grep "Failures: 0" | grep "Errors: 0")
if [ "${RESULT}" == "" ]; then
TESTNAME=$2
echo "There were errors in ${TESTNAME} : ${LINE}"
exit 1
fi
fi
done < $1
}
function test() {
TEST_OUTPUT=$ABSOLUTE_NASHORN_HOME/$(mktemp tmp.XXXXX)
echo "Running 'ant test' on nashorn from ${ABSOLUTE_NASHORN_HOME}/nashorn..."
$(cd $ABSOLUTE_NASHORN_HOME/nashorn; ant test >$TEST_OUTPUT)
echo "Done."
failure_check $TEST_OUTPUT
echo "**** SUCCESS: 'ant test' successful"
if [ $KEEP_OUTPUT == "true" ]; then
cp $TEST_OUTPUT ./checkintest.test.log
rm -fr $TEST_OUTPUT
fi
}
if [ $RUN_TEST != "false" ]; then
test;
fi
function test262() {
echo "Running 'ant test262parallel' on nashorn from ${ABSOLUTE_NASHORN_HOME}/nashorn..."
TEST262_OUTPUT=$ABSOLUTE_NASHORN_HOME/$(mktemp tmp.XXXXX)
echo "Looking for ${ABSOLUTE_NASHORN_HOME}/test/test262..."
if [ ! -e $ABSOLUTE_NASHORN_HOME/nashorn/test/test262 ]; then
echo "test262 is missing... looking in $COMMON_ROOT..."
if [ ! -e $COMMON_ROOT/test262 ]; then
echo "... not there either... cloning from repo..."
hg clone http://hg.ecmascript.org/tests/test262 $COMMON_ROOT/test262 >/dev/null 2>/dev/null
echo "Done."
fi
echo "Adding soft link ${COMMON_ROOT}/test262 -> ${ABSOLUTE_NASHORN_HOME}/test/test262..."
ln -s $COMMON_ROOT/test262 $ABSOLUTE_NASHORN_HOME/nashorn/test/test262
echo "Done."
fi
echo "Ensuring test262 is up to date..."
$(cd $ABSOLUTE_NASHORN_HOME/nashorn/test/test262; hg pull -u >/dev/null 2>/dev/null)
echo "Done."
echo "Running test262..."
$(cd $ABSOLUTE_NASHORN_HOME/nashorn; ant test262parallel > $TEST262_OUTPUT)
FAILED=$(cat $TEST262_OUTPUT|grep "Tests run:"| cut -d ' ' -f 15 |tr -cd '"[[:digit:]]')
if [ $FAILED -gt $TEST262_PASS_AT_LEAST ]; then
echo "FAILURE: There are ${FAILED} failures in test262 and can be no more than ${TEST262_PASS_AT_LEAST}"
cp $TEST262_OUTPUT ./checkintest.test262.log
echo "See ./checkintest.test262.log"
echo "Terminating due to error"
exit 1
elif [ $FAILED -lt $TEST262_PASS_AT_LEAST ]; then
echo "There seem to have been fixes to 262. ${FAILED} < ${TEST262_PASS_AT_LEAST}. Please update limit in bin/checkintest.sh"
fi
echo "**** SUCCESS: Test262 passed with no more than ${TEST262_PASS_AT_LEAST} failures."
if [ $KEEP_OUTPUT == "true" ]; then
cp $TEST262_OUTPUT ./checkintest.test262.log
rm -fr $TEST262_OUTPUT
fi
}
if [ $RUN_TEST262 != "false" ]; then
test262;
fi;
function testnode() {
TESTNODEJAR_OUTPUT=$ABSOLUTE_NASHORN_HOME/$(mktemp tmp.XXXXX)
echo "Running node tests..."
#replace node jar properties nashorn with this nashorn
NODEJAR_PROPERTIES=~/nodejar.properties
NODE_HOME=$(cat $NODEJAR_PROPERTIES | grep ^node.home | cut -f2 -d=)
NASHORN_HOME=$(cat $NODEJAR_PROPERTIES | grep ^nashorn.home | cut -f2 -d=)
ABSOLUTE_NODE_HOME=$COMMON_ROOT/$(lastpart $NODE_HOME)
echo "Writing nodejar.properties..."
cat > $NODEJAR_PROPERTIES << EOF
node.home=../node
nashorn.home=../$(lastpart $NASHORN_ROOT)
EOF
echo "Done."
echo "Checking node home ${ABSOLUTE_NODE_HOME}..."
if [ ! -e $ABSOLUTE_NODE_HOME ]; then
echo "Node base dir not found. Cloning node..."
$(cd $COMMON_ROOT; git clone https://github.com/joyent/node.git $(lastpart $NODE_HOME) >/dev/null 2>/dev/null)
echo "Done."
echo "Updating to last stable version ${NODE_LAST_STABLE}..."
$(cd $ABSOLUTE_NODE_HOME; git checkout $NODE_LAST_STABLE >/dev/null 2>/dev/null)
echo "Done."
echo "Running configure..."
$(cd $ABSOLUTE_NODE_HOME; ./configure >/dev/null 2>/dev/null)
echo "Done."
fi
echo "Ensuring node is built..."
#make sure node is built
$(cd $ABSOLUTE_NODE_HOME; make >/dev/null 2>/dev/null)
echo "Done."
NODEJAR_HOME=$COMMON_ROOT/nodejar
if [ ! -e $NODEJAR_HOME ]; then
echo "No node jar home found. cloning from depot..."
$(cd $COMMON_ROOT; hg clone https://hg.kenai.com/hg/nodejs~source nodejar >/dev/null 2>/dev/null)
$(cd $COMMON_ROOT/nodejar; ant >/dev/null)
echo "Done."
echo "Copying node files..."
$(cd $COMMON_ROOT/nodejar; ant copy-node-files >/dev/null 2>/dev/null)
echo "Patching node files..."
$(cd $COMMON_ROOT/nodejar; ant patch-node-files >/dev/null 2>/dev/null)
echo "Done."
fi
echo "Ensuring node.jar is up to date from source depot..."
$(cd $COMMON_ROOT/nodejar; hg pull -u >/dev/null 2>/dev/null)
echo "Done."
echo "Installing nashorn..."
$(cd $COMMON_ROOT/nodejar; ant >/dev/null)
echo "Done."
echo "Running node.jar test..."
$(cd $COMMON_ROOT/nodejar; mvn clean verify >$TESTNODEJAR_OUTPUT)
echo "Done."
failure_check $TESTNODEJAR_OUTPUT
echo "**** SUCCESS: Node test successful."
if [ $KEEP_OUTPUT == "true" ]; then
rm -fr $TESTNODEJAR_OUTPUT
cp $TESTNODEJAR_OUTPUT ./checkintest.nodejar.log
fi
}
if [ $RUN_NODE != "false" ]; then
testnode;
fi;
echo "Finished"

View File

@ -0,0 +1,52 @@
#!/bin/sh
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
#ensure that all tests tagged with @test are also tagged with @run
for f in $(find test/script/basic/*.js); do
grep @test $f >/dev/null
TEST=$?
grep @run $f >/dev/null
RUN=$?
if [ $TEST -eq 0 ] && [ ! $RUN -eq 0 ]; then
echo "repairing ${f}..."
TEMP=$(mktemp /tmp/scratch.XXXXXX)
#IFS='', -raw flag to preserve white space
while IFS='' read -r line; do
echo $line | grep @test >/dev/null
TEST=$?
printf "%s\n" "$line"
if [ $TEST -eq 0 ]; then
printf "%s\n" "$line" | sed s/@test/@run/g
fi
done < $f >$TEMP
cp $TEMP $f
rm -fr $TEMP
fi
done

View File

@ -0,0 +1,30 @@
#!/bin/bash
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
#convert tabs to spaces
find . -name "*.java" -exec sed -i "" 's/ / /g' {} \;
#remove trailing whitespace
find . -name "*.java" -exec sed -i "" 's/[ ]*$//' \{} \;

29
nashorn/bin/jjs Normal file
View File

@ -0,0 +1,29 @@
#!/bin/bash
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
[ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
$JAVA_HOME/bin/java -server -XX:-TieredCompilation -Xms2G -Xmx2G -esa -ea -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:`dirname $0`/../dist -XX:+HeapDumpOnOutOfMemoryError -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.debug=true jdk.nashorn.tools.Shell $*

27
nashorn/bin/jjs.bat Normal file
View File

@ -0,0 +1,27 @@
rem
rem Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
rem DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
rem
rem This code is free software; you can redistribute it and/or modify it
rem under the terms of the GNU General Public License version 2 only, as
rem published by the Free Software Foundation. Oracle designates this
rem particular file as subject to the "Classpath" exception as provided
rem by Oracle in the LICENSE file that accompanied this code.
rem
rem This code is distributed in the hope that it will be useful, but WITHOUT
rem ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
rem FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
rem version 2 for more details (a copy is included in the LICENSE file that
rem accompanied this code).
rem
rem You should have received a copy of the GNU General Public License version
rem 2 along with this work; if not, write to the Free Software Foundation,
rem Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
rem
rem Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
rem or visit www.oracle.com if you need additional information or have any
rem questions.
rem
@echo off
java -Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -Djava.ext.dirs=%~dp0\..\dist -XX:+HeapDumpOnOutOfMemoryError -Dnashorn.debug=true -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false jdk.nashorn.tools.Shell

29
nashorn/bin/jjssecure Normal file
View File

@ -0,0 +1,29 @@
#!/bin/bash
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
[ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
$JAVA_HOME/bin/java -Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:`dirname $0`/../dist -XX:+HeapDumpOnOutOfMemoryError -Dnashorn.debug=true -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=true -Dnashorn.home=`dirname $0`/.. -Djava.security.manager jdk.nashorn.tools.Shell $*

27
nashorn/bin/jjssecure.bat Normal file
View File

@ -0,0 +1,27 @@
rem
rem Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
rem DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
rem
rem This code is free software; you can redistribute it and/or modify it
rem under the terms of the GNU General Public License version 2 only, as
rem published by the Free Software Foundation. Oracle designates this
rem particular file as subject to the "Classpath" exception as provided
rem by Oracle in the LICENSE file that accompanied this code.
rem
rem This code is distributed in the hope that it will be useful, but WITHOUT
rem ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
rem FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
rem version 2 for more details (a copy is included in the LICENSE file that
rem accompanied this code).
rem
rem You should have received a copy of the GNU General Public License version
rem 2 along with this work; if not, write to the Free Software Foundation,
rem Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
rem
rem Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
rem or visit www.oracle.com if you need additional information or have any
rem questions.
rem
@echo off
java -Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -Djava.ext.dirs=%~dp0\..\dist -XX:+HeapDumpOnOutOfMemoryError -Dnashorn.debug=true -Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -Dnashorn.home=%~dp0\.. -Djava.security.manager jdk.nashorn.tools.Shell

29
nashorn/bin/nashorn Normal file
View File

@ -0,0 +1,29 @@
#!/bin/bash
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
[ -z "$JAVA_HOME" ] && echo "Please set JAVA_HOME" && exit 1;
$JAVA_HOME/bin/jrunscript -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=$JAVA_HOME/jre/lib/ext:`dirname $0`/../dist -J-XX:+HeapDumpOnOutOfMemoryError -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -J-Dnashorn.debug=true -l nashorn $*

27
nashorn/bin/nashorn.bat Normal file
View File

@ -0,0 +1,27 @@
rem
rem Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
rem DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
rem
rem This code is free software; you can redistribute it and/or modify it
rem under the terms of the GNU General Public License version 2 only, as
rem published by the Free Software Foundation. Oracle designates this
rem particular file as subject to the "Classpath" exception as provided
rem by Oracle in the LICENSE file that accompanied this code.
rem
rem This code is distributed in the hope that it will be useful, but WITHOUT
rem ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
rem FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
rem version 2 for more details (a copy is included in the LICENSE file that
rem accompanied this code).
rem
rem You should have received a copy of the GNU General Public License version
rem 2 along with this work; if not, write to the Free Software Foundation,
rem Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
rem
rem Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
rem or visit www.oracle.com if you need additional information or have any
rem questions.
rem
@echo off
jrunscript -J-Xms2G -J-Xmx2G -J-XX:-TieredCompilation -J-server -J-esa -J-ea -J-Djava.ext.dirs=%~dp0\..\dist -J-XX:+HeapDumpOnOutOfMemoryError -J-Dnashorn.debug=true -J-Djava.lang.invoke.MethodHandle.DEBUG_NAMES=false -l nashorn

View File

@ -0,0 +1,24 @@
#!/bin/bash
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
hg status|grep ^\?|awk '{print $2}'|xargs rm

View File

@ -0,0 +1,59 @@
rem
rem Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
rem DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
rem
rem This code is free software; you can redistribute it and/or modify it
rem under the terms of the GNU General Public License version 2 only, as
rem published by the Free Software Foundation.
rem
rem This code is distributed in the hope that it will be useful, but WITHOUT
rem ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
rem FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
rem version 2 for more details (a copy is included in the LICENSE file that
rem accompanied this code).
rem
rem You should have received a copy of the GNU General Public License version
rem 2 along with this work; if not, write to the Free Software Foundation,
rem Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
rem
rem Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
rem or visit www.oracle.com if you need additional information or have any
rem questions.
rem
@echo off
if "%JAVA_HOME%" neq "" (
call :run "%JAVA_HOME%/bin/java"
) else (
call :run java
)
goto :EOF
:run
setlocal
set NASHORN_JAR=dist/nashorn.jar
set JVM_FLAGS=-Xms2G -Xmx2G -XX:-TieredCompilation -server -esa -ea -jar %NASHORN_JAR%
set JVM_FLAGS7=-Xbootclasspath/p:%NASHORN_JAR% %JVM_FLAGS%
set OCTANE_ARGS=--verbose --iterations 7
%1 -fullversion 2>&1 | findstr /L /C:"version ""1.7"
if %errorlevel% equ 0 (
set CMD=%1 %JVM_FLAGS7%
) else (
%1 -fullversion
set CMD=%1 %JVM_FLAGS%
)
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/box2d.js %OCTANE_ARGS%
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/code-load.js %OCTANE_ARGS%
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/crypto.js %OCTANE_ARGS%
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/deltablue.js %OCTANE_ARGS%
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/gbemu.js %OCTANE_ARGS%
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/navier-stokes.js %OCTANE_ARGS%
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/pdfjs.js %OCTANE_ARGS%
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/raytrace.js %OCTANE_ARGS%
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/regexp.js %OCTANE_ARGS%
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/richards.js %OCTANE_ARGS%
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/splay.js %OCTANE_ARGS%
endlocal
goto :EOF

View File

@ -0,0 +1,58 @@
#!/bin/bash
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
ITERS=$1
if [ -z $ITERS ]; then
ITERS=7
fi
NASHORN_JAR=dist/nashorn.jar
JVM_FLAGS="-XX:+UnlockDiagnosticVMOptions -Dnashorn.unstable.relink.threshold=8 -Xms2G -Xmx2G -XX:-TieredCompilation -server -jar ${NASHORN_JAR}"
JVM_FLAGS7="-Xbootclasspath/p:${NASHORN_JAR} ${JVM_FLAGS}"
OCTANE_ARGS="--verbose --iterations ${ITERS}"
BENCHMARKS=( "box2d.js" "code-load.js" "crypto.js" "deltablue.js" "earley-boyer.js" "gbemu.js" "navier-stokes.js" "raytrace.js" "regexp.js" "richards.js" "splay.js" )
# TODO mandreel.js has metaspace issues
if [ ! -z $JAVA7_HOME ]; then
echo "running ${ITERS} iterations with java7 using JAVA_HOME=${JAVA7_HOME}..."
for BENCHMARK in "${BENCHMARKS[@]}"
do
CMD="${JAVA8_HOME}/bin/java ${JVM_FLAGS} test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/${BENCHMARK} ${OCTANE_ARGS}"
$CMD
done
else
echo "no JAVA7_HOME set. skipping java7"
fi
if [ ! -z $JAVA8_HOME ]; then
echo "running ${ITERS} iterations with java8 using JAVA_HOME=${JAVA8_HOME}..."
for BENCHMARK in "${BENCHMARKS[@]}"
do
CMD="${JAVA8_HOME}/bin/java ${JVM_FLAGS} test/script/basic/run-octane.js -- test/script/external/octane/benchmarks/${BENCHMARK} ${OCTANE_ARGS}"
$CMD
done
else
echo "no JAVA8_HOME set"
fi
echo "Done"

View File

@ -0,0 +1,34 @@
Nasgen is a tool for processing Java classes that implement native
JavaScript objects. It does so by looking for the
com.oracle.nashorn.objects.annotations.ScriptClass annotation and other
annotations in that package.
For each class "C", nasgen instruments the original class and generates
two additional classes: a "C$Prototype" class for the JavaScript
prototype object, and a "C$Constructor" class for the JavaScript
constructor function.
Each class instrumented or generated by nasgen contains a private static
"$nasgenmap$" field of type com.oracle.nashorn.runtime.PropertyMap and
static initializer block to initialize the field to the object's
JavaScript properties.
Members annotated with @Function, @Property, @Getter, and @Setter are
mapped to the $Constructor, $Prototype, or main class, depending on the
value of the annotation's 'where' field. By default, @Property, @Getter,
and @Setter belong to the main class while @Function methods without
explicit 'where' field belong to the $Prototype class. The @Constructor
annotation marks a method to be invoked as JavaScript constructor.
Nasgen enforces all @Function/@Getter/@Setter/@Constructor annotated
methods to be declared as static. Static final @Property fields remain
in the main class while other @Property fields are moved to respective
classes depending on the annotation's 'where' value. For functions
mapped to the $Prototype or $Constructor class, nasgen also generates
getters and setters prefixed by G$ and S$, respectively.
Nasgen-generated classes are hidden from normal ClassLoaders by giving
them a ".clazz" file name extension instead of the standard ".class"
extension. This allows script classes to be loaded independently by each
Nashorn context through the com.oracle.nashorn.runtime.StructureLoader
class loader.

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
or visit www.oracle.com if you need additional information or have any
questions.
-->
<project name="nasgen" default="all" basedir=".">
<target name="init">
<loadproperties srcFile="project.properties"/>
</target>
<target name="prepare" depends="init">
<mkdir dir="${build.classes.dir}"/>
<mkdir dir="${dist.dir}"/>
<mkdir dir="${dist.dir}/lib"/>
</target>
<target name="clean" depends="init">
<delete dir="${build.dir}"/>
<delete dir="${dist.dir}"/>
</target>
<target name="compile" depends="prepare" description="Compiles the nasgen sources">
<javac srcdir="${src.dir}"
destdir="${build.classes.dir}"
classpath="${javac.classpath}"
debug="${javac.debug}"
includeantruntime="false">
<compilerarg value="-Xlint:unchecked"/>
<compilerarg value="-Xlint:deprecation"/>
<compilerarg value="-XDignore.symbol.file"/>
</javac>
</target>
<target name="jar" depends="compile" description="Creates nasgen.jar">
<jar jarfile="${dist.jar}" basedir="${build.classes.dir}" manifest="${meta.inf.dir}/MANIFEST.MF"/>
</target>
<target name="dist" depends="jar"/>
<target name="all" depends="dist"
description="Builds sources and generates nasgen.jar"/>
</project>

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
or visit www.oracle.com if you need additional information or have any
questions.
-->
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="false">
<output url="file://$MODULE_DIR$/build/classes" />
<output-test url="file://$MODULE_DIR$/build/test/classes" />
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/dist" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="asm" />
</component>
</module>

View File

@ -0,0 +1,52 @@
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
application.title=nasgen
# source and target levels
build.compiler=modern
javac.source=1.7
javac.target=1.7
build.classes.dir=${build.dir}/classes
# This directory is removed when the project is cleaned:
build.dir=build
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=${dist.dir}/nasgen.jar
dist.javadoc.dir=${dist.dir}/javadoc
nashorn.dir=../../
javac.debug=true
javac.classpath=\
${nashorn.dir}/build/classes
meta.inf.dir=${src.dir}/META-INF
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
run.jvmargs=
src.dir=src

View File

@ -0,0 +1,4 @@
Manifest-Version: 1.0
Class-Path: lib/ant-1.7.1.jar
Main-Class: jdk.nashorn.internal.tools.nasgen.Main

View File

@ -0,0 +1,334 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.tools.nasgen;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.CLINIT;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.GETTER_PREFIX;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_NEWPROPERTY;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_NEWPROPERTY_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_NEWMAP;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_NEWMAP_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SETTER_PREFIX;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_OBJECT;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.FieldVisitor;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Type;
import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
/**
* Base class for class generator classes.
*
*/
public class ClassGenerator {
/** ASM class writer used to output bytecode for this class */
protected final ClassWriter cw;
/**
* Constructor
*/
protected ClassGenerator() {
this.cw = makeClassWriter();
}
MethodGenerator makeStaticInitializer() {
return makeStaticInitializer(cw);
}
MethodGenerator makeConstructor() {
return makeConstructor(cw);
}
MethodGenerator makeMethod(final int access, final String name, final String desc) {
return makeMethod(cw, access, name, desc);
}
void addMapField() {
addMapField(cw);
}
void addField(final String name, final String desc) {
addField(cw, name, desc);
}
void addFunctionField(final String name) {
addFunctionField(cw, name);
}
void addGetter(final String owner, final MemberInfo memInfo) {
addGetter(cw, owner, memInfo);
}
void addSetter(final String owner, final MemberInfo memInfo) {
addSetter(cw, owner, memInfo);
}
void emitGetClassName(final String name) {
final MethodGenerator mi = makeMethod(ACC_PUBLIC, GET_CLASS_NAME, GET_CLASS_NAME_DESC);
mi.loadLiteral(name);
mi.returnValue();
mi.computeMaxs();
mi.visitEnd();
}
static ClassWriter makeClassWriter() {
return new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
@Override
protected String getCommonSuperClass(final String type1, final String type2) {
try {
return super.getCommonSuperClass(type1, type2);
} catch (final RuntimeException | LinkageError e) {
return StringConstants.OBJECT_TYPE;
}
}
};
}
static MethodGenerator makeStaticInitializer(final ClassVisitor cv) {
return makeStaticInitializer(cv, CLINIT);
}
static MethodGenerator makeStaticInitializer(final ClassVisitor cv, final String name) {
final int access = ACC_PUBLIC | ACC_STATIC;
final String desc = DEFAULT_INIT_DESC;
final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
return new MethodGenerator(mv, access, name, desc);
}
static MethodGenerator makeConstructor(final ClassVisitor cv) {
final int access = ACC_PUBLIC;
final String name = INIT;
final String desc = DEFAULT_INIT_DESC;
final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
return new MethodGenerator(mv, access, name, desc);
}
static MethodGenerator makeMethod(final ClassVisitor cv, final int access, final String name, final String desc) {
final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
return new MethodGenerator(mv, access, name, desc);
}
static void emitStaticInitPrefix(final MethodGenerator mi, final String className) {
mi.visitCode();
mi.pushNull();
mi.putStatic(className, MAP_FIELD_NAME, MAP_DESC);
mi.loadClass(className);
mi.invokeStatic(MAP_TYPE, MAP_NEWMAP, MAP_NEWMAP_DESC);
mi.storeLocal(0);
}
static void emitStaticInitSuffix(final MethodGenerator mi, final String className) {
mi.loadLocal(0);
mi.putStatic(className, MAP_FIELD_NAME, MAP_DESC);
mi.returnVoid();
mi.computeMaxs();
mi.visitEnd();
}
@SuppressWarnings("fallthrough")
private static Type memInfoType(final MemberInfo memInfo) {
switch (memInfo.getJavaDesc().charAt(0)) {
case 'I': return Type.INT_TYPE;
case 'J': return Type.LONG_TYPE;
case 'D': return Type.DOUBLE_TYPE;
default: assert false : memInfo.getJavaDesc();
case 'L': return TYPE_OBJECT;
}
}
private static String getterDesc(final MemberInfo memInfo) {
return Type.getMethodDescriptor(memInfoType(memInfo));
}
private static String setterDesc(final MemberInfo memInfo) {
return Type.getMethodDescriptor(Type.VOID_TYPE, memInfoType(memInfo));
}
static void addGetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) {
final int access = ACC_PUBLIC;
final String name = GETTER_PREFIX + memInfo.getJavaName();
final String desc = getterDesc(memInfo);
final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
final MethodGenerator mi = new MethodGenerator(mv, access, name, desc);
mi.visitCode();
if (memInfo.isStatic() && memInfo.getKind() == Kind.PROPERTY) {
mi.getStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
} else {
mi.loadLocal(0);
mi.getField(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
}
mi.returnValue();
mi.computeMaxs();
mi.visitEnd();
}
static void addSetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) {
final int access = ACC_PUBLIC;
final String name = SETTER_PREFIX + memInfo.getJavaName();
final String desc = setterDesc(memInfo);
final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
final MethodGenerator mi = new MethodGenerator(mv, access, name, desc);
mi.visitCode();
if (memInfo.isStatic() && memInfo.getKind() == Kind.PROPERTY) {
mi.loadLocal(1);
mi.putStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
} else {
mi.loadLocal(0);
mi.loadLocal(1);
mi.putField(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
}
mi.returnVoid();
mi.computeMaxs();
mi.visitEnd();
}
static void addMapField(final ClassVisitor cv) {
// add a MAP static field
final FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC,
MAP_FIELD_NAME, MAP_DESC, null, null);
if (fv != null) {
fv.visitEnd();
}
}
static void addField(final ClassVisitor cv, final String name, final String desc) {
final FieldVisitor fv = cv.visitField(ACC_PRIVATE, name, desc, null, null);
if (fv != null) {
fv.visitEnd();
}
}
static void addFunctionField(final ClassVisitor cv, final String name) {
addField(cv, name, OBJECT_DESC);
}
static void newFunction(final MethodGenerator mi, final String className, final MemberInfo memInfo, final List<MemberInfo> specs) {
final boolean arityFound = (memInfo.getArity() != MemberInfo.DEFAULT_ARITY);
mi.loadLiteral(memInfo.getName());
mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className, memInfo.getJavaName(), memInfo.getJavaDesc()));
assert specs != null;
if (!specs.isEmpty()) {
mi.memberInfoArray(className, specs);
mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC);
} else {
mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC);
}
if (arityFound) {
mi.dup();
mi.push(memInfo.getArity());
mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY, SCRIPTFUNCTION_SETARITY_DESC);
}
}
static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo memInfo) {
final String propertyName = memInfo.getName();
mi.loadLocal(0);
mi.loadLiteral(propertyName);
// setup flags
mi.push(memInfo.getAttributes());
// setup getter method handle
String javaName = GETTER_PREFIX + memInfo.getJavaName();
mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, getterDesc(memInfo)));
// setup setter method handle
if (memInfo.isFinal()) {
mi.pushNull();
} else {
javaName = SETTER_PREFIX + memInfo.getJavaName();
mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, setterDesc(memInfo)));
}
mi.invokeStatic(LOOKUP_TYPE, LOOKUP_NEWPROPERTY, LOOKUP_NEWPROPERTY_DESC);
mi.storeLocal(0);
}
static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo getter, final MemberInfo setter) {
final String propertyName = getter.getName();
mi.loadLocal(0);
mi.loadLiteral(propertyName);
// setup flags
mi.push(getter.getAttributes());
// setup getter method handle
mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className,
getter.getJavaName(), getter.getJavaDesc()));
// setup setter method handle
if (setter == null) {
mi.pushNull();
} else {
mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className,
setter.getJavaName(), setter.getJavaDesc()));
}
mi.invokeStatic(LOOKUP_TYPE, LOOKUP_NEWPROPERTY, LOOKUP_NEWPROPERTY_DESC);
mi.storeLocal(0);
}
static ScriptClassInfo getScriptClassInfo(final String fileName) throws IOException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName))) {
return getScriptClassInfo(new ClassReader(bis));
}
}
static ScriptClassInfo getScriptClassInfo(final byte[] classBuf) {
return getScriptClassInfo(new ClassReader(classBuf));
}
private static ScriptClassInfo getScriptClassInfo(final ClassReader reader) {
final ScriptClassInfoCollector scic = new ScriptClassInfoCollector();
reader.accept(scic, 0);
return scic.getScriptClassInfo();
}
}

View File

@ -0,0 +1,276 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.tools.nasgen;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.CONSTRUCTOR_SUFFIX;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC3;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_INIT_DESC4;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_TYPE;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import jdk.internal.org.objectweb.asm.Handle;
/**
* This class generates constructor class for a @ClassInfo annotated class.
*
*/
public class ConstructorGenerator extends ClassGenerator {
private final ScriptClassInfo scriptClassInfo;
private final String className;
private final MemberInfo constructor;
private final int memberCount;
private final List<MemberInfo> specs;
ConstructorGenerator(final ScriptClassInfo sci) {
this.scriptClassInfo = sci;
this.className = scriptClassInfo.getConstructorClassName();
this.constructor = scriptClassInfo.getConstructor();
this.memberCount = scriptClassInfo.getConstructorMemberCount();
this.specs = scriptClassInfo.getSpecializedConstructors();
}
byte[] getClassBytes() {
// new class extensing from ScriptObject
final String superClass = (constructor != null)? SCRIPTFUNCTIONIMPL_TYPE : SCRIPTOBJECT_TYPE;
cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, superClass, null);
if (memberCount > 0) {
// add fields
emitFields();
// add <clinit>
emitStaticInitializer();
}
// add <init>
emitConstructor();
if (constructor == null) {
emitGetClassName(scriptClassInfo.getName());
}
cw.visitEnd();
return cw.toByteArray();
}
// --Internals only below this point
private void emitFields() {
// Introduce "Function" type instance fields for each
// constructor @Function in script class and introduce instance
// fields for each constructor @Property in the script class.
for (MemberInfo memInfo : scriptClassInfo.getMembers()) {
if (memInfo.isConstructorFunction()) {
addFunctionField(memInfo.getJavaName());
memInfo = (MemberInfo)memInfo.clone();
memInfo.setJavaDesc(OBJECT_DESC);
memInfo.setJavaAccess(ACC_PUBLIC);
addGetter(className, memInfo);
addSetter(className, memInfo);
} else if (memInfo.isConstructorProperty()) {
if (memInfo.isStaticFinal()) {
addGetter(scriptClassInfo.getJavaName(), memInfo);
} else {
addField(memInfo.getJavaName(), memInfo.getJavaDesc());
memInfo = (MemberInfo)memInfo.clone();
memInfo.setJavaAccess(ACC_PUBLIC);
addGetter(className, memInfo);
addSetter(className, memInfo);
}
}
}
addMapField();
}
private void emitStaticInitializer() {
final MethodGenerator mi = makeStaticInitializer();
emitStaticInitPrefix(mi, className);
for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
if (memInfo.isConstructorFunction() || memInfo.isConstructorProperty()) {
linkerAddGetterSetter(mi, className, memInfo);
} else if (memInfo.isConstructorGetter()) {
final MemberInfo setter = scriptClassInfo.findSetter(memInfo);
linkerAddGetterSetter(mi, className, memInfo, setter);
}
}
emitStaticInitSuffix(mi, className);
}
private void emitConstructor() {
final MethodGenerator mi = makeConstructor();
mi.visitCode();
callSuper(mi);
if (memberCount > 0) {
// initialize Function type fields
initFunctionFields(mi);
// initialize data fields
initDataFields(mi);
}
if (constructor != null) {
final int arity = constructor.getArity();
if (arity != MemberInfo.DEFAULT_ARITY) {
mi.loadThis();
mi.push(arity);
mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY,
SCRIPTFUNCTION_SETARITY_DESC);
}
}
mi.returnVoid();
mi.computeMaxs();
mi.visitEnd();
}
private void loadMap(final MethodGenerator mi) {
if (memberCount > 0) {
mi.getStatic(className, MAP_FIELD_NAME, MAP_DESC);
// make sure we use duplicated PropertyMap so that original map
// stays intact and so can be used for many globals in same context
mi.invokeVirtual(MAP_TYPE, MAP_DUPLICATE, MAP_DUPLICATE_DESC);
}
}
private void callSuper(final MethodGenerator mi) {
String superClass, superDesc;
mi.loadThis();
if (constructor == null) {
// call ScriptObject.<init>
superClass = SCRIPTOBJECT_TYPE;
superDesc = (memberCount > 0) ? SCRIPTOBJECT_INIT_DESC : DEFAULT_INIT_DESC;
loadMap(mi);
} else {
// call Function.<init>
superClass = SCRIPTFUNCTIONIMPL_TYPE;
superDesc = (memberCount > 0) ? SCRIPTFUNCTIONIMPL_INIT_DESC4 : SCRIPTFUNCTIONIMPL_INIT_DESC3;
mi.loadLiteral(constructor.getName());
mi.visitLdcInsn(new Handle(H_INVOKESTATIC, scriptClassInfo.getJavaName(), constructor.getJavaName(), constructor.getJavaDesc()));
loadMap(mi);
mi.memberInfoArray(scriptClassInfo.getJavaName(), specs); //pushes null if specs empty
}
mi.invokeSpecial(superClass, INIT, superDesc);
}
private void initFunctionFields(final MethodGenerator mi) {
for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
if (!memInfo.isConstructorFunction()) {
continue;
}
mi.loadThis();
newFunction(mi, scriptClassInfo.getJavaName(), memInfo, scriptClassInfo.findSpecializations(memInfo.getJavaName()));
mi.putField(className, memInfo.getJavaName(), OBJECT_DESC);
}
}
private void initDataFields(final MethodGenerator mi) {
for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
if (!memInfo.isConstructorProperty() || memInfo.isFinal()) {
continue;
}
final Object value = memInfo.getValue();
if (value != null) {
mi.loadThis();
mi.loadLiteral(value);
mi.putField(className, memInfo.getJavaName(), memInfo.getJavaDesc());
} else if (!memInfo.getInitClass().isEmpty()) {
final String clazz = memInfo.getInitClass();
mi.loadThis();
mi.newObject(clazz);
mi.dup();
mi.invokeSpecial(clazz, INIT, DEFAULT_INIT_DESC);
mi.putField(className, memInfo.getJavaName(), memInfo.getJavaDesc());
}
}
if (constructor != null) {
mi.loadThis();
final String protoName = scriptClassInfo.getPrototypeClassName();
mi.newObject(protoName);
mi.dup();
mi.invokeSpecial(protoName, INIT, DEFAULT_INIT_DESC);
mi.dup();
mi.loadThis();
mi.invokeStatic(PROTOTYPEOBJECT_TYPE, PROTOTYPEOBJECT_SETCONSTRUCTOR,
PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC);
mi.putField(SCRIPTFUNCTION_TYPE, PROTOTYPE, OBJECT_DESC);
}
}
/**
* Entry point for ConstructorGenerator run separately as an application. Will display
* usage. Takes one argument, a class name.
* @param args args vector
* @throws IOException if class can't be read
*/
public static void main(final String[] args) throws IOException {
if (args.length != 1) {
System.err.println("Usage: " + ConstructorGenerator.class.getName() + " <class>");
System.exit(1);
}
final String className = args[0].replace('.', '/');
final ScriptClassInfo sci = getScriptClassInfo(className + ".class");
if (sci == null) {
System.err.println("No @ScriptClass in " + className);
System.exit(2);
throw new IOException(); // get rid of warning for sci.verify() below - may be null
}
try {
sci.verify();
} catch (final Exception e) {
System.err.println(e.getMessage());
System.exit(3);
}
final ConstructorGenerator gen = new ConstructorGenerator(sci);
try (FileOutputStream fos = new FileOutputStream(className + CONSTRUCTOR_SUFFIX + ".class")) {
fos.write(gen.getClassBytes());
}
}
}

View File

@ -0,0 +1,181 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.tools.nasgen;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
/**
* Main class for the "nasgen" tool.
*
*/
public class Main {
private static final boolean DEBUG = Boolean.getBoolean("nasgen.debug");
private interface ErrorReporter {
public void error(String msg);
}
/**
* Public entry point for Nasgen if invoked from command line. Nasgen takes three arguments
* in order: input directory, package list, output directory
*
* @param args argument vector
*/
public static void main(final String[] args) {
final ErrorReporter reporter = new ErrorReporter() {
@Override
public void error(final String msg) {
Main.error(msg, 1);
}
};
if (args.length == 3) {
processAll(args[0], args[1], args[2], reporter);
} else {
error("Usage: nasgen <input-dir> <package-list> <output-dir>", 1);
}
}
private static void processAll(final String in, final String pkgList, final String out, final ErrorReporter reporter) {
final File inDir = new File(in);
if (!inDir.exists() || !inDir.isDirectory()) {
reporter.error(in + " does not exist or not a directory");
return;
}
final File outDir = new File(out);
if (!outDir.exists() || !outDir.isDirectory()) {
reporter.error(out + " does not exist or not a directory");
return;
}
final String[] packages = pkgList.split(":");
for (String pkg : packages) {
pkg = pkg.replace('.', File.separatorChar);
final File dir = new File(inDir, pkg);
final File[] classes = dir.listFiles();
for (final File clazz : classes) {
if (clazz.isFile() && clazz.getName().endsWith(".class")) {
if (! process(clazz, new File(outDir, pkg), reporter)) {
return;
}
}
}
}
}
private static boolean process(final File inFile, final File outDir, final ErrorReporter reporter) {
try {
byte[] buf = new byte[(int)inFile.length()];
try (FileInputStream fin = new FileInputStream(inFile)) {
fin.read(buf);
}
final ScriptClassInfo sci = ClassGenerator.getScriptClassInfo(buf);
if (sci != null) {
try {
sci.verify();
} catch (final Exception e) {
reporter.error(e.getMessage());
return false;
}
// create necessary output package dir
outDir.mkdirs();
// instrument @ScriptClass
final ClassWriter writer = ClassGenerator.makeClassWriter();
final ClassReader reader = new ClassReader(buf);
final ScriptClassInstrumentor inst = new ScriptClassInstrumentor(writer, sci);
reader.accept(inst, 0);
//noinspection UnusedAssignment
// write instrumented class
try (FileOutputStream fos = new FileOutputStream(new File(outDir, inFile.getName()))) {
buf = writer.toByteArray();
if (DEBUG) {
verify(buf);
}
fos.write(buf);
}
// simple class name without package prefix
String simpleName = inFile.getName();
simpleName = simpleName.substring(0, simpleName.indexOf(".class"));
if (sci.getPrototypeMemberCount() > 0) {
// generate prototype class
final PrototypeGenerator protGen = new PrototypeGenerator(sci);
buf = protGen.getClassBytes();
if (DEBUG) {
verify(buf);
}
try (FileOutputStream fos = new FileOutputStream(new File(outDir, simpleName + StringConstants.PROTOTYPE_SUFFIX + ".class"))) {
fos.write(buf);
}
}
if (sci.getConstructorMemberCount() > 0 || sci.getConstructor() != null) {
// generate constructor class
final ConstructorGenerator consGen = new ConstructorGenerator(sci);
buf = consGen.getClassBytes();
if (DEBUG) {
verify(buf);
}
try (FileOutputStream fos = new FileOutputStream(new File(outDir, simpleName + StringConstants.CONSTRUCTOR_SUFFIX + ".class"))) {
fos.write(buf);
}
}
}
return true;
} catch (final IOException | RuntimeException e) {
if (DEBUG) {
e.printStackTrace(System.err);
}
reporter.error(e.getMessage());
return false;
}
}
private static void verify(final byte[] buf) {
final ClassReader cr = new ClassReader(buf);
CheckClassAdapter.verify(cr, false, new PrintWriter(System.err));
}
private static void error(final String msg, final int exitCode) {
System.err.println(msg);
System.exit(exitCode);
}
}

View File

@ -0,0 +1,377 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.tools.nasgen;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_ARRAY_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.nashorn.internal.objects.annotations.Where;
/**
* Details about a Java method or field annotated with any of the field/method
* annotations from the jdk.nashorn.internal.objects.annotations package.
*/
public final class MemberInfo implements Cloneable {
/**
* The different kinds of available class annotations
*/
public static enum Kind {
/** This is a script class */
SCRIPT_CLASS,
/** This is a constructor */
CONSTRUCTOR,
/** This is a function */
FUNCTION,
/** This is a getter */
GETTER,
/** This is a setter */
SETTER,
/** This is a property */
PROPERTY,
/** This is a specialized version of a function */
SPECIALIZED_FUNCTION,
/** This is a specialized version of a constructor */
SPECIALIZED_CONSTRUCTOR
}
// keep in sync with jdk.nashorn.internal.objects.annotations.Attribute
static final int DEFAULT_ATTRIBUTES = 0x0;
static final int DEFAULT_ARITY = -2;
// the kind of the script annotation - one of the above constants
private MemberInfo.Kind kind;
// script property name
private String name;
// script property attributes
private int attributes;
// name of the java member
private String javaName;
// type descriptor of the java member
private String javaDesc;
// access bits of the Java field or method
private int javaAccess;
// initial value for static @Property fields
private Object value;
// class whose object is created to fill property value
private String initClass;
// arity of the Function or Constructor
private int arity;
private Where where;
/**
* @return the kind
*/
public Kind getKind() {
return kind;
}
/**
* @param kind the kind to set
*/
public void setKind(final Kind kind) {
this.kind = kind;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(final String name) {
this.name = name;
}
/**
* @return the attributes
*/
public int getAttributes() {
return attributes;
}
/**
* @param attributes the attributes to set
*/
public void setAttributes(final int attributes) {
this.attributes = attributes;
}
/**
* @return the javaName
*/
public String getJavaName() {
return javaName;
}
/**
* @param javaName the javaName to set
*/
public void setJavaName(final String javaName) {
this.javaName = javaName;
}
/**
* @return the javaDesc
*/
public String getJavaDesc() {
return javaDesc;
}
void setJavaDesc(final String javaDesc) {
this.javaDesc = javaDesc;
}
int getJavaAccess() {
return javaAccess;
}
void setJavaAccess(final int access) {
this.javaAccess = access;
}
Object getValue() {
return value;
}
void setValue(final Object value) {
this.value = value;
}
Where getWhere() {
return where;
}
void setWhere(final Where where) {
this.where = where;
}
boolean isFinal() {
return (javaAccess & Opcodes.ACC_FINAL) != 0;
}
boolean isStatic() {
return (javaAccess & Opcodes.ACC_STATIC) != 0;
}
boolean isStaticFinal() {
return isStatic() && isFinal();
}
boolean isInstanceGetter() {
return kind == Kind.GETTER && where == Where.INSTANCE;
}
/**
* Check whether this MemberInfo is a getter that resides in the instance
* @return true if instance setter
*/
boolean isInstanceSetter() {
return kind == Kind.SETTER && where == Where.INSTANCE;
}
boolean isInstanceProperty() {
return kind == Kind.PROPERTY && where == Where.INSTANCE;
}
boolean isInstanceFunction() {
return kind == Kind.FUNCTION && where == Where.INSTANCE;
}
boolean isPrototypeGetter() {
return kind == Kind.GETTER && where == Where.PROTOTYPE;
}
boolean isPrototypeSetter() {
return kind == Kind.SETTER && where == Where.PROTOTYPE;
}
boolean isPrototypeProperty() {
return kind == Kind.PROPERTY && where == Where.PROTOTYPE;
}
boolean isPrototypeFunction() {
return kind == Kind.FUNCTION && where == Where.PROTOTYPE;
}
boolean isConstructorGetter() {
return kind == Kind.GETTER && where == Where.CONSTRUCTOR;
}
boolean isConstructorSetter() {
return kind == Kind.SETTER && where == Where.CONSTRUCTOR;
}
boolean isConstructorProperty() {
return kind == Kind.PROPERTY && where == Where.CONSTRUCTOR;
}
boolean isConstructorFunction() {
return kind == Kind.FUNCTION && where == Where.CONSTRUCTOR;
}
boolean isConstructor() {
return kind == Kind.CONSTRUCTOR;
}
void verify() {
if (kind == Kind.CONSTRUCTOR) {
final Type returnType = Type.getReturnType(javaDesc);
if (! returnType.toString().equals(OBJECT_DESC)) {
error("return value should be of Object type, found" + returnType);
}
final Type[] argTypes = Type.getArgumentTypes(javaDesc);
if (argTypes.length < 2) {
error("constructor methods should have at least 2 args");
}
if (! argTypes[0].equals(Type.BOOLEAN_TYPE)) {
error("first argument should be of boolean type, found" + argTypes[0]);
}
if (! argTypes[1].toString().equals(OBJECT_DESC)) {
error("second argument should be of Object type, found" + argTypes[0]);
}
if (argTypes.length > 2) {
for (int i = 2; i < argTypes.length - 1; i++) {
if (! argTypes[i].toString().equals(OBJECT_DESC)) {
error(i + "'th argument should be of Object type, found " + argTypes[i]);
}
}
final String lastArgType = argTypes[argTypes.length - 1].toString();
final boolean isVarArg = lastArgType.equals(OBJECT_ARRAY_DESC);
if (!lastArgType.equals(OBJECT_DESC) && !isVarArg) {
error("last argument is neither Object nor Object[] type: " + lastArgType);
}
if (isVarArg && argTypes.length > 3) {
error("vararg constructor has more than 3 arguments");
}
}
} else if (kind == Kind.FUNCTION) {
final Type returnType = Type.getReturnType(javaDesc);
if (! returnType.toString().equals(OBJECT_DESC)) {
error("return value should be of Object type, found" + returnType);
}
final Type[] argTypes = Type.getArgumentTypes(javaDesc);
if (argTypes.length < 1) {
error("function methods should have at least 1 arg");
}
if (! argTypes[0].toString().equals(OBJECT_DESC)) {
error("first argument should be of Object type, found" + argTypes[0]);
}
if (argTypes.length > 1) {
for (int i = 1; i < argTypes.length - 1; i++) {
if (! argTypes[i].toString().equals(OBJECT_DESC)) {
error(i + "'th argument should be of Object type, found " + argTypes[i]);
}
}
final String lastArgType = argTypes[argTypes.length - 1].toString();
final boolean isVarArg = lastArgType.equals(OBJECT_ARRAY_DESC);
if (!lastArgType.equals(OBJECT_DESC) && !isVarArg) {
error("last argument is neither Object nor Object[] type: " + lastArgType);
}
if (isVarArg && argTypes.length > 2) {
error("vararg function has more than 2 arguments");
}
}
} else if (kind == Kind.GETTER) {
final Type[] argTypes = Type.getArgumentTypes(javaDesc);
if (argTypes.length != 1) {
error("getter methods should have one argument");
}
if (! argTypes[0].toString().equals(OBJECT_DESC)) {
error("first argument of getter should be of Object type, found: " + argTypes[0]);
}
if (Type.getReturnType(javaDesc).equals(Type.VOID_TYPE)) {
error("return type of getter should not be void");
}
} else if (kind == Kind.SETTER) {
final Type[] argTypes = Type.getArgumentTypes(javaDesc);
if (argTypes.length != 2) {
error("setter methods should have two arguments");
}
if (! argTypes[0].toString().equals(OBJECT_DESC)) {
error("first argument of setter should be of Object type, found: " + argTypes[0]);
}
if (!Type.getReturnType(javaDesc).toString().equals("V")) {
error("return type of setter should be void, found: " + Type.getReturnType(javaDesc));
}
}
}
private void error(final String msg) {
throw new RuntimeException(javaName + javaDesc + " : " + msg);
}
/**
* @return the initClass
*/
String getInitClass() {
return initClass;
}
/**
* @param initClass the initClass to set
*/
void setInitClass(final String initClass) {
this.initClass = initClass;
}
@Override
protected Object clone() {
try {
return super.clone();
} catch (final CloneNotSupportedException e) {
assert false : "clone not supported " + e;
return null;
}
}
/**
* @return the arity
*/
int getArity() {
return arity;
}
/**
* @param arity the arity to set
*/
void setArity(final int arity) {
this.arity = arity;
}
}

View File

@ -0,0 +1,426 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.tools.nasgen;
import static jdk.internal.org.objectweb.asm.Opcodes.AALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.AASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.ANEWARRAY;
import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.ASM4;
import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.BALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.BASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.CALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.CASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
import static jdk.internal.org.objectweb.asm.Opcodes.DALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.DASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.DCONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.DRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP2;
import static jdk.internal.org.objectweb.asm.Opcodes.FALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.FASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.FCONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.FRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.GETFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.IALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.IASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.LALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.LASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.NEW;
import static jdk.internal.org.objectweb.asm.Opcodes.POP;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.SALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.SASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.SWAP;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.METHODHANDLE_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_METHODHANDLE;
import java.util.List;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Type;
/**
* Base class for all method generating classes.
*
*/
public class MethodGenerator extends MethodVisitor {
private final int access;
private final String name;
private final String descriptor;
private final Type returnType;
private final Type[] argumentTypes;
MethodGenerator(final MethodVisitor mv, final int access, final String name, final String descriptor) {
super(ASM4, mv);
this.access = access;
this.name = name;
this.descriptor = descriptor;
this.returnType = Type.getReturnType(descriptor);
this.argumentTypes = Type.getArgumentTypes(descriptor);
}
int getAccess() {
return access;
}
final String getName() {
return name;
}
final String getDescriptor() {
return descriptor;
}
final Type getReturnType() {
return returnType;
}
final Type[] getArgumentTypes() {
return argumentTypes;
}
/**
* Check whether access for this method is static
* @return true if static
*/
protected final boolean isStatic() {
return (getAccess() & ACC_STATIC) != 0;
}
/**
* Check whether this method is a constructor
* @return true if constructor
*/
protected final boolean isConstructor() {
return "<init>".equals(name);
}
void newObject(final String type) {
super.visitTypeInsn(NEW, type);
}
void newObjectArray(final String type) {
super.visitTypeInsn(ANEWARRAY, type);
}
void loadThis() {
if ((access & ACC_STATIC) != 0) {
throw new IllegalStateException("no 'this' inside static method");
}
super.visitVarInsn(ALOAD, 0);
}
void returnValue() {
super.visitInsn(returnType.getOpcode(IRETURN));
}
void returnVoid() {
super.visitInsn(RETURN);
}
// load, store
void arrayLoad(final Type type) {
super.visitInsn(type.getOpcode(IALOAD));
}
void arrayLoad() {
super.visitInsn(AALOAD);
}
void arrayStore(final Type type) {
super.visitInsn(type.getOpcode(IASTORE));
}
void arrayStore() {
super.visitInsn(AASTORE);
}
void loadLiteral(final Object value) {
super.visitLdcInsn(value);
}
void classLiteral(final String className) {
super.visitLdcInsn(className);
}
void loadLocal(final Type type, final int index) {
super.visitVarInsn(type.getOpcode(ILOAD), index);
}
void loadLocal(final int index) {
super.visitVarInsn(ALOAD, index);
}
void storeLocal(final Type type, final int index) {
super.visitVarInsn(type.getOpcode(ISTORE), index);
}
void storeLocal(final int index) {
super.visitVarInsn(ASTORE, index);
}
void checkcast(final String type) {
super.visitTypeInsn(CHECKCAST, type);
}
// push constants/literals
void pushNull() {
super.visitInsn(ACONST_NULL);
}
void push(final int value) {
if (value >= -1 && value <= 5) {
super.visitInsn(ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
super.visitIntInsn(BIPUSH, value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
super.visitIntInsn(SIPUSH, value);
} else {
super.visitLdcInsn(value);
}
}
void loadClass(final String className) {
super.visitLdcInsn(Type.getObjectType(className));
}
void pop() {
super.visitInsn(POP);
}
// various "dups"
void dup() {
super.visitInsn(DUP);
}
void dup2() {
super.visitInsn(DUP2);
}
void swap() {
super.visitInsn(SWAP);
}
void dupArrayValue(final int arrayOpcode) {
switch (arrayOpcode) {
case IALOAD: case FALOAD:
case AALOAD: case BALOAD:
case CALOAD: case SALOAD:
case IASTORE: case FASTORE:
case AASTORE: case BASTORE:
case CASTORE: case SASTORE:
dup();
break;
case LALOAD: case DALOAD:
case LASTORE: case DASTORE:
dup2();
break;
default:
throw new AssertionError("invalid dup");
}
}
void dupReturnValue(final int returnOpcode) {
switch (returnOpcode) {
case IRETURN:
case FRETURN:
case ARETURN:
super.visitInsn(DUP);
return;
case LRETURN:
case DRETURN:
super.visitInsn(DUP2);
return;
case RETURN:
return;
default:
throw new IllegalArgumentException("not return");
}
}
void dupValue(final Type type) {
switch (type.getSize()) {
case 1:
dup();
break;
case 2:
dup2();
break;
default:
throw new AssertionError("invalid dup");
}
}
void dupValue(final String desc) {
final int typeCode = desc.charAt(0);
switch (typeCode) {
case '[':
case 'L':
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
super.visitInsn(DUP);
break;
case 'J':
case 'D':
super.visitInsn(DUP2);
break;
default:
throw new RuntimeException("invalid signature");
}
}
// push default value of given type desc
void defaultValue(final String desc) {
final int typeCode = desc.charAt(0);
switch (typeCode) {
case '[':
case 'L':
super.visitInsn(ACONST_NULL);
break;
case 'Z':
case 'C':
case 'B':
case 'S':
case 'I':
super.visitInsn(ICONST_0);
break;
case 'J':
super.visitInsn(LCONST_0);
break;
case 'F':
super.visitInsn(FCONST_0);
break;
case 'D':
super.visitInsn(DCONST_0);
break;
default:
throw new AssertionError("invalid desc " + desc);
}
}
// invokes, field get/sets
void invokeVirtual(final String owner, final String method, final String desc) {
super.visitMethodInsn(INVOKEVIRTUAL, owner, method, desc);
}
void invokeSpecial(final String owner, final String method, final String desc) {
super.visitMethodInsn(INVOKESPECIAL, owner, method, desc);
}
void invokeStatic(final String owner, final String method, final String desc) {
super.visitMethodInsn(INVOKESTATIC, owner, method, desc);
}
void putStatic(final String owner, final String field, final String desc) {
super.visitFieldInsn(PUTSTATIC, owner, field, desc);
}
void getStatic(final String owner, final String field, final String desc) {
super.visitFieldInsn(GETSTATIC, owner, field, desc);
}
void putField(final String owner, final String field, final String desc) {
super.visitFieldInsn(PUTFIELD, owner, field, desc);
}
void getField(final String owner, final String field, final String desc) {
super.visitFieldInsn(GETFIELD, owner, field, desc);
}
void memberInfoArray(final String className, final List<MemberInfo> mis) {
if (mis.isEmpty()) {
pushNull();
return;
}
int pos = 0;
push(mis.size());
newObjectArray(METHODHANDLE_TYPE);
for (final MemberInfo mi : mis) {
dup();
push(pos++);
visitLdcInsn(new Handle(H_INVOKESTATIC, className, mi.getJavaName(), mi.getJavaDesc()));
arrayStore(TYPE_METHODHANDLE);
}
}
void computeMaxs() {
// These values are ignored as we create class writer
// with ClassWriter.COMPUTE_MAXS flag.
super.visitMaxs(Short.MAX_VALUE, Short.MAX_VALUE);
}
// debugging support - print calls
void println(final String msg) {
super.visitFieldInsn(GETSTATIC,
"java/lang/System",
"out",
"Ljava/io/PrintStream;");
super.visitLdcInsn(msg);
super.visitMethodInsn(INVOKEVIRTUAL,
"java/io/PrintStream",
"println",
"(Ljava/lang/String;)V");
}
// print the object on the top of the stack
void printObject() {
super.visitFieldInsn(GETSTATIC,
"java/lang/System",
"out",
"Ljava/io/PrintStream;");
super.visitInsn(SWAP);
super.visitMethodInsn(INVOKEVIRTUAL,
"java/io/PrintStream",
"println",
"(Ljava/lang/Object;)V");
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.tools.nasgen;
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.FieldVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
/**
* A visitor that does nothing on visitXXX calls.
*
*/
public class NullVisitor extends ClassVisitor {
NullVisitor() {
super(Opcodes.ASM4);
}
@Override
public MethodVisitor visitMethod(
final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
return new MethodVisitor(Opcodes.ASM4) {
@Override
public AnnotationVisitor visitAnnotationDefault() {
return new NullAnnotationVisitor();
}
@Override
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
return new NullAnnotationVisitor();
}
};
}
@Override
public FieldVisitor visitField(
final int access,
final String name,
final String desc,
final String signature,
final Object value) {
return new FieldVisitor(Opcodes.ASM4) {
@Override
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
return new NullAnnotationVisitor();
}
};
}
@Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
return new NullAnnotationVisitor();
}
private static class NullAnnotationVisitor extends AnnotationVisitor {
NullAnnotationVisitor() {
super(Opcodes.ASM4);
}
}
}

View File

@ -0,0 +1,184 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.tools.nasgen;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DUPLICATE_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPE_SUFFIX;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* This class generates prototype class for a @ClassInfo annotated class.
*
*/
public class PrototypeGenerator extends ClassGenerator {
private final ScriptClassInfo scriptClassInfo;
private final String className;
private final int memberCount;
PrototypeGenerator(final ScriptClassInfo sci) {
this.scriptClassInfo = sci;
this.className = scriptClassInfo.getPrototypeClassName();
this.memberCount = scriptClassInfo.getPrototypeMemberCount();
}
byte[] getClassBytes() {
// new class extensing from ScriptObject
cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, PROTOTYPEOBJECT_TYPE, null);
if (memberCount > 0) {
// add fields
emitFields();
// add <clinit>
emitStaticInitializer();
}
// add <init>
emitConstructor();
// add getClassName()
emitGetClassName(scriptClassInfo.getName());
cw.visitEnd();
return cw.toByteArray();
}
// --Internals only below this point
private void emitFields() {
// introduce "Function" type instance fields for each
// prototype @Function in script class info
for (MemberInfo memInfo : scriptClassInfo.getMembers()) {
if (memInfo.isPrototypeFunction()) {
addFunctionField(memInfo.getJavaName());
memInfo = (MemberInfo)memInfo.clone();
memInfo.setJavaDesc(OBJECT_DESC);
addGetter(className, memInfo);
addSetter(className, memInfo);
} else if (memInfo.isPrototypeProperty()) {
if (memInfo.isStaticFinal()) {
addGetter(scriptClassInfo.getJavaName(), memInfo);
} else {
addField(memInfo.getJavaName(), memInfo.getJavaDesc());
memInfo = (MemberInfo)memInfo.clone();
memInfo.setJavaAccess(ACC_PUBLIC);
addGetter(className, memInfo);
addSetter(className, memInfo);
}
}
}
addMapField();
}
private void emitStaticInitializer() {
final MethodGenerator mi = makeStaticInitializer();
emitStaticInitPrefix(mi, className);
for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
if (memInfo.isPrototypeFunction() || memInfo.isPrototypeProperty()) {
linkerAddGetterSetter(mi, className, memInfo);
} else if (memInfo.isPrototypeGetter()) {
final MemberInfo setter = scriptClassInfo.findSetter(memInfo);
linkerAddGetterSetter(mi, className, memInfo, setter);
}
}
emitStaticInitSuffix(mi, className);
}
private void emitConstructor() {
final MethodGenerator mi = makeConstructor();
mi.visitCode();
mi.loadThis();
if (memberCount > 0) {
// call "super(map$)"
mi.getStatic(className, MAP_FIELD_NAME, MAP_DESC);
// make sure we use duplicated PropertyMap so that original map
// stays intact and so can be used for many globals in same context
mi.invokeVirtual(MAP_TYPE, MAP_DUPLICATE, MAP_DUPLICATE_DESC);
mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, SCRIPTOBJECT_INIT_DESC);
// initialize Function type fields
initFunctionFields(mi);
} else {
// call "super()"
mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, DEFAULT_INIT_DESC);
}
mi.returnVoid();
mi.computeMaxs();
mi.visitEnd();
}
private void initFunctionFields(final MethodGenerator mi) {
for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
if (! memInfo.isPrototypeFunction()) {
continue;
}
mi.loadThis();
newFunction(mi, scriptClassInfo.getJavaName(), memInfo, scriptClassInfo.findSpecializations(memInfo.getJavaName()));
mi.putField(className, memInfo.getJavaName(), OBJECT_DESC);
}
}
/**
* External entry point for PrototypeGenerator if called from the command line
*
* @param args arguments, takes 1 argument which is the class to process
* @throws IOException if class cannot be read
*/
public static void main(final String[] args) throws IOException {
if (args.length != 1) {
System.err.println("Usage: " + ConstructorGenerator.class.getName() + " <class>");
System.exit(1);
}
final String className = args[0].replace('.', '/');
final ScriptClassInfo sci = getScriptClassInfo(className + ".class");
if (sci == null) {
System.err.println("No @ScriptClass in " + className);
System.exit(2);
throw new AssertionError(); //guard against warning that sci is null below
}
try {
sci.verify();
} catch (final Exception e) {
System.err.println(e.getMessage());
System.exit(3);
}
final PrototypeGenerator gen = new PrototypeGenerator(sci);
try (FileOutputStream fos = new FileOutputStream(className + PROTOTYPE_SUFFIX + ".class")) {
fos.write(gen.getClassBytes());
}
}
}

View File

@ -0,0 +1,237 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.tools.nasgen;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import jdk.internal.org.objectweb.asm.Type;
import jdk.nashorn.internal.objects.annotations.Constructor;
import jdk.nashorn.internal.objects.annotations.Function;
import jdk.nashorn.internal.objects.annotations.Getter;
import jdk.nashorn.internal.objects.annotations.Property;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.Setter;
import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
/**
* All annotation information from a class that is annotated with
* the annotation com.sun.oracle.objects.annotations.ScriptClass.
*
*/
public final class ScriptClassInfo {
// descriptots for various annotations
static final String SCRIPT_CLASS_ANNO_DESC = Type.getDescriptor(ScriptClass.class);
static final String CONSTRUCTOR_ANNO_DESC = Type.getDescriptor(Constructor.class);
static final String FUNCTION_ANNO_DESC = Type.getDescriptor(Function.class);
static final String GETTER_ANNO_DESC = Type.getDescriptor(Getter.class);
static final String SETTER_ANNO_DESC = Type.getDescriptor(Setter.class);
static final String PROPERTY_ANNO_DESC = Type.getDescriptor(Property.class);
static final String WHERE_ENUM_DESC = Type.getDescriptor(Where.class);
static final String SPECIALIZED_FUNCTION = Type.getDescriptor(SpecializedFunction.class);
static final String SPECIALIZED_CONSTRUCTOR = Type.getDescriptor(SpecializedConstructor.class);
static final Map<String, Kind> annotations = new HashMap<>();
static {
annotations.put(SCRIPT_CLASS_ANNO_DESC, Kind.SCRIPT_CLASS);
annotations.put(FUNCTION_ANNO_DESC, Kind.FUNCTION);
annotations.put(CONSTRUCTOR_ANNO_DESC, Kind.CONSTRUCTOR);
annotations.put(GETTER_ANNO_DESC, Kind.GETTER);
annotations.put(SETTER_ANNO_DESC, Kind.SETTER);
annotations.put(PROPERTY_ANNO_DESC, Kind.PROPERTY);
annotations.put(SPECIALIZED_FUNCTION, Kind.SPECIALIZED_FUNCTION);
annotations.put(SPECIALIZED_CONSTRUCTOR, Kind.SPECIALIZED_CONSTRUCTOR);
}
// name of the script class
private String name;
// member info for script properties
private List<MemberInfo> members = Collections.emptyList();
// java class name that is annotated with @ScriptClass
private String javaName;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(final String name) {
this.name = name;
}
/**
* @return the members
*/
public List<MemberInfo> getMembers() {
return Collections.unmodifiableList(members);
}
/**
* @param members the members to set
*/
public void setMembers(final List<MemberInfo> members) {
this.members = members;
}
MemberInfo getConstructor() {
for (final MemberInfo memInfo : members) {
if (memInfo.getKind() == Kind.CONSTRUCTOR) {
return memInfo;
}
}
return null;
}
List<MemberInfo> getSpecializedConstructors() {
final List<MemberInfo> res = new LinkedList<>();
for (final MemberInfo memInfo : members) {
if (memInfo.getKind() == Kind.SPECIALIZED_CONSTRUCTOR) {
res.add(memInfo);
}
}
return res;
}
int getPrototypeMemberCount() {
int count = 0;
for (final MemberInfo memInfo : members) {
if (memInfo.getWhere() == Where.PROTOTYPE || memInfo.isConstructor()) {
count++;
}
}
return count;
}
int getConstructorMemberCount() {
int count = 0;
for (final MemberInfo memInfo : members) {
if (memInfo.getWhere() == Where.CONSTRUCTOR) {
count++;
}
}
return count;
}
int getInstancePropertyCount() {
int count = 0;
for (final MemberInfo memInfo : members) {
if (memInfo.getWhere() == Where.INSTANCE) {
count++;
}
}
return count;
}
MemberInfo find(final String findJavaName, final String findJavaDesc, final int findAccess) {
for (final MemberInfo memInfo : members) {
if (memInfo.getJavaName().equals(findJavaName) &&
memInfo.getJavaDesc().equals(findJavaDesc) &&
memInfo.getJavaAccess() == findAccess) {
return memInfo;
}
}
return null;
}
List<MemberInfo> findSpecializations(final String methodName) {
final List<MemberInfo> res = new LinkedList<>();
for (final MemberInfo memInfo : members) {
if (memInfo.getName().equals(methodName) &&
memInfo.getKind() == Kind.SPECIALIZED_FUNCTION) {
res.add(memInfo);
}
}
return res;
}
MemberInfo findSetter(final MemberInfo getter) {
assert getter.getKind() == Kind.GETTER : "getter expected";
final String getterName = getter.getName();
final Where getterWhere = getter.getWhere();
for (final MemberInfo memInfo : members) {
if (memInfo.getKind() == Kind.SETTER &&
getterName.equals(memInfo.getName()) &&
getterWhere == memInfo.getWhere()) {
return memInfo;
}
}
return null;
}
/**
* @return the javaName
*/
public String getJavaName() {
return javaName;
}
/**
* @param javaName the javaName to set
*/
void setJavaName(final String javaName) {
this.javaName = javaName;
}
String getConstructorClassName() {
return getJavaName() + StringConstants.CONSTRUCTOR_SUFFIX;
}
String getPrototypeClassName() {
return getJavaName() + StringConstants.PROTOTYPE_SUFFIX;
}
void verify() {
boolean constructorSeen = false;
for (final MemberInfo memInfo : getMembers()) {
if (memInfo.isConstructor()) {
if (constructorSeen) {
error("more than @Constructor method");
}
constructorSeen = true;
}
try {
memInfo.verify();
} catch (final Exception e) {
error(e.getMessage());
}
}
}
private void error(final String msg) throws RuntimeException {
throw new RuntimeException(javaName + " : " + msg);
}
}

View File

@ -0,0 +1,331 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.tools.nasgen;
import static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.SCRIPT_CLASS_ANNO_DESC;
import static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.WHERE_ENUM_DESC;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.FieldVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
/**
* This class collects all @ScriptClass and other annotation information from a
* compiled .class file. Enforces that @Function/@Getter/@Setter/@Constructor
* methods are declared to be 'static'.
*/
public class ScriptClassInfoCollector extends ClassVisitor {
private String scriptClassName;
private List<MemberInfo> scriptMembers;
private String javaClassName;
ScriptClassInfoCollector(final ClassVisitor visitor) {
super(Opcodes.ASM4, visitor);
}
ScriptClassInfoCollector() {
this(new NullVisitor());
}
private void addScriptMember(final MemberInfo memInfo) {
if (scriptMembers == null) {
scriptMembers = new ArrayList<>();
}
scriptMembers.add(memInfo);
}
@Override
public void visit(final int version, final int access, final String name, final String signature,
final String superName, final String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
javaClassName = name;
}
@Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
final AnnotationVisitor delegateAV = super.visitAnnotation(desc, visible);
if (SCRIPT_CLASS_ANNO_DESC.equals(desc)) {
return new AnnotationVisitor(Opcodes.ASM4, delegateAV) {
@Override
public void visit(final String name, final Object value) {
if ("value".equals(name)) {
scriptClassName = (String) value;
}
super.visit(name, value);
}
};
}
return delegateAV;
}
@Override
public FieldVisitor visitField(final int fieldAccess, final String fieldName, final String fieldDesc, final String signature, final Object value) {
final FieldVisitor delegateFV = super.visitField(fieldAccess, fieldName, fieldDesc, signature, value);
return new FieldVisitor(Opcodes.ASM4, delegateFV) {
@Override
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
final AnnotationVisitor delegateAV = super.visitAnnotation(descriptor, visible);
if (ScriptClassInfo.PROPERTY_ANNO_DESC.equals(descriptor)) {
final MemberInfo memInfo = new MemberInfo();
memInfo.setKind(Kind.PROPERTY);
memInfo.setJavaName(fieldName);
memInfo.setJavaDesc(fieldDesc);
memInfo.setJavaAccess(fieldAccess);
if ((fieldAccess & Opcodes.ACC_STATIC) != 0) {
memInfo.setValue(value);
}
addScriptMember(memInfo);
return new AnnotationVisitor(Opcodes.ASM4, delegateAV) {
// These could be "null" if values are not suppiled,
// in which case we have to use the default values.
private String name;
private Integer attributes;
private String clazz = "";
private Where where;
@Override
public void visit(final String annotationName, final Object annotationValue) {
switch (annotationName) {
case "name":
this.name = (String) annotationValue;
break;
case "attributes":
this.attributes = (Integer) annotationValue;
break;
case "clazz":
this.clazz = (annotationValue == null) ? "" : annotationValue.toString();
break;
default:
break;
}
super.visit(annotationName, annotationValue);
}
@Override
public void visitEnum(final String enumName, final String desc, final String enumValue) {
if ("where".equals(enumName) && WHERE_ENUM_DESC.equals(desc)) {
this.where = Where.valueOf(enumValue);
}
super.visitEnum(enumName, desc, enumValue);
}
@Override
public void visitEnd() {
super.visitEnd();
memInfo.setName(name == null ? fieldName : name);
memInfo.setAttributes(attributes == null
? MemberInfo.DEFAULT_ATTRIBUTES : attributes);
clazz = clazz.replace('.', '/');
memInfo.setInitClass(clazz);
memInfo.setWhere(where == null? Where.INSTANCE : where);
}
};
}
return delegateAV;
}
};
}
private void error(final String javaName, final String javaDesc, final String msg) {
throw new RuntimeException(scriptClassName + "." + javaName + javaDesc + " : " + msg);
}
@Override
public MethodVisitor visitMethod(final int methodAccess, final String methodName,
final String methodDesc, final String signature, final String[] exceptions) {
final MethodVisitor delegateMV = super.visitMethod(methodAccess, methodName, methodDesc,
signature, exceptions);
return new MethodVisitor(Opcodes.ASM4, delegateMV) {
@Override
public AnnotationVisitor visitAnnotation(final String descriptor, final boolean visible) {
final AnnotationVisitor delegateAV = super.visitAnnotation(descriptor, visible);
final Kind annoKind = ScriptClassInfo.annotations.get(descriptor);
if (annoKind != null) {
if ((methodAccess & Opcodes.ACC_STATIC) == 0) {
error(methodName, methodDesc, "nasgen method annotations cannot be on instance methods");
}
final MemberInfo memInfo = new MemberInfo();
memInfo.setKind(annoKind);
memInfo.setJavaName(methodName);
memInfo.setJavaDesc(methodDesc);
memInfo.setJavaAccess(methodAccess);
addScriptMember(memInfo);
return new AnnotationVisitor(Opcodes.ASM4, delegateAV) {
// These could be "null" if values are not suppiled,
// in which case we have to use the default values.
private String name;
private Integer attributes;
private Integer arity;
private Where where;
@Override
public void visit(final String annotationName, final Object annotationValue) {
switch (annotationName) {
case "name":
this.name = (String)annotationValue;
break;
case "attributes":
this.attributes = (Integer)annotationValue;
break;
case "arity":
this.arity = (Integer)annotationValue;
break;
default:
break;
}
super.visit(annotationName, annotationValue);
}
@Override
public void visitEnum(final String enumName, final String desc, final String enumValue) {
if ("where".equals(enumName) && WHERE_ENUM_DESC.equals(desc)) {
this.where = Where.valueOf(enumValue);
}
super.visitEnum(enumName, desc, enumValue);
}
@Override
public void visitEnd() {
super.visitEnd();
if (memInfo.getKind() == Kind.CONSTRUCTOR) {
memInfo.setName(name == null ? scriptClassName : name);
} else {
memInfo.setName(name == null ? methodName : name);
}
memInfo.setAttributes(attributes == null ? MemberInfo.DEFAULT_ATTRIBUTES : attributes);
memInfo.setArity((arity == null)? MemberInfo.DEFAULT_ARITY : arity);
if (where == null) {
// by default @Getter/@Setter belongs to INSTANCE
// @Function belong to PROTOTYPE.
switch (memInfo.getKind()) {
case GETTER:
case SETTER:
where = Where.INSTANCE;
break;
case SPECIALIZED_CONSTRUCTOR:
case CONSTRUCTOR:
where = Where.CONSTRUCTOR;
break;
case FUNCTION:
where = Where.PROTOTYPE;
break;
case SPECIALIZED_FUNCTION:
//TODO is this correct
default:
break;
}
}
memInfo.setWhere(where);
}
};
}
return delegateAV;
}
};
}
ScriptClassInfo getScriptClassInfo() {
ScriptClassInfo sci = null;
if (scriptClassName != null) {
sci = new ScriptClassInfo();
sci.setName(scriptClassName);
if (scriptMembers == null) {
scriptMembers = Collections.emptyList();
}
sci.setMembers(scriptMembers);
sci.setJavaName(javaClassName);
}
return sci;
}
/**
* External entry point for ScriptClassInfoCollector if invoked from the command line
* @param args argument vector, args contains a class for which to collect info
* @throws IOException if there were problems parsing args or class
*/
public static void main(final String[] args) throws IOException {
if (args.length != 1) {
System.err.println("Usage: " + ScriptClassInfoCollector.class.getName() + " <class>");
System.exit(1);
}
args[0] = args[0].replace('.', '/');
final ScriptClassInfoCollector scic = new ScriptClassInfoCollector();
try (final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(args[0] + ".class"))) {
final ClassReader reader = new ClassReader(bis);
reader.accept(scic, 0);
}
final ScriptClassInfo sci = scic.getScriptClassInfo();
final PrintStream out = System.out;
if (sci != null) {
out.println("script class: " + sci.getName());
out.println("===================================");
for (final MemberInfo memInfo : sci.getMembers()) {
out.println("kind : " + memInfo.getKind());
out.println("name : " + memInfo.getName());
out.println("attributes: " + memInfo.getAttributes());
out.println("javaName: " + memInfo.getJavaName());
out.println("javaDesc: " + memInfo.getJavaDesc());
out.println("where: " + memInfo.getWhere());
out.println("=====================================");
}
} else {
out.println(args[0] + " is not a @ScriptClass");
}
}
}

View File

@ -0,0 +1,309 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.tools.nasgen;
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.NEW;
import static jdk.internal.org.objectweb.asm.Opcodes.PUTFIELD;
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.$CLINIT$;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.CLINIT;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_TYPE;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import jdk.internal.org.objectweb.asm.AnnotationVisitor;
import jdk.internal.org.objectweb.asm.Attribute;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.FieldVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
/**
* This class instruments the java class annotated with @ScriptClass.
*
* Changes done are:
*
* 1) remove all jdk.nashorn.internal.objects.annotations.* annotations.
* 2) static final @Property fields stay here. Other @Property fields moved to
* respective classes depending on 'where' value of annotation.
* 2) add "Map" type static field named "$map".
* 3) add static initializer block to initialize map.
*/
public class ScriptClassInstrumentor extends ClassVisitor {
private final ScriptClassInfo scriptClassInfo;
private final int memberCount;
private boolean staticInitFound;
ScriptClassInstrumentor(final ClassVisitor visitor, final ScriptClassInfo sci) {
super(Opcodes.ASM4, visitor);
if (sci == null) {
throw new IllegalArgumentException("Null ScriptClassInfo, is the class annotated?");
}
this.scriptClassInfo = sci;
this.memberCount = scriptClassInfo.getInstancePropertyCount();
}
@Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
if (ScriptClassInfo.annotations.containsKey(desc)) {
// ignore @ScriptClass
return null;
}
return super.visitAnnotation(desc, visible);
}
@Override
public FieldVisitor visitField(final int fieldAccess, final String fieldName,
final String fieldDesc, final String signature, final Object value) {
final MemberInfo memInfo = scriptClassInfo.find(fieldName, fieldDesc, fieldAccess);
if (memInfo != null && memInfo.getKind() == Kind.PROPERTY &&
memInfo.getWhere() != Where.INSTANCE && !memInfo.isStaticFinal()) {
// non-instance @Property fields - these have to go elsewhere unless 'static final'
return null;
}
final FieldVisitor delegateFV = super.visitField(fieldAccess, fieldName, fieldDesc,
signature, value);
return new FieldVisitor(Opcodes.ASM4, delegateFV) {
@Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
if (ScriptClassInfo.annotations.containsKey(desc)) {
// ignore script field annotations
return null;
}
return fv.visitAnnotation(desc, visible);
}
@Override
public void visitAttribute(final Attribute attr) {
fv.visitAttribute(attr);
}
@Override
public void visitEnd() {
fv.visitEnd();
}
};
}
@Override
public MethodVisitor visitMethod(final int methodAccess, final String methodName,
final String methodDesc, final String signature, final String[] exceptions) {
final boolean isConstructor = INIT.equals(methodName);
final boolean isStaticInit = CLINIT.equals(methodName);
if (isStaticInit) {
staticInitFound = true;
}
final MethodGenerator delegateMV = new MethodGenerator(super.visitMethod(methodAccess, methodName, methodDesc,
signature, exceptions), methodAccess, methodName, methodDesc);
return new MethodVisitor(Opcodes.ASM4, delegateMV) {
@Override
public void visitInsn(final int opcode) {
// call $clinit$ just before return from <clinit>
if (isStaticInit && opcode == RETURN) {
super.visitMethodInsn(INVOKESTATIC, scriptClassInfo.getJavaName(),
$CLINIT$, DEFAULT_INIT_DESC);
}
super.visitInsn(opcode);
}
@Override
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
if (isConstructor && opcode == INVOKESPECIAL &&
INIT.equals(name) && SCRIPTOBJECT_TYPE.equals(owner)) {
super.visitFieldInsn(GETSTATIC, scriptClassInfo.getJavaName(),
MAP_FIELD_NAME, MAP_DESC);
super.visitMethodInsn(INVOKESPECIAL, SCRIPTOBJECT_TYPE, INIT,
SCRIPTOBJECT_INIT_DESC);
if (memberCount > 0) {
// initialize @Property fields if needed
for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
if (memInfo.isInstanceProperty() && !memInfo.getInitClass().isEmpty()) {
final String clazz = memInfo.getInitClass();
super.visitVarInsn(ALOAD, 0);
super.visitTypeInsn(NEW, clazz);
super.visitInsn(DUP);
super.visitMethodInsn(INVOKESPECIAL, clazz,
INIT, DEFAULT_INIT_DESC);
super.visitFieldInsn(PUTFIELD, scriptClassInfo.getJavaName(),
memInfo.getJavaName(), memInfo.getJavaDesc());
}
if (memInfo.isInstanceFunction()) {
super.visitVarInsn(ALOAD, 0);
ClassGenerator.newFunction(delegateMV, scriptClassInfo.getJavaName(), memInfo, scriptClassInfo.findSpecializations(memInfo.getJavaName()));
super.visitFieldInsn(PUTFIELD, scriptClassInfo.getJavaName(),
memInfo.getJavaName(), OBJECT_DESC);
}
}
}
} else {
super.visitMethodInsn(opcode, owner, name, desc);
}
}
@Override
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
if (ScriptClassInfo.annotations.containsKey(desc)) {
// ignore script method annotations
return null;
}
return super.visitAnnotation(desc, visible);
}
};
}
@Override
public void visitEnd() {
emitFields();
emitStaticInitializer();
emitGettersSetters();
super.visitEnd();
}
private void emitFields() {
// introduce "Function" type instance fields for each
// instance @Function in script class info
final String className = scriptClassInfo.getJavaName();
for (MemberInfo memInfo : scriptClassInfo.getMembers()) {
if (memInfo.isInstanceFunction()) {
ClassGenerator.addFunctionField(cv, memInfo.getJavaName());
memInfo = (MemberInfo)memInfo.clone();
memInfo.setJavaDesc(OBJECT_DESC);
ClassGenerator.addGetter(cv, className, memInfo);
ClassGenerator.addSetter(cv, className, memInfo);
}
}
ClassGenerator.addMapField(this);
}
void emitGettersSetters() {
if (memberCount > 0) {
for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
final String className = scriptClassInfo.getJavaName();
if (memInfo.isInstanceProperty()) {
ClassGenerator.addGetter(cv, className, memInfo);
if (! memInfo.isFinal()) {
ClassGenerator.addSetter(cv, className, memInfo);
}
}
}
}
}
private void emitStaticInitializer() {
final String className = scriptClassInfo.getJavaName();
if (! staticInitFound) {
// no user written <clinit> and so create one
final MethodVisitor mv = ClassGenerator.makeStaticInitializer(this);
mv.visitCode();
mv.visitInsn(RETURN);
mv.visitMaxs(Short.MAX_VALUE, 0);
mv.visitEnd();
}
// Now generate $clinit$
final MethodGenerator mi = ClassGenerator.makeStaticInitializer(this, $CLINIT$);
ClassGenerator.emitStaticInitPrefix(mi, className);
if (memberCount > 0) {
for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
if (memInfo.isInstanceProperty() || memInfo.isInstanceFunction()) {
ClassGenerator.linkerAddGetterSetter(mi, className, memInfo);
} else if (memInfo.isInstanceGetter()) {
final MemberInfo setter = scriptClassInfo.findSetter(memInfo);
ClassGenerator.linkerAddGetterSetter(mi, className, memInfo, setter);
}
}
}
ClassGenerator.emitStaticInitSuffix(mi, className);
}
/**
* External entry point for ScriptClassInfoCollector if run from the command line
*
* @param args arguments - one argument is needed, the name of the class to collect info from
*
* @throws IOException if there are problems reading class
*/
public static void main(final String[] args) throws IOException {
if (args.length != 1) {
System.err.println("Usage: " + ScriptClassInfoCollector.class.getName() + " <class>");
System.exit(1);
}
final String fileName = args[0].replace('.', '/') + ".class";
final ScriptClassInfo sci = ClassGenerator.getScriptClassInfo(fileName);
if (sci == null) {
System.err.println("No @ScriptClass in " + fileName);
System.exit(2);
throw new AssertionError(); //guard against warning that sci is null below
}
try {
sci.verify();
} catch (final Exception e) {
System.err.println(e.getMessage());
System.exit(3);
}
final ClassWriter writer = ClassGenerator.makeClassWriter();
try (final BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName))) {
final ClassReader reader = new ClassReader(bis);
final CheckClassAdapter checker = new CheckClassAdapter(writer);
final ScriptClassInstrumentor instr = new ScriptClassInstrumentor(checker, sci);
reader.accept(instr, 0);
}
try (FileOutputStream fos = new FileOutputStream(fileName)) {
fos.write(writer.toByteArray());
}
}
}

View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.tools.nasgen;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Method;
import jdk.internal.org.objectweb.asm.Type;
import jdk.nashorn.internal.objects.PrototypeObject;
import jdk.nashorn.internal.objects.ScriptFunctionImpl;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.linker.Lookup;
/**
* String constants used for code generation/instrumentation.
*/
@SuppressWarnings("javadoc")
public interface StringConstants {
static final Type TYPE_METHOD = Type.getType(Method.class);
static final Type TYPE_METHODHANDLE = Type.getType(MethodHandle.class);
static final Type TYPE_METHODHANDLE_ARRAY = Type.getType(MethodHandle[].class);
static final Type TYPE_OBJECT = Type.getType(Object.class);
static final Type TYPE_CLASS = Type.getType(Class.class);
static final Type TYPE_STRING = Type.getType(String.class);
// Nashorn types
static final Type TYPE_LOOKUP = Type.getType(Lookup.class);
static final Type TYPE_PROPERTYMAP = Type.getType(PropertyMap.class);
static final Type TYPE_PROTOTYPEOBJECT = Type.getType(PrototypeObject.class);
static final Type TYPE_SCRIPTFUNCTION = Type.getType(ScriptFunction.class);
static final Type TYPE_SCRIPTFUNCTIONIMPL = Type.getType(ScriptFunctionImpl.class);
static final Type TYPE_SCRIPTOBJECT = Type.getType(ScriptObject.class);
static final String PROTOTYPE = "prototype";
static final String PROTOTYPE_SUFFIX = "$Prototype";
static final String CONSTRUCTOR_SUFFIX = "$Constructor";
// This field name is known to Nashorn runtime (Context).
// Synchronize the name change, if needed at all.
static final String MAP_FIELD_NAME = "$nasgenmap$";
static final String $CLINIT$ = "$clinit$";
static final String CLINIT = "<clinit>";
static final String INIT = "<init>";
static final String DEFAULT_INIT_DESC = Type.getMethodDescriptor(Type.VOID_TYPE);
static final String SCRIPTOBJECT_INIT_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_PROPERTYMAP);
static final String METHODHANDLE_TYPE = TYPE_METHODHANDLE.getInternalName();
static final String OBJECT_TYPE = TYPE_OBJECT.getInternalName();
static final String OBJECT_DESC = TYPE_OBJECT.getDescriptor();
static final String OBJECT_ARRAY_DESC = Type.getDescriptor(Object[].class);
static final String SCRIPTFUNCTION_TYPE = TYPE_SCRIPTFUNCTION.getInternalName();
static final String SCRIPTFUNCTIONIMPL_TYPE = TYPE_SCRIPTFUNCTIONIMPL.getInternalName();
static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION = "makeFunction";
static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC =
Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE);
static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC =
Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE, TYPE_METHODHANDLE_ARRAY);
static final String SCRIPTFUNCTIONIMPL_INIT_DESC3 =
Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_METHODHANDLE_ARRAY);
static final String SCRIPTFUNCTIONIMPL_INIT_DESC4 =
Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_PROPERTYMAP, TYPE_METHODHANDLE_ARRAY);
static final String SCRIPTFUNCTION_SETARITY = "setArity";
static final String SCRIPTFUNCTION_SETARITY_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE);
static final String PROTOTYPEOBJECT_TYPE = TYPE_PROTOTYPEOBJECT.getInternalName();
static final String PROTOTYPEOBJECT_SETCONSTRUCTOR = "setConstructor";
static final String PROTOTYPEOBJECT_SETCONSTRUCTOR_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_OBJECT, TYPE_OBJECT);
static final String SCRIPTOBJECT_TYPE = TYPE_SCRIPTOBJECT.getInternalName();
static final String MAP_TYPE = TYPE_PROPERTYMAP.getInternalName();
static final String MAP_DESC = TYPE_PROPERTYMAP.getDescriptor();
static final String MAP_NEWMAP = "newMap";
static final String MAP_NEWMAP_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP, TYPE_CLASS);
static final String MAP_DUPLICATE = "duplicate";
static final String MAP_DUPLICATE_DESC = Type.getMethodDescriptor(TYPE_PROPERTYMAP);
static final String MAP_SETFLAGS = "setFlags";
static final String LOOKUP_TYPE = TYPE_LOOKUP.getInternalName();
static final String LOOKUP_GETMETHOD = "getMethod";
static final String LOOKUP_NEWPROPERTY = "newProperty";
static final String LOOKUP_NEWPROPERTY_DESC =
Type.getMethodDescriptor(TYPE_PROPERTYMAP, TYPE_PROPERTYMAP, TYPE_STRING, Type.INT_TYPE, TYPE_METHODHANDLE, TYPE_METHODHANDLE);
static final String GETTER_PREFIX = "G$";
static final String SETTER_PREFIX = "S$";
// ScriptObject.getClassName() method.
static final String GET_CLASS_NAME = "getClassName";
static final String GET_CLASS_NAME_DESC = Type.getMethodDescriptor(TYPE_STRING);
}

View File

@ -0,0 +1,445 @@
This document describes system properties that are used for internal
debugging and instrumentation purposes, along with the system loggers,
which are used for the same thing.
This document is intended as a developer resource, and it is not
needed as Nashorn documentation for normal usage. Flags and system
properties described herein are subject to change without notice.
=====================================
1. System properties used internally
=====================================
This documentation of the system property flags assume that the
default value of the flag is false, unless otherwise specified.
SYSTEM PROPERTY: -Dnashorn.callsiteaccess.debug
See the description of the access logger below. This flag is
equivalent to enabling the access logger with "info" level.
SYSTEM PROPERTY: -Dnashorn.compiler.ints.disable
This flag prevents ints and longs (non double values) from being used
for any primitive representation in the lowered IR. This is default
false, i.e Lower will attempt to use integer variables as long as it
can. For example, var x = 17 would try to use x as an integer, unless
other operations occur later that require coercion to wider type, for
example x *= 17.1;
SYSTEM PROPERTY: -Dnashorn.compiler.intarithmetic
Arithmetic operations in Nashorn (except bitwise ones) typically
coerce the operands to doubles (as per the JavaScript spec). To switch
this off and remain in integer mode, for example for "var x = a&b; var
y = c&d; var z = x*y;", use this flag. This will force the
multiplication of variables that are ints to be done with the IMUL
bytecode and the result "z" to become an int.
WARNING: Note that is is experimental only to ensure that type support
exists for all primitive types. The generated code is unsound. This
will be the case until we do optimizations based on it. There is a CR
in Nashorn to do better range analysis, and ensure that this is only
done where the operation can't overflow into a wider type. Currently
no overflow checking is done, so at the moment, until range analysis
has been completed, this option is turned off.
We've experimented by using int arithmetic for everything and putting
overflow checks afterwards, which would recompute the operation with
the correct precision, but have yet to find a configuration where this
is faster than just using doubles directly, even if the int operation
does not overflow. Getting access to a JVM intrinsic that does branch
on overflow would probably alleviate this.
There is also a problem with this optimistic approach if the symbol
happens to reside in a local variable slot in the bytecode, as those
are strongly typed. Then we would need to split large sections of
control flow, so this is probably not the right way to go, while range
analysis is. There is a large difference between integer bytecode
without overflow checks and double bytecode. The former is
significantly faster.
SYSTEM PROPERTY: -Dnashorn.codegen.debug, -Dnashorn.codegen.debug.trace=<x>
See the description of the codegen logger below.
SYSTEM_PROPERTY: -Dnashorn.fields.debug
See the description on the fields logger below.
SYSTEM PROPERTY: -Dnashorn.fields.dual
When this property is true, Nashorn will attempt to use primitive
fields for AccessorProperties (currently just AccessorProperties, not
spill properties). Memory footprint for script objects will increase,
as we need to maintain both a primitive field (a long) as well as an
Object field for the property value. Ints are represented as the 32
low bits of the long fields. Doubles are represented as the
doubleToLongBits of their value. This way a single field can be used
for all primitive types. Packing and unpacking doubles to their bit
representation is intrinsified by the JVM and extremely fast.
While dual fields in theory runs significantly faster than Object
fields due to reduction of boxing and memory allocation overhead,
there is still work to be done to make this a general purpose
solution. Research is ongoing.
In the future, this might complement or be replaced by experimental
feature sun.misc.TaggedArray, which has been discussed on the mlvm
mailing list. TaggedArrays are basically a way to share data space
between primitives and references, and have the GC understand this.
As long as only primitive values are written to the fields and enough
type information exists to make sure that any reads don't have to be
uselessly boxed and unboxed, this is significantly faster than the
standard "Objects only" approach that currently is the default. See
test/examples/dual-fields-micro.js for an example that runs twice as
fast with dual fields as without them. Here, the compiler, can
determine that we are dealing with numbers only throughout the entire
property life span of the properties involved.
If a "real" object (not a boxed primitive) is written to a field that
has a primitive representation, its callsite is relinked and an Object
field is used forevermore for that particular field in that
PropertyMap and its children, even if primitives are later assigned to
it.
As the amount of compile time type information is very small in a
dynamic language like JavaScript, it is frequently the case that
something has to be treated as an object, because we don't know any
better. In reality though, it is often a boxed primitive is stored to
an AccessorProperty. The fastest way to handle this soundly is to use
a callsite typecheck and avoid blowing the field up to an Object. We
never revert object fields to primitives. Ping-pong:ing back and forth
between primitive representation and Object representation would cause
fatal performance overhead, so this is not an option.
For a general application the dual fields approach is still slower
than objects only fields in some places, about the same in most cases,
and significantly faster in very few. This is due the program using
primitives, but we still can't prove it. For example "local_var a =
call(); field = a;" may very well write a double to the field, but the
compiler dare not guess a double type if field is a local variable,
due to bytecode variables being strongly typed and later non
interchangeable. To get around this, the entire method would have to
be replaced and a continuation retained to restart from. We believe
that the next steps we should go through are instead:
1) Implement method specialization based on callsite, as it's quite
frequently the case that numbers are passed around, but currently our
function nodes just have object types visible to the compiler. For
example "var b = 17; func(a,b,17)" is an example where two parameters
can be specialized, but the main version of func might also be called
from another callsite with func(x,y,"string").
2) This requires lazy jitting as the functions have to be specialized
per callsite.
Even though "function square(x) { return x*x }" might look like a
trivial function that can always only take doubles, this is not
true. Someone might have overridden the valueOf for x so that the
toNumber coercion has side effects. To fulfil JavaScript semantics,
the coercion has to run twice for both terms of the multiplication
even if they are the same object. This means that call site
specialization is necessary, not parameter specialization on the form
"function square(x) { var xd = (double)x; return xd*xd; }", as one
might first think.
Generating a method specialization for any variant of a function that
we can determine by types at compile time is a combinatorial explosion
of byte code (try it e.g. on all the variants of am3 in the Octane
benchmark crypto.js). Thus, this needs to be lazy
3) Possibly optimistic callsite writes, something on the form
x = y; //x is a field known to be a primitive. y is only an object as
far as we can tell
turns into
try {
x = (int)y;
} catch (X is not an integer field right now | ClassCastException e) {
x = y;
}
Mini POC shows that this is the key to a lot of dual field performance
in seemingly trivial micros where one unknown object, in reality
actually a primitive, foils it for us. Very common pattern. Once we
are "all primitives", dual fields runs a lot faster than Object fields
only.
We still have to deal with objects vs primitives for local bytecode
slots, possibly through code copying and versioning.
SYSTEM PROPERTY: -Dnashorn.compiler.symbol.trace=<x>
When this property is set, creation and manipulation of any symbol
named "x" will show information about when the compiler changes its
type assumption, bytecode local variable slot assignment and other
data. This is useful if, for example, a symbol shows up as an Object,
when you believe it should be a primitive. Usually there is an
explanation for this, for example that it exists in the global scope
and type analysis has to be more conservative. In that case, the stack
trace upon type change to object will usually tell us why.
SYSTEM PROPERTY: nashorn.lexer.xmlliterals
If this property it set, it means that the Lexer should attempt to
parse XML literals, which would otherwise generate syntax
errors. Warning: there are currently no unit tests for this
functionality.
XML literals, when this is enabled, end up as standard LiteralNodes in
the IR.
SYSTEM_PROPERTY: nashorn.debug
If this property is set to true, Nashorn runs in Debug mode. Debug
mode is slightly slower, as for example statistics counters are enabled
during the run. Debug mode makes available a NativeDebug instance
called "Debug" in the global space that can be used to print property
maps and layout for script objects, as well as a "dumpCounters" method
that will print the current values of the previously mentioned stats
counters.
These functions currently exists for Debug:
"map" - print(Debug.map(x)) will dump the PropertyMap for object x to
stdout (currently there also exist functions called "embedX", where X
is a value from 0 to 3, that will dump the contents of the embed pool
for the first spill properties in any script object and "spill", that
will dump the contents of the growing spill pool of spill properties
in any script object. This is of course subject to change without
notice, should we change the script object layout.
"methodHandle" - this method returns the method handle that is used
for invoking a particular script function.
"identical" - this method compares two script objects for reference
equality. It is a == Java comparison
"dumpCounters" - will dump the debug counters' current values to
stdout.
Currently we count number of ScriptObjects in the system, number of
Scope objects in the system, number of ScriptObject listeners added,
removed and dead (without references).
We also count number of ScriptFunctions, ScriptFunction invocations
and ScriptFunction allocations.
Furthermore we count PropertyMap statistics: how many property maps
exist, how many times were property maps cloned, how many times did
the property map history cache hit, prevent new allocations, how many
prototype invalidations were done, how many time the property map
proto cache hit.
Finally we count callsite misses on a per callsite bases, which occur
when a callsite has to be relinked, due to a previous assumption of
object layout being invalidated.
SYSTEM PROPERTY: nashorn.methodhandles.debug,
nashorn.methodhandles.debug=create
If this property is enabled, each MethodHandle related call that uses
the java.lang.invoke package gets its MethodHandle intercepted and an
instrumentation printout of arguments and return value appended to
it. This shows exactly which method handles are executed and from
where. (Also MethodTypes and SwitchPoints). This can be augmented with
more information, for example, instance count, by subclassing or
further extending the TraceMethodHandleFactory implementation in
MethodHandleFactory.java.
If the property is specialized with "=create" as its option,
instrumentation will be shown for method handles upon creation time
rather than at runtime usage.
SYSTEM PROPERTY: nashorn.methodhandles.debug.stacktrace
This does the same as nashorn.methodhandles.debug, but when enabled
also dumps the stack trace for every instrumented method handle
operation. Warning: This is enormously verbose, but provides a pretty
decent "grep:able" picture of where the calls are coming from.
See the description of the codegen logger below for a more verbose
description of this option
SYSTEM PROPERTY: nashorn.scriptfunction.specialization.disable
There are several "fast path" implementations of constructors and
functions in the NativeObject classes that, in their original form,
take a variable amount of arguments. Said functions are also declared
to take Object parameters in their original form, as this is what the
JavaScript specification mandates.
However, we often know quite a lot more at a callsite of one of these
functions. For example, Math.min is called with a fixed number (2) of
integer arguments. The overhead of boxing these ints to Objects and
folding them into an Object array for the generic varargs Math.min
function is an order of magnitude slower than calling a specialized
implementation of Math.min that takes two integers. Specialized
functions and constructors are identified by the tag
@SpecializedFunction and @SpecializedConstructor in the Nashorn
code. The linker will link in the most appropriate (narrowest types,
right number of types and least number of arguments) specialization if
specializations are available.
Every ScriptFunction may carry specializations that the linker can
choose from. This framework will likely be extended for user defined
functions. The compiler can often infer enough parameter type info
from callsites for in order to generate simpler versions with less
generic Object types. This feature depends on future lazy jitting, as
there tend to be many calls to user defined functions, some where the
callsite can be specialized, some where we mostly see object
parameters even at the callsite.
If this system property is set to true, the linker will not attempt to
use any specialized function or constructor for native objects, but
just call the generic one.
SYSTEM PROPERTY: nashorn.tcs.miss.samplePercent=<x>
When running with the trace callsite option (-tcs), Nashorn will count
and instrument any callsite misses that require relinking. As the
number of relinks is large and usually produces a lot of output, this
system property can be used to constrain the percentage of misses that
should be logged. Typically this is set to 1 or 5 (percent). 1% is the
default value.
SYSTEM_PROPERTY: nashorn.profilefile=<filename>
When running with the profile callsite options (-pcs), Nashorn will
dump profiling data for all callsites to stderr as a shutdown hook. To
instead redirect this to a file, specify the path to the file using
this system property.
===============
2. The loggers.
===============
The Nashorn loggers can be used to print per-module or per-subsystem
debug information with different levels of verbosity. The loggers for
a given subsystem are available are enabled by using
--log=<systemname>[:<level>]
on the command line.
Here <systemname> identifies the name of the subsystem to be logged
and the optional colon and level argument is a standard
java.util.logging.Level name (severe, warning, info, config, fine,
finer, finest). If the level is left out for a particular subsystem,
it defaults to "info". Any log message logged as the level or a level
that is more important will be output to stderr by the logger.
Several loggers can be enabled by a single command line option, by
putting a comma after each subsystem/level tuple (or each subsystem if
level is unspecified). The --log option can also be given multiple
times on the same command line, with the same effect.
For example: --log=codegen,fields:finest is equivalent to
--log=codegen:info --log=fields:finest
The subsystems that currently support logging are:
* compiler
The compiler is in charge of turning source code and function nodes
into byte code, and installs the classes into a class loader
controlled from the Context. Log messages are, for example, about
things like new compile units being allocated. The compiler has global
settings that all the tiers of codegen (e.g. Lower and CodeGenerator)
use.
* codegen
The code generator is the emitter stage of the code pipeline, and
turns the lowest tier of a FunctionNode into bytecode. Codegen logging
shows byte codes as they are being emitted, line number information
and jumps. It also shows the contents of the bytecode stack prior to
each instruction being emitted. This is a good debugging aid. For
example:
[codegen] #41 line:2 (f)_afc824e
[codegen] #42 load symbol x slot=2
[codegen] #43 {1:O} load int 0
[codegen] #44 {2:I O} dynamic_runtime_call GT:ZOI_I args=2 returnType=boolean
[codegen] #45 signature (Ljava/lang/Object;I)Z
[codegen] #46 {1:Z} ifeq ternary_false_5402fe28
[codegen] #47 load symbol x slot=2
[codegen] #48 {1:O} goto ternary_exit_107c1f2f
[codegen] #49 ternary_false_5402fe28
[codegen] #50 load symbol x slot=2
[codegen] #51 {1:O} convert object -> double
[codegen] #52 {1:D} neg
[codegen] #53 {1:D} convert double -> object
[codegen] #54 {1:O} ternary_exit_107c1f2f
[codegen] #55 {1:O} return object
shows a ternary node being generated for the sequence "return x > 0 ?
x : -x"
The first number on the log line is a unique monotonically increasing
emission id per bytecode. There is no guarantee this is the same id
between runs. depending on non deterministic code
execution/compilation, but for small applications it usually is. If
the system variable -Dnashorn.codegen.debug.trace=<x> is set, where x
is a bytecode emission id, a stack trace will be shown as the
particular bytecode is about to be emitted. This can be a quick way to
determine where it comes from without attaching the debugger. "Who
generated that neg?"
The --log=codegen option is equivalent to setting the system variable
"nashorn.codegen.debug" to true.
* lower
The lowering annotates a FunctionNode with symbols for each identifier
and transforms high level constructs into lower level ones, that the
CodeGenerator consumes.
Lower logging typically outputs things like post pass actions,
insertions of casts because symbol types have been changed and type
specialization information. Currently very little info is generated by
this logger. This will probably change.
* access
The --log=access option is equivalent to setting the system variable
"nashorn.callsiteaccess.debug" to true. There are several levels of
the access logger, usually the default level "info" is enough
It is very simple to create your own logger. Use the DebugLogger class
and give the subsystem name as a constructor argument.
* fields
The --log=fields option (at info level) is equivalent to setting the
system variable "nashorn.fields.debug" to true. At the info level it
will only show info about type assumptions that were invalidated. If
the level is set to finest, it will also trace every AccessorProperty
getter and setter in the program, show arguments, return values
etc. It will also show the internal representation of respective field
(Object in the normal case, unless running with the dual field
representation)

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Generate HTML documentation for shell tool. Re-run this tool to regenerate
* html doc when you change options.
*
* Usage:
*
* jjs -scripting genshelldoc.js > shell.html
*/
var Options = Packages.jdk.nashorn.internal.runtime.options.Options;
var title = "Nashorn command line shell tool";
print(<<PREFIX
<html>
<head>
<title>
${title}
</title>
</head>
<body>
<h1>Usage</h1>
<p>
<code>
<b>jjs &lt;options&gt; &lt;script-files&gt; [ -- &lt;script-arguments&gt; ]</b>
</code>
</p>
<h1>${title} options</h1>
<table border="0">
<tr>
<th>name</th>
<th>type</th>
<th>default</th>
<th>description</th>
</tr>
PREFIX);
for each (opt in Options.validOptions) {
var isTimezone = (opt.type == "timezone");
var defValue = opt.defaultValue;
if (defValue == null) {
defValue = "&lt;none&gt;";
}
if (isTimezone) {
// don't output current user's timezone
defValue = "&lt;default-timezone&gt;"
}
print(<<ROW
<tr>
<td><b>${opt.name} ${opt.shortName == null? "" : opt.shortName}</b></td>
<td>${opt.type}</td>
<td>${defValue}</td>
<td>${opt.description}</td>
</tr>
ROW);
}
print(<<SUFFIX
</table>
</body>
</html>
SUFFIX);

224
nashorn/make/Makefile Normal file
View File

@ -0,0 +1,224 @@
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
# Makefile for nashorn: wrapper around Ant build.xml file
#
# On Solaris, the standard 'make' utility will not work with these makefiles.
# This little rule is only understood by Solaris make, and is harmless
# when seen by the GNU make tool. If using Solaris make, this causes the
# make command to fail.
#
SUN_MAKE_TEST:sh = @echo "ERROR: PLEASE USE GNU VERSION OF MAKE"; exit 33
#
# Minimal platform defs
# Need FullPath because we can't rely on gnumake abspath, until we use v3.81
#
SYSTEM_UNAME := $(shell uname)
# Where is unwanted output to be delivered?
# On Windows, MKS uses the special file "NUL", cygwin uses the customary unix file.
ifeq ($(SYSTEM_UNAME), Windows_NT)
DEV_NULL = NUL
else
DEV_NULL = /dev/null
endif
ifneq (,$(findstring CYGWIN,$(SYSTEM_UNAME)))
USING_CYGWIN = true
endif
ifdef USING_CYGWIN
define FullPath
$(shell cygpath -a -s -m $1 2> $(DEV_NULL))
endef
else
define FullPath
$(shell cd $1 2> $(DEV_NULL) && pwd)
endef
endif
#
# Makefile args
#
ifdef QUIET
ANT_OPTIONS += -quiet
endif
ifdef VERBOSE
ANT_OPTIONS += -verbose -debug
endif
ifdef JDK_VERSION
ANT_OPTIONS += -Djdk.version=$(JDK_VERSION)
endif
ifdef FULL_VERSION
ANT_OPTIONS += -Dfull.version='$(FULL_VERSION)' # will contain spaces
endif
ifdef MILESTONE
ifneq ($(MILESTONE),fcs)
ANT_OPTIONS += -Dmilestone=$(MILESTONE)
else
ANT_OPTIONS += -Drelease=$(JDK_VERSION)
endif
endif
ifdef BUILD_NUMBER
ANT_OPTIONS += -Dbuild.number=$(BUILD_NUMBER)
else
ifdef JDK_BUILD_NUMBER
ANT_OPTIONS += -Dbuild.number=$(JDK_BUILD_NUMBER)
endif
endif
ifeq ($(VARIANT), DBG)
ANT_OPTIONS += -Djavac.debug=true
else
ifeq ($(VARIANT), OPT)
ANT_OPTIONS += -Djavac.debug=false
endif
endif
ifeq ($(DEBUG_CLASSFILES), true)
ANT_OPTIONS += -Djavac.debug=true
ANT_OPTIONS += -Ddebug.classfiles=true
endif
# Note: jdk/make/common/Defs.gmk uses LANGUAGE_VERSION (-source NN)
# and the somewhat misnamed CLASS_VERSION (-target NN)
ifdef TARGET_CLASS_VERSION
ANT_OPTIONS += -Djavac.target=$(TARGET_CLASS_VERSION)
else
ifdef JAVAC_TARGET_ARG
ANT_OPTIONS += -Djavac.target=$(JAVAC_TARGET_ARG)
endif
endif
ifdef SOURCE_LANGUAGE_VERSION
ANT_OPTIONS += -Djavac.source=$(SOURCE_LANGUAGE_VERSION)
else
ifdef JAVAC_SOURCE_ARG
ANT_OPTIONS += -Djavac.source=$(JAVAC_SOURCE_ARG)
endif
endif
ifdef ALT_BOOTDIR
ANT_OPTIONS += -Dboot.java.home=$(ALT_BOOTDIR)
ANT_JAVA_HOME = JAVA_HOME=$(ALT_BOOTDIR)
endif
# To facilitate bootstrapping, much of langtools can be compiled with (just)
# a boot JDK. However, some source files need to be compiled against
# new JDK API. In a bootstrap build, an import JDK may not be available,
# so build.xml can also build against the source files in a jdk repo,
# in which case it will automatically generate stub files for the new JDK API.
ifdef JDK_TOPDIR
ANT_OPTIONS += -Dimport.jdk=$(JDK_TOPDIR)
else
ifdef ALT_JDK_TOPDIR
ANT_OPTIONS += -Dimport.jdk=$(ALT_JDK_TOPDIR)
else
ifdef ALT_JDK_IMPORT_PATH
ANT_OPTIONS += -Dimport.jdk=$(ALT_JDK_IMPORT_PATH)
endif
endif
endif
ifdef ALT_OUTPUTDIR
OUTPUTDIR = $(ALT_OUTPUTDIR)
ANT_OPTIONS += -Dbuild.dir=$(ALT_OUTPUTDIR)/build
ANT_OPTIONS += -Ddist.dir=$(ALT_OUTPUTDIR)/dist
else
OUTPUTDIR = ..
endif
#ABS_OUTPUTDIR = $(abspath $(OUTPUTDIR))
ABS_OUTPUTDIR = $(call FullPath,$(OUTPUTDIR))
ANT_TMPDIR = $(ABS_OUTPUTDIR)/build/ant-tmp
ANT_OPTS = ANT_OPTS=-Djava.io.tmpdir='$(ANT_TMPDIR)'
ifdef FINDBUGS_HOME
ANT_OPTIONS += -Dfindbugs.home=$(FINDBUGS_HOME)
endif
ifdef ANT_HOME
ANT = $(ANT_HOME)/bin/ant
ifneq ($(shell test -x $(ANT) && echo OK), OK)
$(error $(ANT) not found -- please update ANT_HOME)
endif
else
ANT = ant
ifneq ($(shell test -x "`which $(ANT)`" && echo OK), OK)
$(error 'ant' not found -- please set ANT_HOME or put 'ant' on your PATH)
endif
endif
# Default target and expected 'do everything' target
# comments docs to avoid too many ASM warnings
# all: test docs
all: test
# Standard make clobber target
clobber: clean
# All ant targets of interest
ANT_TARGETS = clean jar javadoc shelldoc docs test test262 test262parallel # for now
# Create diagnostics log (careful, ant 1.8.0 -diagnostics always does an exit 1)
$(OUTPUTDIR)/build/ant-diagnostics.log:
@mkdir -p $(OUTPUTDIR)/build $(ANT_TMPDIR)
@$(RM) $@
$(ANT_JAVA_HOME) $(ANT_OPTS) $(ANT) -diagnostics > $@ ; \
$(ANT_JAVA_HOME) $(ANT_OPTS) $(ANT) -version >> $@
# Create a make target for each
$(ANT_TARGETS): $(OUTPUTDIR)/build/ant-diagnostics.log
@ mkdir -p $(OUTPUTDIR)/build $(ANT_TMPDIR)
$(ANT_JAVA_HOME) $(ANT_OPTS) $(ANT) $(ANT_OPTIONS) $@
#-------------------------------------------------------------------
#
# Targets for Oracle's internal JPRT build system
CD = cd
ZIP = zip
JPRT_ARCHIVE_BUNDLE=$(ABS_OUTPUTDIR)/$(JPRT_BUILD_FLAVOR)-bundle.zip
jprt_build_product jprt_build_debug jprt_build_fastdebug: all
( $(CD) $(OUTPUTDIR) && \
$(ZIP) -q -r $(JPRT_ARCHIVE_BUNDLE) build dist )
#-------------------------------------------------------------------
# Declare these phony (not filenames)
.PHONY: $(ANT_TARGETS) all clobber \
jprt_build_product jprt_build_debug jprt_build_fastdebug

View File

@ -0,0 +1,348 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
or visit www.oracle.com if you need additional information or have any
questions.
-->
<project name="nashorn-benchmarks" default="all" basedir="..">
<target name="octane-init" depends="jar">
<fileset id="octane-set"
dir="${octane-test-sys-prop.test.js.roots}"
excludes="${octane-test-sys-prop.test.js.exclude.list}">
<include name="**/*.js"/>
</fileset>
<pathconvert pathsep=" " property="octane-tests" refid="octane-set"/>
<property name="extra-arg" value=""/>
</target>
<!-- box2d -->
<target name="box2d-init">
<property name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/box2d.js"/>
</target>
<target name="octane-box2d" depends="jar, box2d-init, octane-verbose">
<antcall target="run-octane"/>
</target>
<target name="octane-box2d-v8" depends="jar, box2d-init, octane-verbose">
<antcall target="run-octane-v8"/>
</target>
<target name="octane-box2d-rhino" depends="jar, box2d-init, octane-verbose">
<antcall target="run-octane-rhino"/>
</target>
<!-- code-load -->
<target name="code-load-init">
<property name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/code-load.js"/>
</target>
<target name="octane-code-load" depends="jar, code-load-init, octane-verbose">
<antcall target="run-octane"/>
</target>
<target name="octane-code-load-v8" depends="jar, code-load-init, octane-verbose">
<antcall target="run-octane-v8"/>
</target>
<target name="octane-code-load-rhino" depends="jar, code-load-init, octane-verbose">
<antcall target="run-octane-rhino"/>
</target>
<!-- crypto -->
<target name="crypto-init">
<property name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/crypto.js"/>
</target>
<target name="octane-crypto" depends="jar, crypto-init, octane-verbose">
<antcall target="run-octane"/>
</target>
<target name="octane-crypto-v8" depends="jar, crypto-init, octane-verbose">
<antcall target="run-octane-v8"/>
</target>
<target name="octane-crypto-rhino" depends="jar, crypto-init, octane-verbose">
<antcall target="run-octane-rhino"/>
</target>
<!-- deltablue -->
<target name="deltablue-init">
<property name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/deltablue.js"/>
</target>
<target name="octane-deltablue" depends="jar, deltablue-init, octane-verbose">
<antcall target="run-octane"/>
</target>
<target name="octane-deltablue-v8" depends="jar, deltablue-init, octane-verbose">
<antcall target="run-octane-v8"/>
</target>
<target name="octane-deltablue-rhino" depends="jar, deltablue-init, octane-verbose">
<antcall target="run-octane-rhino"/>
</target>
<!-- earley-boyer -->
<target name="earley-boyer-init">
<property name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/earley-boyer.js"/>
</target>
<target name="octane-earley-boyer" depends="jar, earley-boyer-init, octane-verbose">
<antcall target="run-octane"/>
</target>
<target name="octane-earley-boyer-v8" depends="jar, earley-boyer-init, octane-verbose">
<antcall target="run-octane-v8"/>
</target>
<target name="octane-earley-boyer-rhino" depends="jar, earley-boyer-init, octane-verbose">
<antcall target="run-octane-rhino"/>
</target>
<!-- gbemu -->
<target name="gbemu-init">
<property name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/gbemu.js"/>
</target>
<target name="octane-gbemu" depends="jar, gbemu-init, octane-verbose">
<antcall target="run-octane"/>
</target>
<target name="octane-gbemu-v8" depends="jar, gbemu-init, octane-verbose">
<antcall target="run-octane-v8"/>
</target>
<target name="octane-gbemu-rhino" depends="jar, gbemu-init, octane-verbose">
<antcall target="run-octane-rhino"/>
</target>
<!-- navier-stokes -->
<target name="navier-stokes-init">
<property name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/navier-stokes.js"/>
</target>
<target name="octane-navier-stokes" depends="jar, navier-stokes-init, octane-verbose">
<antcall target="run-octane"/>
</target>
<target name="octane-navier-stokes-v8" depends="jar, navier-stokes-init, octane-verbose">
<antcall target="run-octane-v8"/>
</target>
<target name="octane-navier-stokes-rhino" depends="jar, navier-stokes-init, octane-verbose">
<antcall target="run-octane-rhino"/>
</target>
<!-- pdfjs -->
<target name="pdfjs-init">
<property name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/pdfjs.js"/>
</target>
<target name="octane-pdfjs" depends="jar, pdfjs-init, octane-verbose">
<antcall target="run-octane"/>
</target>
<target name="octane-pdfjs-v8" depends="jar, pdfjs-init, octane-verbose">
<antcall target="run-octane-v8"/>
</target>
<target name="octane-pdfjs-rhino" depends="jar, pdfjs-init, octane-verbose">
<antcall target="run-octane-rhino"/>
</target>
<!-- raytrace -->
<target name="raytrace-init">
<property name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/raytrace.js"/>
</target>
<target name="octane-raytrace" depends="jar, raytrace-init, octane-verbose">
<antcall target="run-octane"/>
</target>
<target name="octane-raytrace-v8" depends="jar, raytrace-init, octane-verbose">
<antcall target="run-octane-v8"/>
</target>
<target name="octane-raytrace-rhino" depends="jar, raytrace-init, octane-verbose">
<antcall target="run-octane-rhino"/>
</target>
<!-- regexp -->
<target name="regexp-init">
<property name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/regexp.js"/>
</target>
<target name="octane-regexp" depends="jar, regexp-init, octane-verbose">
<antcall target="run-octane"/>
</target>
<target name="octane-regexp-octane-v8" depends="jar, regexp-init, octane-verbose">
<antcall target="run-octane-v8"/>
</target>
<target name="octane-regexp-rhino" depends="jar, regexp-init, octane-verbose">
<antcall target="run-octane-rhino"/>
</target>
<!-- splay -->
<target name="splay-init">
<property name="octane-tests" value="${octane-test-sys-prop.test.js.roots}/splay.js"/>
</target>
<target name="octane-splay" depends="jar, splay-init, octane-verbose">
<antcall target="run-octane"/>
</target>
<target name="octane-splay-v8" depends="jar, splay-init, octane-verbose">
<antcall target="run-octane-v8"/>
</target>
<target name="octane-splay-rhino" depends="jar, splay-init, octane-verbose">
<antcall target="run-octane-rhino"/>
</target>
<target name="octane-verbose">
<property name="extra-arg" value="--verbose"/>
</target>
<!-- run octane benchmarks using Nashorn as runtime -->
<target name="octane" depends="octane-init">
<antcall target="run-octane"/>
</target>
<!-- run octane benchmarks using octane as runtime -->
<target name="octane-v8" depends="octane-init">
<antcall target="run-octane-v8"/>
</target>
<!-- run octane benchmarks using Rhino as runtime -->
<target name="octane-rhino" depends="octane-init">
<antcall target="run-octane-rhino"/>
</target>
<target name="run-octane">
<java classname="${nashorn.shell.tool}"
classpath="${run.test.classpath}"
fork="true"
dir=".">
<jvmarg line="${boot.class.path}"/>
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs.octane}"/>
<arg value="${octane-test-sys-prop.test.js.framework}"/>
<arg value="--"/>
<arg value="${octane-tests}"/>
<arg value="--runtime"/>
<arg value="Nashorn"/>
<arg value="--verbose"/>
<arg value="--iterations 8"/>
<arg value="${extra-arg}"/>
</java>
</target>
<target name="run-octane-v8">
<exec executable="${v8.shell}">
<arg value="${octane-test-sys-prop.test.js.framework}"/>
<arg value="--"/>
<arg value="${octane-tests}"/>
<arg value="--runtime"/>
<arg value="v8"/>
<arg value="--verbose"/>
<arg value="--iterations 8"/>
<arg value="${extra-arg}"/>
</exec>
</target>
<target name="run-octane-rhino">
<java jar="${rhino.jar}"
classpath="${run.test.classpath}"
fork="true"
dir=".">
<jvmarg line="${boot.class.path}"/>
<jvmarg line="${run.test.jvmargs.octane}"/>
<arg value="${octane-test-sys-prop.test.js.framework}"/>
<arg value="${octane-tests}"/>
<arg value="--runtime"/>
<arg value="Rhino"/>
<arg value="--verbose"/>
<arg value="--iterations 8"/>
<arg value="${extra-arg}"/>
</java>
</target>
<!-- run octane with all known runtimes for comparison -->
<target name="octane-all" depends="octane, octane-v8, octane-rhino">
<exec executable="${v8.shell}">
<arg value="${octane-test-sys-prop.test.js.framework}"/>
<arg value="${octane-tests}/"/>
</exec>
</target>
<target name="sunspider-init" depends="jar">
<fileset id="sunspider-set"
dir="${sunspider-test-sys-prop.test.js.roots}"
excludes="${sunspider-test-sys-prop.test.js.exclude.list}">
<include name="**/*.js"/>
</fileset>
<pathconvert pathsep=" " property="sunspider-tests" refid="sunspider-set"/>
</target>
<!-- run sunspider with Nashorn -->
<target name="sunspider" depends="sunspider-init">
<java classname="${nashorn.shell.tool}"
classpath="${run.test.classpath}"
fork="true"
dir=".">
<jvmarg line="${boot.class.path}"/>
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs}"/>
<arg value="-timezone=PST"/>
<arg value="--class-cache-size=50"/>
<arg value="${sunspider-test-sys-prop.test.js.framework}"/>
<arg value="--"/>
<arg value="${sunspider-tests}/"/>
</java>
</target>
<!-- run sunspider with v8 -->
<target name="sunspider-v8" depends="sunspider-init">
<exec executable="${v8.shell}">
<arg value="${sunspider-test-sys-prop.test.js.framework}"/>
<arg value="--"/>
<arg value="${sunspider-tests}/"/>
</exec>
</target>
<!-- run sunspider with Rhino -->
<target name="sunspider-rhino" depends="sunspider-init">
<java jar="${rhino.jar}"
classpath="${run.test.classpath}"
fork="true"
dir=".">
<jvmarg line="${boot.class.path}"/>
<jvmarg line="${run.test.jvmargs}"/>
<arg value="${sunspider-test-sys-prop.test.js.framework}"/>
<arg value="${sunspider-tests}/"/>
</java>
</target>
</project>

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
or visit www.oracle.com if you need additional information or have any
questions.
-->
<project name="build-nasgen" default="run-nasgen" basedir="..">
<description>Builds and runs nasgen.</description>
<import file="build.xml"/>
<target name="build-nasgen" depends="compile-asm">
<ant inheritAll="false" dir="${basedir}/buildtools/nasgen"
antfile="build.xml" target="jar"/>
</target>
<target name="run-nasgen" depends="build-nasgen">
<java classname="${nasgen.tool}" fork="true" failonerror="true">
<classpath>
<pathelement location="${basedir}/jcov2/lib/jcov_j2se_rt.jar"/>
<pathelement location="${basedir}/buildtools/nasgen/dist/nasgen.jar"/>
<pathelement path="${basedir}/build/classes"/>
</classpath>
<arg value="${basedir}/build/classes"/>
<arg value="jdk.nashorn.internal.objects"/>
<arg value="${basedir}/build/classes"/>
</java>
<move todir="${basedir}/build/classes/jdk/nashorn/internal/objects">
<fileset dir="${basedir}/build/classes/jdk/nashorn/internal/objects"/>
<mapper type="glob" from="*.class" to="*.clazz"/>
</move>
</target>
<target name="run-nasgen-eclipse">
<mkdir dir="${basedir}/build/eclipse/.nasgentmp"/>
<java classname="jdk.nashorn.internal.tools.nasgen.Main" fork="true" failonerror="true">
<classpath>
<pathelement location="${basedir}/buildtools/nasgen/dist/nasgen.jar"/>
<pathelement path="${basedir}/build/eclipse"/>
</classpath>
<arg value="${basedir}/build/eclipse"/>
<arg value="jdk.nashorn.internal.objects"/>
<arg value="${basedir}/build/eclipse/.nasgentmp"/>
</java>
<move todir="${basedir}/build/eclipse/jdk/nashorn/internal/objects">
<fileset dir="${basedir}/build/eclipse/.nasgentmp/jdk/nashorn/internal/objects">
<include name="*.class"/>
</fileset>
<mapper type="glob" from="*.class" to="*.clazz"/>
</move>
<delete includeemptydirs="true"><fileset dir="${basedir}/build/eclipse/.nasgentmp" includes="**"/></delete>
<copy todir="${basedir}/build/eclipse/jdk/nashorn/internal/objects" preservelastmodified="true">
<fileset dir="${basedir}/build/eclipse/jdk/nashorn/internal/objects">
<include name="**/*.class"/>
</fileset>
<mapper type="glob" from="*.class" to="*.clazz"/>
</copy>
</target>
<target name="clean-nasgen">
<ant inheritAll="false" dir="${basedir}/buildtools/nasgen"
antfile="build.xml" target="clean"/>
</target>
</project>

355
nashorn/make/build.xml Normal file
View File

@ -0,0 +1,355 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
or visit www.oracle.com if you need additional information or have any
questions.
-->
<project name="nashorn" default="all" basedir="..">
<import file="build-nasgen.xml"/>
<import file="build-benchmark.xml"/>
<target name="init">
<loadproperties srcFile="make/project.properties"/>
<path id="nashorn.ext.path">
<pathelement location="${dist.dir}"/>
</path>
<property name="ext.class.path" value="-Djava.ext.dirs=&quot;${toString:nashorn.ext.path}&quot;"/>
</target>
<target name="prepare" depends="init">
<mkdir dir="${build.dir}"/>
<mkdir dir="${build.classes.dir}"/>
<mkdir dir="${build.classes.dir}/META-INF/services"/>
<mkdir dir="${build.test.classes.dir}"/>
<mkdir dir="${dist.dir}"/>
<mkdir dir="${dist.javadoc.dir}"/>
<!-- check if JDK already has ASM classes -->
<available property="asm.available" classname="jdk.internal.org.objectweb.asm.Type"/>
<!-- check if testng.jar is avaiable -->
<available property="testng.available" file="${file.reference.testng.jar}"/>
</target>
<target name="clean" depends="init, clean-nasgen">
<delete includeemptydirs="true">
<fileset dir="${build.dir}" excludes="${dynalink.dir.name}/**/*" erroronmissingdir="false"/>
</delete>
<delete dir="${dist.dir}"/>
</target>
<target name="clean-dynalink">
<delete dir="${dynalink.dir}"/>
</target>
<target name="clean-all" depends="clean-dynalink, clean">
<delete dir="${build.dir}"/>
</target>
<!-- do it only if ASM is not available -->
<target name="compile-asm" depends="prepare" unless="asm.available">
<javac srcdir="${asm.src.dir}"
destdir="${build.classes.dir}"
excludes="**/optimizer/* **/xml/* **/attrs/*"
source="${javac.source}"
target="${javac.target}"
debug="${javac.debug}"
encoding="${javac.encoding}"
includeantruntime="false"/>
</target>
<target name="check-dynalink-uptodate" depends="init">
<property name="versioned.dynalink.jar" value="${dynalink.dir}/dynalink-${dynalink.jar.version}.jar"/>
<condition property="dynalink.uptodate">
<and>
<available file="${versioned.dynalink.jar}"/>
<filesmatch file1="${versioned.dynalink.jar}" file2="${dynalink.jar}"/>
</and>
</condition>
</target>
<target name="get-dynalink" depends="check-dynalink-uptodate" unless="dynalink.uptodate">
<mkdir dir="${dynalink.dir}"/>
<!-- Delete previous snapshots, if any -->
<delete>
<fileset dir="${dynalink.dir}" includes="*"/>
</delete>
<property name="dynalink.download.base.url" value="http://oss.sonatype.org/content/repositories/${dynalink.version.type}s/org/dynalang/dynalink/${dynalink.version}/dynalink-${dynalink.jar.version}"/>
<get src="${dynalink.download.base.url}.jar" dest="${versioned.dynalink.jar}" usetimestamp="true"/>
<get src="${dynalink.download.base.url}-sources.jar" dest="${dynalink.dir}/dynalink-sources.jar" usetimestamp="true"/>
<copy file="${versioned.dynalink.jar}" tofile="${dynalink.jar}" overwrite="true"/>
</target>
<target name="compile" depends="compile-asm, get-dynalink" description="Compiles nashorn">
<javac srcdir="${src.dir}"
destdir="${build.classes.dir}"
classpath="${javac.classpath}"
source="${javac.source}"
target="${javac.target}"
debug="${javac.debug}"
encoding="${javac.encoding}"
includeantruntime="false">
<compilerarg value="-Xlint:unchecked"/>
<compilerarg value="-Xlint:deprecation"/>
<compilerarg value="-XDignore.symbol.file"/>
</javac>
<copy todir="${build.classes.dir}/META-INF/services">
<fileset dir="${meta.inf.dir}/services/"/>
</copy>
<copy todir="${build.classes.dir}/jdk/nashorn/api/scripting/resources">
<fileset dir="${src.dir}/jdk/nashorn/api/scripting/resources/"/>
</copy>
<copy todir="${build.classes.dir}/jdk/nashorn/internal/runtime/resources">
<fileset dir="${src.dir}/jdk/nashorn/internal/runtime/resources/"/>
</copy>
<copy todir="${build.classes.dir}/jdk/nashorn/tools/resources">
<fileset dir="${src.dir}/jdk/nashorn/tools/resources/"/>
</copy>
<echo message="full=${nashorn.fullversion}" file="${build.classes.dir}/jdk/nashorn/internal/runtime/resources/version.properties"/>
<echo file="${build.classes.dir}/jdk/nashorn/internal/runtime/resources/version.properties" append="true">${line.separator}</echo>
<echo message="release=${nashorn.version}" file="${build.classes.dir}/jdk/nashorn/internal/runtime/resources/version.properties" append="true"/>
</target>
<target name="jar" depends="compile, run-nasgen" description="Creates nashorn.jar">
<jar jarfile="${dist.jar}" manifest="${meta.inf.dir}/MANIFEST.MF" index="true" filesetmanifest="merge">
<fileset dir="${build.classes.dir}"/>
<zipfileset src="${dynalink.jar}"/>
<manifest>
<attribute name="Archiver-Version" value="n/a"/>
<attribute name="Build-Jdk" value="${java.runtime.version}"/>
<attribute name="Built-By" value="n/a"/>
<attribute name="Created-By" value="Ant jar task"/>
<section name="jdk/nashorn/">
<attribute name="Implementation-Title" value="${nashorn.product.name}"/>
<attribute name="Implementation-Version" value="${nashorn.version}"/>
</section>
</manifest>
</jar>
</target>
<target name="javadoc" depends="compile-asm">
<javadoc destdir="${dist.javadoc.dir}" private="yes" use="yes" overview="src/overview.html" windowtitle="${nashorn.product.name} ${nashorn.version}">
<classpath>
<pathelement location="${build.classes.dir}"/>
<pathelement location="${dynalink.jar}"/>
</classpath>
<fileset dir="${src.dir}" includes="**/*.java"/>
<link href="http://docs.oracle.com/javase/7/docs/api"/>
<link href="http://szegedi.github.com/dynalink/0.4/javadoc"/>
</javadoc>
</target>
<!-- generate shell.html for shell tool documentation -->
<target name="shelldoc" depends="jar">
<java classname="${nashorn.shell.tool}" dir="${basedir}" output="${dist.dir}/shell.html" failonerror="true" fork="true">
<jvmarg line="${ext.class.path}"/>
<arg value="-scripting"/>
<arg value="docs/genshelldoc.js"/>
</java>
</target>
<!-- generate all docs -->
<target name="docs" depends="javadoc, shelldoc"/>
<!-- create .zip and .tar.gz for nashorn binaries and scripts. -->
<target name="dist" depends="jar">
<zip destfile="${build.zip}" basedir=".."
excludes="nashorn/bin/*.sh" includes="nashorn/bin/** nashorn/dist/**"/>
<tar destfile="${build.gzip}" basedir=".." compression="gzip"
excludes="nashorn/bin/*.sh" includes="nashorn/bin/** nashorn/dist/**"/>
</target>
<target name="compile-test" depends="compile, run-nasgen" if="testng.available">
<!-- testng task -->
<taskdef name="testng" classname="org.testng.TestNGAntTask"
classpath="${file.reference.testng.jar}"/>
<javac srcdir="${test.src.dir}"
destdir="${build.test.classes.dir}"
classpath="${javac.test.classpath}"
source="${javac.source}"
target="${javac.target}"
debug="${javac.debug}"
encoding="${javac.encoding}"
includeantruntime="false"/>
</target>
<target name="generate-policy-file">
<!-- Generating nashorn.policy file -->
<echo message="grant codeBase &quot;file:/${basedir}/dist/nashorn.jar&quot; {" file="${build.dir}/nashorn.policy"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="permission java.security.AllPermission;" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="};" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="grant codeBase &quot;file:/${basedir}/build/test/classes&quot; {" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message=" permission java.security.AllPermission;" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="};" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="grant codeBase &quot;file:/${basedir}/${file.reference.testng.jar}&quot; {" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message=" permission java.security.AllPermission;" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="};" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="grant codeBase &quot;file:/${basedir}/test/script/basic/*&quot; {" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message=" permission java.security.AllPermission;" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="};" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="grant codeBase &quot;file:/${basedir}/test/perf/*&quot; {" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message=" permission java.security.AllPermission;" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="};" file="${build.dir}/nashorn.policy" append="true"/>
<echo message="" file="${build.dir}/nashorn.policy" append="true"/>
<replace file="${build.dir}/nashorn.policy"><replacetoken>\</replacetoken><replacevalue>/</replacevalue></replace> <!--hack for Windows - to make URLs with normal path separators -->
<replace file="${build.dir}/nashorn.policy"><replacetoken>//</replacetoken><replacevalue>/</replacevalue></replace> <!--hack for Unix - to avoid leading // in URLs -->
</target>
<target name="check-external-tests">
<available file="${test.external.dir}/prototype" property="test-sys-prop.external.prototype"/>
<available file="${test.external.dir}/sunspider" property="test-sys-prop.external.sunspider"/>
<available file="${test.external.dir}/underscore" property="test-sys-prop.external.underscore"/>
<available file="${test.external.dir}/octane" property="test-sys-prop.external.octane"/>
<available file="${test.external.dir}/yui" property="test-sys-prop.external.yui"/>
<available file="${test.external.dir}/jquery" property="test-sys-prop.external.jquery"/>
<available file="${test.external.dir}/test262" property="test-sys-prop.external.test262"/>
</target>
<target name="check-testng" unless="testng.available">
<echo message="WARNING: TestNG not available, will not run tests. Please copy testng.jar under test/lib directory."/>
</target>
<target name="test" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
<java classname="${nashorn.shell.tool}" fork="true" dir="${test.script.dir}/representations" output="${build.dir}/output1.log" error="${build.dir}/err.log">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="-Dnashorn.fields.dual=true"/>
<arg value="NASHORN-592a.js"/>
</java>
<java classname="${nashorn.shell.tool}" fork="true" dir="${test.script.dir}/representations" output="${build.dir}/output2.log" error="${build.dir}/err.log">
<jvmarg line="${ext.class.path}"/>
<arg value="NASHORN-592a.js"/>
</java>
<condition property="representation-ok">
<filesmatch file1="${build.dir}/output1.log" file2="${build.dir}/output2.log"/>
</condition>
<fail unless="representation-ok">Representation test failed - output differs!</fail>
<fileset id="test.classes" dir="${build.test.classes.dir}">
<include name="**/access/*Test.class"/>
<include name="**/api/scripting/*Test.class"/>
<include name="**/codegen/*Test.class"/>
<include name="**/parser/*Test.class"/>
<include name="**/runtime/*Test.class"/>
<include name="**/framework/*Test.class"/>
</fileset>
<testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs} ${run.test.jvmsecurityargs}"/>
<propertyset>
<propertyref prefix="test-sys-prop."/>
<mapper from="test-sys-prop.*" to="*" type="glob"/>
</propertyset>
<classpath>
<pathelement path="${run.test.classpath}"/>
</classpath>
</testng>
</target>
<target name="test-basicparallel" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file">
<!-- use just build.test.classes.dir to avoid referring to TestNG -->
<java classname="${parallel.test.runner}" dir="${basedir}" classpath="${build.test.classes.dir}" failonerror="true" fork="true">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs} ${run.test.jvmsecurityargs}"/>
<syspropertyset>
<propertyref prefix="test-sys-prop."/>
<mapper type="glob" from="test-sys-prop.*" to="*"/>
</syspropertyset>
</java>
</target>
<target name="test262" depends="jar, check-testng, check-external-tests, compile-test" if="testng.available">
<fileset id="test.classes" dir="${build.test.classes.dir}">
<include name="**/framework/*Test.class"/>
</fileset>
<testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs}"/>
<propertyset>
<propertyref prefix="test262-test-sys-prop."/>
<mapper from="test262-test-sys-prop.*" to="*" type="glob"/>
</propertyset>
<classpath>
<pathelement path="${run.test.classpath}"/>
</classpath>
</testng>
</target>
<target name="test262parallel" depends="test262-parallel"/>
<target name="test262-parallel" depends="jar, check-testng, check-external-tests, compile-test" if="testng.available">
<!-- use just build.test.classes.dir to avoid referring to TestNG -->
<java classname="${parallel.test.runner}" dir="${basedir}" classpath="${build.test.classes.dir}" fork="true">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs}"/>
<syspropertyset>
<propertyref prefix="test262-test-sys-prop."/>
<mapper type="glob" from="test262-test-sys-prop.*" to="*"/>
</syspropertyset>
</java>
</target>
<target name="all" depends="test, docs"
description="Build, test and generate docs for nashorn"/>
<target name="run" depends="jar"
description="Run the shell with a sample script">
<java classname="${nashorn.shell.tool}" fork="true" dir="samples">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs}"/>
<arg value="-dump-on-error"/>
<arg value="test.js"/>
</java>
</target>
<target name="debug" depends="jar"
description="Debug the shell with a sample script">
<java classname="${nashorn.shell.tool}" fork="true" dir="samples">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs}"/>
<arg value="--print-code"/>
<arg value="--verify-code"/>
<arg value="--print-symbols"/>
<jvmarg value="-Dnashorn.codegen.debug=true"/>
<arg value="test.js"/>
</java>
</target>
</project>

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
or visit www.oracle.com if you need additional information or have any
questions.
-->
<project basedir=".." name="nashorn-IDE">
<property file="nbproject/nbjdk.properties"/>
<property location="${netbeans.user}/build.properties" name="user.properties.file"/>
<property file="${user.properties.file}"/>
<import file="jdk.xml"/>
<import file="${basedir}/build-init.xml"/>
<!-- TODO: edit the following target according to your needs -->
<!-- (more info: http://www.netbeans.org/kb/articles/freeform-config.html#runsingle) -->
<target depends="-jdk-init, init" name="debug-selected-file-in-src">
<fail unless="debug.class">Must set property 'debug.class'</fail>
<ant antfile="build.xml" inheritall="false" target="jar"/>
<nbjpdastart addressproperty="jpda.address" name="nashorn" transport="dt_socket">
<classpath path="${run.test.classpath}"/>
</nbjpdastart>
<java classname="${debug.class}" fork="false">
<classpath path="${run.test.classpath}"/>
<jvmarg line="${boot.class.path}"/>
<jvmarg value="-Xdebug"/>
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
<jvmarg line="${run.test.jvmargs}"/>
<arg value="${debug.class}"/>
</java>
</target>
<!-- TODO: edit the following target according to your needs -->
<!-- (more info: http://www.netbeans.org/kb/articles/freeform-config.html#runsingle) -->
<target depends="-jdk-init, init" name="run-selected-file-in-src">
<fail unless="run.class">Must set property 'run.class'</fail>
<ant antfile="build.xml" inheritall="false" target="jar"/>
<java classname="${run.class}" failonerror="true" fork="false">
<classpath path="${run.test.classpath}"/>
<jvmarg line="${boot.class.path}"/>
<jvmarg line="${run.test.jvmargs}"/>
<arg value="${run.class}"/>
</java>
</target>
</project>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
or visit www.oracle.com if you need additional information or have any
questions.
-->
<project basedir="../.." name="nashorn-IDE">
<import file="../build.xml"/>
<!-- TODO: edit the following target according to your needs -->
<!-- (more info: http://www.netbeans.org/kb/articles/freeform-config.html#debugj2se) -->
<target depends="jar" description="Run the shell with a sample script" name="debug-nb">
<nbjpdastart addressproperty="jpda.address" name="nashorn" transport="dt_socket">
<classpath path="${run.test.classpath}"/>
</nbjpdastart>
<java classname="jdk.nashorn.tools.Shell" classpath="${run.test.classpath}" dir="samples" fork="true">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs}"/>
<arg value="test.js"/>
<jvmarg value="-Xdebug"/>
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
</java>
</target>
</project>

View File

@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8"?><project name="jdk" basedir=".">
<!--
Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
or visit www.oracle.com if you need additional information or have any
questions.
-->
<description>
Permits selection of a JDK to use when building and running project.
See: http://www.netbeans.org/issues/show_bug.cgi?id=64160
</description>
<target name="-jdk-pre-preinit">
<condition property="nbjdk.active-or-nbjdk.home">
<or>
<and>
<isset property="nbjdk.active"/>
<not>
<equals arg1="${nbjdk.active}" arg2="default_platform"/>
</not>
</and>
<and>
<isset property="nbjdk.home"/>
<not>
<isset property="nbjdk.home.defaulted"/>
</not>
</and>
</or>
</condition>
</target>
<target xmlns:common="http://java.netbeans.org/freeform/jdk.xml" name="-jdk-preinit" depends="-jdk-pre-preinit" if="nbjdk.active-or-nbjdk.home">
<macrodef name="property" uri="http://java.netbeans.org/freeform/jdk.xml">
<attribute name="name"/>
<attribute name="value"/>
<sequential>
<property name="@{name}" value="${@{value}}"/>
</sequential>
</macrodef>
<common:property name="nbjdk.home" value="platforms.${nbjdk.active}.home"/>
<common:property name="nbjdk.javac.tmp" value="platforms.${nbjdk.active}.javac"/>
<condition property=".exe" value=".exe">
<os family="windows"/>
</condition>
<property name=".exe" value=""/>
<condition property="nbjdk.javac" value="${nbjdk.home}/bin/javac${.exe}">
<equals arg1="${nbjdk.javac.tmp}" arg2="$${platforms.${nbjdk.active}.javac}"/>
</condition>
<property name="nbjdk.javac" value="${nbjdk.javac.tmp}"/>
<common:property name="nbjdk.java.tmp" value="platforms.${nbjdk.active}.java"/>
<condition property="nbjdk.java" value="${nbjdk.home}/bin/java${.exe}">
<equals arg1="${nbjdk.java.tmp}" arg2="$${platforms.${nbjdk.active}.java}"/>
</condition>
<property name="nbjdk.java" value="${nbjdk.java.tmp}"/>
<common:property name="nbjdk.javadoc.tmp" value="platforms.${nbjdk.active}.javadoc"/>
<condition property="nbjdk.javadoc" value="${nbjdk.home}/bin/javadoc${.exe}">
<equals arg1="${nbjdk.javadoc.tmp}" arg2="$${platforms.${nbjdk.active}.javadoc}"/>
</condition>
<property name="nbjdk.javadoc" value="${nbjdk.javadoc.tmp}"/>
<common:property name="nbjdk.bootclasspath.tmp" value="platforms.${nbjdk.active}.bootclasspath"/>
<condition property="nbjdk.bootclasspath" value="${nbjdk.home}/jre/lib/rt.jar">
<equals arg1="${nbjdk.bootclasspath.tmp}" arg2="$${platforms.${nbjdk.active}.bootclasspath}"/>
</condition>
<property name="nbjdk.bootclasspath" value="${nbjdk.bootclasspath.tmp}"/>
<condition property="nbjdk.valid">
<and>
<available file="${nbjdk.home}" type="dir"/>
<available file="${nbjdk.javac}" type="file"/>
<available file="${nbjdk.java}" type="file"/>
<available file="${nbjdk.javadoc}" type="file"/>
</and>
</condition>
<echo level="verbose">nbjdk.active=${nbjdk.active} nbjdk.home=${nbjdk.home} nbjdk.java=${nbjdk.java} nbjdk.javac=${nbjdk.javac} nbjdk.javadoc=${nbjdk.javadoc} nbjdk.bootclasspath=${nbjdk.bootclasspath} nbjdk.valid=${nbjdk.valid} have-jdk-1.4=${have-jdk-1.4} have-jdk-1.5=${have-jdk-1.5}</echo>
</target>
<target name="-jdk-warn" depends="-jdk-preinit" if="nbjdk.active-or-nbjdk.home" unless="nbjdk.valid">
<property name="jdkhome.presumed" location="${java.home}/.."/>
<echo level="warning">Warning: nbjdk.active=${nbjdk.active} or nbjdk.home=${nbjdk.home} is an invalid Java platform; ignoring and using ${jdkhome.presumed}</echo>
</target>
<target name="-jdk-presetdef-basic" depends="-jdk-preinit" if="nbjdk.valid" unless="nbjdk.presetdef.basic.done">
<macrodef name="javac-presetdef">
<attribute name="javacval"/>
<sequential>
<presetdef name="javac">
<javac fork="yes" executable="@{javacval}"/>
</presetdef>
</sequential>
</macrodef>
<javac-presetdef javacval="${nbjdk.javac}"/>
<macrodef name="java-presetdef">
<attribute name="javaval"/>
<sequential>
<presetdef name="java">
<java fork="yes" jvm="@{javaval}"/>
</presetdef>
</sequential>
</macrodef>
<java-presetdef javaval="${nbjdk.java}"/>
<macrodef name="javadoc-presetdef">
<attribute name="javadocval"/>
<sequential>
<presetdef name="javadoc">
<javadoc executable="@{javadocval}"/>
</presetdef>
</sequential>
</macrodef>
<javadoc-presetdef javadocval="${nbjdk.javadoc}"/>
<macrodef name="junit-presetdef">
<attribute name="javaval"/>
<sequential>
<presetdef name="junit">
<junit fork="yes" jvm="@{javaval}"/>
</presetdef>
</sequential>
</macrodef>
<junit-presetdef javaval="${nbjdk.java}"/>
<property name="nbjdk.presetdef.basic.done" value="true"/>
</target>
<target name="-jdk-presetdef-nbjpdastart" depends="-jdk-preinit" if="nbjdk.valid" unless="nbjdk.presetdef.nbjpdastart.done">
<macrodef name="nbjpdastart-presetdef">
<attribute name="bootcpval"/>
<sequential>
<presetdef name="nbjpdastart">
<nbjpdastart>
<bootclasspath>
<path path="@{bootcpval}"/>
</bootclasspath>
</nbjpdastart>
</presetdef>
</sequential>
</macrodef>
<nbjpdastart-presetdef bootcpval="${nbjdk.bootclasspath}"/>
<property name="nbjdk.presetdef.nbjpdastart.done" value="true"/>
</target>
<target name="-jdk-default" unless="nbjdk.active-or-nbjdk.home">
<property name="java.home.parent" location="${java.home}/.."/>
<condition property="nbjdk.home" value="${java.home.parent}">
<available file="${java.home.parent}/lib/tools.jar" type="file"/>
</condition>
<condition property="nbjdk.home" value="${java.home}">
<available file="${java.home}/lib/tools.jar" type="file"/>
</condition>
<condition property="nbjdk.home" value="/Library/Java/Home">
<available file="/Library/Java/Home" type="dir"/>
</condition>
<property name="nbjdk.home" location="${java.home.parent}"/>
<property name="nbjdk.home.defaulted" value="true"/>
</target>
<target name="-jdk-init" depends="-jdk-preinit,-jdk-warn,-jdk-presetdef-basic,-jdk-default"/>
</project>

View File

@ -0,0 +1,24 @@
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
nbjdk.active=JDK_1.8

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
or visit www.oracle.com if you need additional information or have any
questions.
-->
<project basedir="..">
<property file="../global.properties"/>
<property file="nbproject/nbjdk.properties"/>
<property location="${netbeans.user}/build.properties" name="user.properties.file"/>
<property file="${user.properties.file}"/>
<import file="jdk.xml"/>
<target depends="-jdk-init" name="jar">
<ant inheritall="false" target="jar"/>
</target>
<target depends="-jdk-init" name="clean">
<ant inheritall="false" target="clean"/>
</target>
<target depends="-jdk-init" name="javadoc">
<ant inheritall="false" target="javadoc"/>
</target>
<target depends="-jdk-init" name="test">
<ant inheritall="false" target="test"/>
</target>
<target depends="-jdk-init" name="run">
<ant inheritall="false" target="run"/>
</target>
<target depends="-jdk-init,-jdk-presetdef-nbjpdastart" name="debug-nb">
<ant antfile="nbproject/ide-targets.xml" inheritall="false" target="debug-nb"/>
</target>
</project>

View File

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License version 2 only, as
published by the Free Software Foundation.
This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
version 2 for more details (a copy is included in the LICENSE file that
accompanied this code).
You should have received a copy of the GNU General Public License version
2 along with this work; if not, write to the Free Software Foundation,
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
or visit www.oracle.com if you need additional information or have any
questions.
-->
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.ant.freeform</type>
<configuration>
<general-data xmlns="http://www.netbeans.org/ns/freeform-project/1">
<name>nashorn</name>
</general-data>
<general-data xmlns="http://www.netbeans.org/ns/freeform-project/2">
<!-- Do not use Project Properties customizer when editing this file manually. -->
<name>nashorn</name>
<properties/>
<folders>
<source-folder>
<label>nashorn</label>
<location>.</location>
<encoding>UTF-8</encoding>
</source-folder>
<source-folder>
<label>../src</label>
<location>../src</location>
</source-folder>
<source-folder>
<label>../test/src</label>
<location>../test/src</location>
</source-folder>
<source-folder>
<label>../buildtools/nasgen/src</label>
<location>../buildtools/nasgen/src</location>
</source-folder>
<source-folder>
<label>../test/src</label>
<type>java</type>
<location>../test/src</location>
<encoding>UTF-8</encoding>
</source-folder>
<source-folder>
<label>../src</label>
<type>java</type>
<location>../src</location>
<encoding>UTF-8</encoding>
</source-folder>
<source-folder>
<label>../buildtools/nasgen/src</label>
<type>java</type>
<location>../buildtools/nasgen/src</location>
<encoding>UTF-8</encoding>
</source-folder>
</folders>
<ide-actions>
<action name="build">
<script>nbproject/nbjdk.xml</script>
<target>jar</target>
</action>
<action name="clean">
<script>nbproject/nbjdk.xml</script>
<target>clean</target>
</action>
<action name="javadoc">
<script>nbproject/nbjdk.xml</script>
<target>javadoc</target>
</action>
<action name="test">
<script>nbproject/nbjdk.xml</script>
<target>test</target>
</action>
<action name="rebuild">
<script>nbproject/nbjdk.xml</script>
<target>clean</target>
<target>jar</target>
</action>
<action name="run">
<script>nbproject/nbjdk.xml</script>
<target>run</target>
</action>
<action name="debug">
<script>nbproject/nbjdk.xml</script>
<target>debug-nb</target>
</action>
<action name="debug.single">
<script>nbproject/ide-file-targets.xml</script>
<target>debug-selected-file-in-src</target>
<context>
<property>debug.class</property>
<folder>test/src</folder>
<pattern>\.java$</pattern>
<format>java-name</format>
<arity>
<one-file-only/>
</arity>
</context>
</action>
<action name="run.single">
<script>nbproject/ide-file-targets.xml</script>
<target>run-selected-file-in-src</target>
<context>
<property>run.class</property>
<folder>test/src</folder>
<pattern>\.java$</pattern>
<format>java-name</format>
<arity>
<one-file-only/>
</arity>
</context>
</action>
</ide-actions>
<view>
<items>
<source-folder style="packages">
<label>../test/src</label>
<location>../test/src</location>
</source-folder>
<source-folder style="packages">
<label>../src</label>
<location>../src</location>
</source-folder>
<source-folder style="packages">
<label>../buildtools/nasgen/src</label>
<location>../buildtools/nasgen/src</location>
</source-folder>
<source-file>
<location>build.xml</location>
</source-file>
</items>
<context-menu>
<ide-action name="build"/>
<ide-action name="rebuild"/>
<ide-action name="clean"/>
<ide-action name="javadoc"/>
<ide-action name="run"/>
<ide-action name="test"/>
<ide-action name="debug"/>
</context-menu>
</view>
<subprojects/>
</general-data>
<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/3">
<compilation-unit>
<package-root>../test/src</package-root>
<unit-tests/>
<classpath mode="compile">../test/lib/testng.jar:../build/classes:../src</classpath>
<source-level>1.7</source-level>
</compilation-unit>
<compilation-unit>
<package-root>../src</package-root>
<classpath mode="compile">../build/dynalink/dynalink.jar</classpath>
<source-level>1.7</source-level>
</compilation-unit>
<compilation-unit>
<package-root>../buildtools/nasgen/src</package-root>
<classpath mode="compile">../build/classes:../src</classpath>
<source-level>1.7</source-level>
</compilation-unit>
</java-data>
</configuration>
</project>

View File

@ -0,0 +1,223 @@
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
application.title=nashorn
# source and target levels
build.compiler=modern
javac.source=1.7
javac.target=1.7
# nashorn version information
nashorn.version=0.1
nashorn.fullversion=0.1
nashorn.product.name=Oracle Nashorn
# This directory is removed when the project is cleaned:
build.dir=build
build.classes.dir=${build.dir}/classes
build.zip=${build.dir}/nashorn.zip
build.gzip=${build.dir}/nashorn.tar.gz
# nashorn Shell tool
nashorn.shell.tool=jdk.nashorn.tools.Shell
# nasgen tool
nasgen.tool=jdk.nashorn.internal.tools.nasgen.Main
# parallel test runner tool
parallel.test.runner=jdk.nashorn.internal.test.framework.ParallelTestRunner
# test classes directory
build.test.classes.dir=${build.dir}/test/classes
# test results directory
build.test.results.dir=${build.dir}/test/reports
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=${dist.dir}/nashorn.jar
dist.javadoc.dir=${dist.dir}/javadoc
# directory where asm project lives
asm.dir=../asm
asm.src.dir=${asm.dir}/src
# jars refererred
file.reference.testng.jar=test/lib/testng.jar
# Set testng verbose level
# From TestNG docs: "the verbosity level (0 to 10 where 10 is most detailed)
# Actually, this is a lie: you can specify -1 and this will put TestNG in
# debug mode (no longer slicing off stack traces and all)."
testng.verbose=2
# TestNG listeners - we want to replace TestNG's own JUnit
# reporter, but want everything else provided by default
# Unfortunately, we've to clone the other default reporters here.
testng.listeners=\
org.testng.reporters.SuiteHTMLReporter, \
org.testng.reporters.jq.Main, \
org.testng.reporters.FailedReporter, \
org.testng.reporters.XMLReporter \
org.testng.reporters.EmailableReporter, \
jdk.nashorn.internal.test.framework.JSJUnitReportReporter
# Define the version of Dynalink that is used. Version types are either
# 'snapshot' or 'release'. When it is 'snapshot', the version must have
# "-SNAPSHOT" suffix and the jar version will have a timestamp in it. When
# it's 'release', the version has no suffix, and the jar version is
# identical to version - fun with Maven central.
dynalink.version=0.5-SNAPSHOT
dynalink.version.type=snapshot
dynalink.jar.version=0.5-20121218.140128-11
dynalink.dir.name=dynalink
dynalink.dir=build/${dynalink.dir.name}
dynalink.jar=${dynalink.dir}/dynalink.jar
javac.debug=true
javac.encoding=ascii
javac.classpath=\
${build.classes.dir}:\
${dynalink.jar}
javac.test.classpath=\
${build.classes.dir}:\
${build.test.classes.dir}:\
${file.reference.testng.jar}
meta.inf.dir=${src.dir}/META-INF
run.classpath=\
${build.classes.dir}
# test scripts to run
test.dir=test
test.script.dir=test/script
test.basic.dir=test/script/basic
test.error.dir=test/script/error
test.sandbox.dir=test/script/sandbox
test.external.dir=test/script/external
test262.dir=${test.external.dir}/test262
test262.suite.dir=${test262.dir}/test/suite
test-sys-prop.test.dir=${test.dir}
test-sys-prop.test.js.roots=${test.basic.dir} ${test.error.dir} ${test.sandbox.dir}
test-sys-prop.test262.suite.dir=${test262.suite.dir}
test-sys-prop.es5conform.testcases.dir=${test.external.dir}/ES5Conform/TestCases
test-sys-prop.test.basic.dir=${test.basic.dir}
# framework root for our script tests
test-sys-prop.test.js.framework=${test.script.dir}/assert.js
# Control the verbosity of ParserTest
test-sys-prop.parsertest.verbose=false
# turn on/off scripting mode for parser tests
test-sys-prop.parsertest.scripting=true
# turn on/off test262 scripts for parser tests
test-sys-prop.parsertest.test262=false
# Control the verbosity of the CompilerTest
test-sys-prop.compilertest.verbose=false
# turn on/off scripting mode for compiler tests
test-sys-prop.compilertest.scripting=true
# turn on/off test262 scripts for compiler tests
test-sys-prop.compilertest.test262=false
# test directory to be excluded.
test-sys-prop.test.js.exclude.dir=${test.script.dir}/currently-failing ${test.external.dir}
# run everything that's js in here, without checking file headers for test annotations
test-sys-prop.test.js.unchecked.dir=${test262.dir}
# test root for octane
octane-test-sys-prop.test.js.roots=${test.external.dir}/octane/benchmarks
# framework root for octane
octane-test-sys-prop.test.js.framework=${test.basic.dir}/run-octane.js
# list of tests to be excluded
octane-test-sys-prop.test.js.exclude.list=base.js
# test root for sunspider
sunspider-test-sys-prop.test.js.roots=${test.external.dir}/sunspider/
# framework root for sunspider
sunspider-test-sys-prop.test.js.framework=${test.basic.dir}/runsunspider.js
# list of tests to be excluded
sunspider-test-sys-prop.test.js.exclude.list=
# execute our script tests in shared nashorn context or not?
test-sys-prop.test.js.shared.context=false
# execute test262 tests in shared nashorn context or not?
test262-test-sys-prop.test.js.shared.context=true
# test262 test root
test262-test-sys-prop.test.js.roots=${test262.suite.dir}
# test262 enable/disable strict mode tests
test262-test-sys-prop.test.js.enable.strict.mode=true
# file containing test262 tests to be excluded
# test262-test-sys-prop.test.js.excludes.file=${test262.dir}/test/config/excludelist.xml
# list of test262 test dirs to be excluded
test262-test-sys-prop.test.js.exclude.dir=\
${test262.suite.dir}/intl402/
# test262 test frameworks
test262-test-sys-prop.test.js.framework=\
-timezone=PST \
${test.script.dir}/test262.js \
${test262.dir}/test/harness/framework.js \
${test262.dir}/test/harness/sta.js
run.test.classpath=\
${file.reference.testng.jar}:\
${build.test.classes.dir}
src.dir=src
test.src.dir=test/src
# -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
# add '-Dtest.js.outofprocess' to run each test in a new sub-process
run.test.jvmargs=-server -Xmx3G -XX:-TieredCompilation -esa -ea -Dnashorn.debug=true -Dfile.encoding=UTF-8
#-XX:+HeapDumpOnOutOfMemoryError -XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
run.test.jvmargs.octane=-Xms2G -Xmx2G ${run.test.jvmargs}
run.test.jvmsecurityargs=-Xverify:all -Djava.security.manager -Djava.security.policy=${basedir}/build/nashorn.policy
# path of rhino.jar for benchmarks
rhino.jar=
v8.shell=d8
#path to rhino jar file
octaneperf-sys-prop.rhino.jar=${rhino.jar}
#timeout for performance tests in minutes
octaneperf-sys-prop.timeout.value=10

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* This file can be run along with any script you want to run
* to print aggregate stat counters from nashorn.
*
* Usage: jjs <your-file.js> counters.js
*/
Debug.dumpCounters();

49
nashorn/samples/letter.js Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Demonstrates "heredoc" feature with "scripting" mode.
*
* Usage: jjs -scripting letter.js -- <sender> <recipient>
*/
# This is shell-style line comment
var obj = { sender: $ARG[0], recipient: $ARG[1] };
// JavaScript style line comment is ok too.
print(<<EOF);
Dear ${obj.recipient},
I wish you all the best.
Regards,
${obj.sender}
EOF

43
nashorn/samples/parser.js Normal file
View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Simple sample demonstrating parser API.
*/
load("nashorn:parser.js");
try {
var json = parse("print('hello')");
print(JSON.stringify(json));
} catch (e) {
print(e);
}

78
nashorn/samples/shell.js Normal file
View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* This is a simple shell tool in JavaScript.
*
* Runs any operating system command using Java "exec". When "eval" command is
* used, evaluates argument(s) as JavaScript code.
*/
var imports = new JavaImporter(java.io, java.lang, java.util);
function prompt() {
java.lang.System.out.print(">");
}
with (imports) {
var reader = new BufferedReader(new InputStreamReader(System["in"]));
var line = null;
prompt();
while ((line = reader.readLine()) != null) {
if (line != "") {
var args = line.split(" ");
try {
if (args[0] == "eval") {
var code = line.substring("eval".length);
var res = eval(code);
if (res != undefined) {
print(res);
}
} else {
var argList = new ArrayList();
for (i in args) { argList.add(args[i]); }
var procBuilder = new ProcessBuilder(argList);
procBuilder.redirectErrorStream();
var proc = procBuilder.start();
var out = new BufferedReader(new InputStreamReader(proc.getInputStream()));
var line = null;
while ((line = out.readLine()) != null) {
System.out.println(line);
}
proc.waitFor();
}
} catch (e) {
print(e);
}
}
prompt();
}
}

32
nashorn/samples/test.js Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
print("Hello World");

55
nashorn/samples/uniq.js Normal file
View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* Prints unique lines from a given file.
*/
if (arguments.length != 1) {
print("Usage: jjs uniq.js -- <file>");
java.lang.System.exit(1);
}
var imports = new JavaImporter(java.io);
var uniqueLines = {};
with (imports) {
var reader = new BufferedReader(new FileReader(arguments[0]));
while ((line = reader.readLine()) != null) {
// using a JS object as a map...
uniqueLines[line] = true;
}
}
// now print the collected lines
for (i in uniqueLines) {
print(i);
}

View File

@ -0,0 +1,5 @@
Manifest-Version: 1.0
Main-Class: jdk.nashorn.tools.Shell
Name: jdk/nashorn/
Implementation-Vendor: Oracle Corporation

View File

@ -0,0 +1,25 @@
#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle in the LICENSE file that accompanied this code.
#
# This code is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# version 2 for more details (a copy is included in the LICENSE file that
# accompanied this code).
#
# You should have received a copy of the GNU General Public License version
# 2 along with this work; if not, write to the Free Software Foundation,
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
jdk.nashorn.api.scripting.NashornScriptEngineFactory

View File

@ -0,0 +1,121 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.api.scripting;
/**
* This is base exception for all Nashorn exceptions. These originate from user's
* ECMAScript code. Example: script parse errors, exceptions thrown from scripts.
* Note that ScriptEngine methods like "eval", "invokeMethod", "invokeFunction"
* will wrap this as ScriptException and throw it. But, there are cases where user
* may need to access this exception (or implementation defined subtype of this).
* For example, if java interface is implemented by a script object or Java access
* to script object properties via java.util.Map interface. In these cases, user
* code will get an instance of this or implementation defined subclass.
*/
@SuppressWarnings("serial")
public class NashornException extends RuntimeException {
// script file name
private String fileName;
// script line number
private int line;
// script column number
private int column;
/**
* Constructor
*
* @param msg exception message
*/
protected NashornException(final String msg) {
super(msg);
}
/**
* Constructor
* @param msg exception message
* @param cause exception cause
*/
protected NashornException(final String msg, final Throwable cause) {
super(msg, cause);
}
/**
* Constructor
*
* @param cause exception cause
*/
protected NashornException(final Throwable cause) {
super(cause);
}
/**
* Get the source file name for this {@code NashornException}
* @return the file name
*/
public final String getFileName() {
return fileName;
}
/**
* Set the source file name for this {@code NashornException}
* @param fileName file name
*/
protected final void setFileName(final String fileName) {
this.fileName = fileName;
}
/**
* Get the line number for this {@code NashornException}
* @return the line number
*/
public final int getLineNumber() {
return line;
}
/**
* Set the line number for this {@code NashornException}
* @param line line number
*/
protected final void setLineNumber(final int line) {
this.line = line;
}
/**
* Get the column for this {@code NashornException}
* @return the column
*/
public final int getColumnNumber() {
return column;
}
/**
* Set the column number for this {@code NashornException}
* @param column the column
*/
public final void setColumnNumber(final int column) {
this.column = column;
}
}

View File

@ -0,0 +1,521 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.api.scripting;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import javax.script.AbstractScriptEngine;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.GlobalObject;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
import jdk.nashorn.internal.runtime.options.Options;
/**
* JSR-223 compliant script engine for Nashorn. Instances are not created directly, but rather returned through
* {@link NashornScriptEngineFactory#getScriptEngine()}. Note that this engine implements the {@link Compilable} and
* {@link Invocable} interfaces, allowing for efficient precompilation and repeated execution of scripts.
* @see NashornScriptEngineFactory
*/
public final class NashornScriptEngine extends AbstractScriptEngine implements Compilable, Invocable {
private final ScriptEngineFactory factory;
private final Context nashornContext;
private final ScriptObject global;
// default options passed to Nashorn Options object
private static final String[] DEFAULT_OPTIONS = new String[] { "-scripting", "-af", "-doe" };
NashornScriptEngine(final NashornScriptEngineFactory factory) {
this(factory, DEFAULT_OPTIONS);
}
@SuppressWarnings("LeakingThisInConstructor")
NashornScriptEngine(final NashornScriptEngineFactory factory, final String[] args) {
this.factory = factory;
final Options options = new Options("nashorn");
options.process(args);
// throw ParseException on first error from script
final ErrorManager errors = new Context.ThrowErrorManager();
// create new Nashorn Context and get global object
this.nashornContext = AccessController.doPrivileged(new PrivilegedAction<Context>() {
@Override
public Context run() {
try {
return new Context(options, errors);
} catch (final RuntimeException e) {
if (Context.DEBUG) {
e.printStackTrace();
}
throw e;
}
}
});
// create new global object
this.global = nashornContext.createGlobal();
// current ScriptContext exposed as "context"
global.addOwnProperty("context", Property.NOT_ENUMERABLE, context);
// current ScriptEngine instance exposed as "engine". We added @SuppressWarnings("LeakingThisInConstructor") as
// NetBeans identifies this assignment as such a leak - this is a false positive as we're setting this property
// in the Global of a Context we just created - both the Context and the Global were just created and can not be
// seen from another thread outside of this constructor.
global.addOwnProperty("engine", Property.NOT_ENUMERABLE, this);
// global script arguments
global.addOwnProperty("arguments", Property.NOT_ENUMERABLE, UNDEFINED);
// evaluate engine initial script
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws ScriptException {
evalEngineScript();
return null;
}
});
} catch (final PrivilegedActionException e) {
if (Context.DEBUG) {
e.printStackTrace();
}
throw new RuntimeException(e);
}
}
@Override
public Object eval(final Reader reader, final ScriptContext ctxt) throws ScriptException {
try {
return evalImpl(Source.readFully(reader), ctxt);
} catch (final IOException e) {
throw new ScriptException(e);
}
}
@Override
public Object eval(final String script, final ScriptContext ctxt) throws ScriptException {
return evalImpl(script.toCharArray(), ctxt);
}
/*
* FIXME: This is not exactly right. Script execution should actually
* put the variables in ENGINE_SCOPE bindings. But, it is difficult
* (not possible?) with the way ScriptObject is implemented. So, for now
* giving access to script variables by accessing it from "globals". This
* way at least engine.get("foo") will work whenever "foo" is a global var
* defined by eval'ed scripts.
*/
@Override
public Object get(final String key) {
Object value = super.get(key);
if (value == null) {
value = ScriptObjectMirror.wrap(global.get(key), global);
}
return (value == UNDEFINED) ? null : value;
}
@Override
public ScriptEngineFactory getFactory() {
return factory;
}
@Override
public Bindings createBindings() {
return new SimpleBindings();
}
// Compilable methods
@Override
public CompiledScript compile(final Reader reader) throws ScriptException {
try {
return asCompiledScript(compileImpl(Source.readFully(reader), context));
} catch (final IOException e) {
throw new ScriptException(e);
}
}
@Override
public CompiledScript compile(final String str) throws ScriptException {
return asCompiledScript(compileImpl(str.toCharArray(), context));
}
// Invocable methods
@Override
public Object invokeFunction(final String name, final Object... args)
throws ScriptException, NoSuchMethodException {
return invokeImpl(null, name, args);
}
@Override
public Object invokeMethod(final Object self, final String name, final Object... args)
throws ScriptException, NoSuchMethodException {
if (self == null) {
throw new IllegalArgumentException("script object can not be null");
}
return invokeImpl(self, name, args);
}
private <T> T getInterfaceInner(final Object self, final Class<T> clazz) {
final Object realSelf;
if(self == null) {
realSelf = global;
} else if (!(self instanceof ScriptObject)) {
realSelf = ScriptObjectMirror.unwrap(self, global);
} else {
realSelf = self;
}
try {
final ScriptObject oldGlobal = Context.getGlobal();
try {
if(oldGlobal != global) {
setNashornGlobal(global);
}
return clazz.cast(JavaAdapterFactory.getConstructor(realSelf.getClass(), clazz).invoke(realSelf));
} finally {
if(oldGlobal != global) {
setNashornGlobal(oldGlobal);
}
}
} catch(final RuntimeException|Error e) {
throw e;
} catch(final Throwable t) {
throw new RuntimeException(t);
}
}
@Override
public <T> T getInterface(final Class<T> clazz) {
return getInterfaceInner(null, clazz);
}
@Override
public <T> T getInterface(final Object self, final Class<T> clazz) {
if (self == null) {
throw new IllegalArgumentException("script object can not be null");
}
return getInterfaceInner(self, clazz);
}
// These are called from the "engine.js" script
/**
* This hook is used to search js global variables exposed from Java code.
*
* @param self 'this' passed from the script
* @param ctxt current ScriptContext in which name is searched
* @param name name of the variable searched
* @return the value of the named variable
*/
public Object __noSuchProperty__(final Object self, final ScriptContext ctxt, final String name) {
final int scope = ctxt.getAttributesScope(name);
if (scope != -1) {
return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), global);
}
if (self == UNDEFINED) {
// scope access and so throw ReferenceError
referenceError(global, "not.defined", name);
}
return UNDEFINED;
}
/**
* This hook is used to call js global functions exposed from Java code.
*
* @param self 'this' passed from the script
* @param ctxt current ScriptContext in which method is searched
* @param name name of the method
* @param args arguments to be passed to the method
* @return return value of the called method
*/
public Object __noSuchMethod__(final Object self, final ScriptContext ctxt, final String name, final Object args) {
final int scope = ctxt.getAttributesScope(name);
Object value;
if (scope != -1) {
value = ctxt.getAttribute(name, scope);
} else {
if (self == UNDEFINED) {
referenceError(global, "not.defined", name);
} else {
typeError(global, "no.such.function", name, ScriptRuntime.safeToString(global));
}
return UNDEFINED;
}
value = ScriptObjectMirror.unwrap(value, global);
if (value instanceof Method) {
final Method method = (Method) value;
final int mods = method.getModifiers();
if (Modifier.isStatic(mods) && Modifier.isPublic(mods)) {
value = MH.find((Method)value);
}
}
if (value instanceof MethodHandle) {
value = ((GlobalObject)global).newScriptFunction(name, (MethodHandle)value, null, false);
}
if (!(value instanceof ScriptFunction)) {
typeError(global, "not.a.function", name);
}
if (value instanceof ScriptFunction) {
return ScriptObjectMirror.unwrap(ScriptRuntime.apply((ScriptFunction)value, global, args), global);
}
typeError(global, "not.a.function", ScriptRuntime.safeToString(name));
return UNDEFINED;
}
private void evalEngineScript() throws ScriptException {
final URL url = NashornScriptEngine.class.getResource("resources/engine.js");
try {
final InputStream is = url.openStream();
put(ScriptEngine.FILENAME, url);
try (final InputStreamReader isr = new InputStreamReader(is)) {
eval(isr);
}
} catch (final IOException e) {
throw new ScriptException(e);
} finally {
put(ScriptEngine.FILENAME, null);
}
}
// scripts should see "context" and "engine" as variables
private void setContextVariables(final ScriptContext ctxt) {
ctxt.setAttribute("context", ctxt, ScriptContext.ENGINE_SCOPE);
// current ScriptContext exposed as "context"
global.set("context", ctxt, false);
Object args = ctxt.getAttribute("arguments");
// if no arguments passed, make it empty array
if (args == null) {
args = ScriptRuntime.EMPTY_ARRAY;
}
args = ((GlobalObject)global).wrapAsObject(args);
global.set("arguments", args, false);
}
private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException {
final ScriptObject oldGlobal = Context.getGlobal();
final boolean globalChanged = (oldGlobal != global);
Object self = selfObject;
try {
if (globalChanged) {
setNashornGlobal(global);
}
ScriptObject sobj;
Object value;
self = ScriptObjectMirror.unwrap(self, global);
// FIXME: should convert when self is not ScriptObject
if (self instanceof ScriptObject) {
sobj = (ScriptObject)self;
value = sobj.get(name);
} else if (self == null) {
self = global;
sobj = global;
value = sobj.get(name);
} else {
// Find the java method and make a ScriptFunction of it.
final Method[] methods = self.getClass().getMethods();
Method target = null;
for (final Method mth : methods) {
// choose the right overload by number of arguments -- don't
// care overload resolution for now..
if (mth.getName().equals(name) &&
mth.getParameterTypes().length == args.length) {
target = mth;
break;
}
}
if (target == null) {
throw new NoSuchMethodException(name);
}
final GlobalObject gobj = (GlobalObject) global;
value = gobj.newScriptFunction(name, MH.find(target), null, false);
}
if (value instanceof ScriptFunction) {
final Object res;
try {
res = ScriptRuntime.apply((ScriptFunction)value, self, ScriptObjectMirror.unwrapArray(args, global));
} catch (final Exception e) {
throwAsScriptException(e);
throw new AssertionError("should not reach here");
}
return ScriptObjectMirror.wrap(res, global);
}
throw new NoSuchMethodException(name);
} finally {
if (globalChanged) {
setNashornGlobal(oldGlobal);
}
}
}
private Object evalImpl(final char[] buf, final ScriptContext ctxt) throws ScriptException {
return evalImpl(compileImpl(buf, ctxt), ctxt);
}
private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt) throws ScriptException {
if (script == null) {
return null;
}
final ScriptObject oldGlobal = Context.getGlobal();
final boolean globalChanged = (oldGlobal != global);
try {
if (globalChanged) {
setNashornGlobal(global);
}
setContextVariables(ctxt);
Object res = ScriptRuntime.apply(script, global);
res = ScriptObjectMirror.wrap(res, global);
return (res == UNDEFINED) ? null : res;
} catch (final Exception e) {
throwAsScriptException(e);
throw new AssertionError("should not reach here");
} finally {
if (globalChanged) {
setNashornGlobal(oldGlobal);
}
}
}
private static void throwAsScriptException(final Exception e) throws ScriptException {
if (e instanceof ScriptException) {
throw (ScriptException)e;
} else if (e instanceof NashornException) {
final NashornException ne = (NashornException)e;
final ScriptException se = new ScriptException(
ne.getMessage(), ne.getFileName(),
ne.getLineNumber(), ne.getColumnNumber());
se.initCause(e);
throw se;
} else if (e instanceof RuntimeException) {
throw (RuntimeException)e;
} else {
// wrap any other exception as ScriptException
throw new ScriptException(e);
}
}
private CompiledScript asCompiledScript(final ScriptFunction script) {
return new CompiledScript() {
@Override
public Object eval(final ScriptContext ctxt) throws ScriptException {
return evalImpl(script, ctxt);
}
@Override
public ScriptEngine getEngine() {
return NashornScriptEngine.this;
}
};
}
private ScriptFunction compileImpl(final char[] buf, final ScriptContext ctxt) throws ScriptException {
final ScriptObject oldGlobal = Context.getGlobal();
final boolean globalChanged = (oldGlobal != global);
try {
final Object val = ctxt.getAttribute(ScriptEngine.FILENAME);
final String fileName = (val != null) ? val.toString() : "<eval>";
// !!HACK!! do not evaluate "init.js" from jrunscript tool!!
if ("<system-init>".equals(fileName)) {
return null;
}
final Source source = new Source(fileName, buf);
if (globalChanged) {
setNashornGlobal(global);
}
setContextVariables(ctxt);
return nashornContext.compileScript(source, global, nashornContext._strict);
} catch (final Exception e) {
throwAsScriptException(e);
throw new AssertionError("should not reach here");
} finally {
if (globalChanged) {
setNashornGlobal(oldGlobal);
}
}
}
// don't make this public!!
static void setNashornGlobal(final ScriptObject global) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
Context.setGlobal(global);
return null;
}
});
}
}

View File

@ -0,0 +1,179 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.api.scripting;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import jdk.nashorn.internal.runtime.Version;
/**
* JSR-223 compliant script engine factory for Nashorn. The engine answers for:
* <ul>
* <li>names {@code "nashorn"}, {@code "Nashorn"}, {@code "js"}, {@code "JS"}, {@code "JavaScript"},
* {@code "javascript"}, {@code "ECMAScript"}, and {@code "ecmascript"};</li>
* <li>MIME types {@code "application/javascript"}, {@code "application/ecmascript"}, {@code "text/javascript"}, and
* {@code "text/ecmascript"};</li>
* <li>as well as for the extension {@code "js"}.</li>
* </ul>
* Programs executing in engines created using {@link #getScriptEngine(String[])} will have the passed arguments
* accessible as a global variable named {@code "arguments"}.
*/
public final class NashornScriptEngineFactory implements ScriptEngineFactory {
@Override
public String getEngineName() {
return (String) getParameter(ScriptEngine.ENGINE);
}
@Override
public String getEngineVersion() {
return (String) getParameter(ScriptEngine.ENGINE_VERSION);
}
@Override
public List<String> getExtensions() {
return Collections.unmodifiableList(extensions);
}
@Override
public String getLanguageName() {
return (String) getParameter(ScriptEngine.LANGUAGE);
}
@Override
public String getLanguageVersion() {
return (String) getParameter(ScriptEngine.LANGUAGE_VERSION);
}
@Override
public String getMethodCallSyntax(final String obj, final String method, final String... args) {
final StringBuilder sb = new StringBuilder().append(obj).append('.').append(method).append('(');
final int len = args.length;
if (len > 0) {
sb.append(args[0]);
}
for (int i = 1; i < len; i++) {
sb.append(',').append(args[i]);
}
sb.append(')');
return sb.toString();
}
@Override
public List<String> getMimeTypes() {
return Collections.unmodifiableList(mimeTypes);
}
@Override
public List<String> getNames() {
return Collections.unmodifiableList(names);
}
@Override
public String getOutputStatement(final String toDisplay) {
return "print(" + toDisplay + ")";
}
@Override
public Object getParameter(final String key) {
switch (key) {
case ScriptEngine.NAME:
return "javascript";
case ScriptEngine.ENGINE:
return "Oracle Nashorn";
case ScriptEngine.ENGINE_VERSION:
return Version.version();
case ScriptEngine.LANGUAGE:
return "ECMAScript";
case ScriptEngine.LANGUAGE_VERSION:
return "ECMA - 262 Edition 5.1";
case "THREADING":
// The engine implementation is not thread-safe. Can't be
// used to execute scripts concurrently on multiple threads.
return null;
default:
throw new IllegalArgumentException("Invalid key");
}
}
@Override
public String getProgram(final String... statements) {
final StringBuilder sb = new StringBuilder();
for (final String statement : statements) {
sb.append(statement).append(';');
}
return sb.toString();
}
@Override
public ScriptEngine getScriptEngine() {
return new NashornScriptEngine(this);
}
/**
* Create a new Script engine initialized by given arguments.
*
* @param args arguments array passed to script engine.
* @return newly created script engine.
*/
public ScriptEngine getScriptEngine(final String[] args) {
return new NashornScriptEngine(this, args);
}
// -- Internals only below this point
private static final List<String> names;
private static final List<String> mimeTypes;
private static final List<String> extensions;
static {
names = immutableList(
"nashorn", "Nashorn",
"js", "JS",
"JavaScript", "javascript",
"ECMAScript", "ecmascript"
);
mimeTypes = immutableList(
"application/javascript",
"application/ecmascript",
"text/javascript",
"text/ecmascript"
);
extensions = immutableList("js");
}
private static List<String> immutableList(final String... elements) {
return Collections.unmodifiableList(Arrays.asList(elements));
}
}

View File

@ -0,0 +1,323 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.api.scripting;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import netscape.javascript.JSObject;
/**
* Mirror object that wraps a given ScriptObject instance. User can
* access ScriptObject via the java.util.Map interface.
*/
final class ScriptObjectMirror extends JSObject implements Map<Object, Object> {
private final ScriptObject sobj;
private final ScriptObject global;
ScriptObjectMirror(final ScriptObject sobj, final ScriptObject global) {
this.sobj = sobj;
this.global = global;
}
@Override
public boolean equals(final Object other) {
if (other instanceof ScriptObjectMirror) {
return sobj.equals(((ScriptObjectMirror)other).sobj);
}
return false;
}
@Override
public int hashCode() {
return sobj.hashCode();
}
private <V> V inGlobal(final Callable<V> callable) {
final ScriptObject oldGlobal = Context.getGlobal();
final boolean globalChanged = (oldGlobal != global);
if (globalChanged) {
NashornScriptEngine.setNashornGlobal(global);
}
try {
return callable.call();
} catch (final RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new AssertionError("Cannot happen", e);
} finally {
if (globalChanged) {
NashornScriptEngine.setNashornGlobal(oldGlobal);
}
}
}
// JSObject methods
@Override
public Object call(final String methodName, final Object args[]) {
final Object val = sobj.get(methodName);
final ScriptObject oldGlobal = Context.getGlobal();
final boolean globalChanged = (oldGlobal != global);
if (val instanceof ScriptFunction) {
final Object[] modifiedArgs = unwrapArray(args, global);
if (modifiedArgs != null) {
for (int i = 0; i < modifiedArgs.length; i++) {
final Object arg = modifiedArgs[i];
if (arg instanceof ScriptObject) {
modifiedArgs[i] = wrap(arg, oldGlobal);
}
}
}
try {
if (globalChanged) {
NashornScriptEngine.setNashornGlobal(global);
}
return wrap(((ScriptFunction)val).invoke(sobj, modifiedArgs), global);
} catch (final RuntimeException | Error e) {
throw e;
} catch (final Throwable t) {
throw new RuntimeException(t);
} finally {
if (globalChanged) {
NashornScriptEngine.setNashornGlobal(oldGlobal);
}
}
}
throw new RuntimeException("No such method: " + methodName);
}
@Override
public Object eval(final String s) {
return inGlobal(new Callable<Object>() {
@Override
public Object call() {
return wrap(global.getContext().eval(global, s, null, null, false), global);
}
});
}
@Override
public Object getMember(final String name) {
return get(name);
}
@Override
public Object getSlot(final int index) {
return get(Integer.valueOf(index));
}
@Override
public void removeMember(final String name) {
remove(name);
}
@Override
public void setMember(final String name, final Object value) {
put(name, wrap(value, Context.getGlobal()));
}
@Override
public void setSlot(final int index, final Object value) {
put(Integer.valueOf(index), wrap(value, Context.getGlobal()));
}
@Override
public void clear() {
inGlobal(new Callable<Object>() {
@Override public Object call() {
sobj.clear();
return null;
}});
}
@Override
public boolean containsKey(final Object key) {
return inGlobal(new Callable<Boolean>() {
@Override public Boolean call() {
return sobj.containsKey(unwrap(key, global));
}});
}
@Override
public boolean containsValue(final Object value) {
return inGlobal(new Callable<Boolean>() {
@Override public Boolean call() {
return sobj.containsValue(unwrap(value, global));
}});
}
@Override
public Set<Map.Entry<Object, Object>> entrySet() {
return inGlobal(new Callable<Set<Map.Entry<Object, Object>>>() {
@Override public Set<Map.Entry<Object, Object>> call() {
final Iterator<String> iter = sobj.propertyIterator();
final Set<Map.Entry<Object, Object>> entries = new HashSet<>();
while (iter.hasNext()) {
final Object key = wrap(iter.next(), global);
final Object value = wrap(sobj.get(key), global);
entries.add(new AbstractMap.SimpleImmutableEntry<>(key, value));
}
return Collections.unmodifiableSet(entries);
}
});
}
@Override
public Object get(final Object key) {
return inGlobal(new Callable<Object>() { @Override public Object call() {
return wrap(sobj.get(key), global);
}});
}
@Override
public boolean isEmpty() {
return inGlobal(new Callable<Boolean>() { @Override public Boolean call() {
return sobj.isEmpty();
}});
}
@Override
public Set<Object> keySet() {
return inGlobal(new Callable<Set<Object>>() { @Override public Set<Object> call() {
final Iterator<String> iter = sobj.propertyIterator();
final Set<Object> keySet = new HashSet<>();
while (iter.hasNext()) {
keySet.add(wrap(iter.next(), global));
}
return Collections.unmodifiableSet(keySet);
}});
}
@Override
public Object put(final Object key, final Object value) {
return inGlobal(new Callable<Object>() {
@Override public Object call() {
return sobj.put(unwrap(key, global), unwrap(value, global));
}});
}
@Override
public void putAll(final Map<?, ?> map) {
final boolean strict = sobj.getContext()._strict;
inGlobal(new Callable<Object>() { @Override public Object call() {
for (final Map.Entry<?, ?> entry : map.entrySet()) {
sobj.set(unwrap(entry.getKey(), global), unwrap(entry.getValue(), global), strict);
}
return null;
}});
}
@Override
public Object remove(final Object key) {
return inGlobal(new Callable<Object>() {
@Override public Object call() {
return wrap(sobj.remove(unwrap(key, global)), global);
}
});
}
@Override
public int size() {
return inGlobal(new Callable<Integer>() {
@Override public Integer call() {
return sobj.size();
}
});
}
@Override
public Collection<Object> values() {
return inGlobal(new Callable<Collection<Object>>() { @Override public Collection<Object> call() {
final List<Object> values = new ArrayList<>(size());
final Iterator<Object> iter = sobj.valueIterator();
while (iter.hasNext()) {
values.add(wrap(iter.next(), global));
}
return Collections.unmodifiableList(values);
}});
}
static Object wrap(final Object obj, final ScriptObject homeGlobal) {
return (obj instanceof ScriptObject) ? new ScriptObjectMirror((ScriptObject)obj, homeGlobal) : obj;
}
static Object unwrap(final Object obj, final ScriptObject homeGlobal) {
if (obj instanceof ScriptObjectMirror) {
final ScriptObjectMirror mirror = (ScriptObjectMirror)obj;
return (mirror.global == homeGlobal)? mirror.sobj : obj;
}
return obj;
}
static Object[] wrapArray(final Object[] args, final ScriptObject homeGlobal) {
if (args == null || args.length == 0) {
return args;
}
final Object[] newArgs = new Object[args.length];
int index = 0;
for (final Object obj : args) {
newArgs[index] = wrap(obj, homeGlobal);
index++;
}
return newArgs;
}
static Object[] unwrapArray(final Object[] args, final ScriptObject homeGlobal) {
if (args == null || args.length == 0) {
return args;
}
final Object[] newArgs = new Object[args.length];
int index = 0;
for (final Object obj : args) {
newArgs[index] = unwrap(obj, homeGlobal);
index++;
}
return newArgs;
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* This package provides the {@code javax.script} integration, which is the preferred way to use Nashorn.
* You will ordinarily do this to obtain an instance of a Nashorn script engine:
* <pre>
* import javax.script.*;
* ...
* ScriptEngine nashornEngine = new ScriptEngineManager().getEngineByName("Nashorn");
* </pre>
* <p>Nashorn script engines implement the optional {@link javax.script.Invocable} and {@link javax.script.Compilable}
* interfaces, allowing for efficient pre-compilation and repeated execution of scripts. See
* {@link jdk.nashorn.api.scripting.NashornScriptEngineFactory} for further details.
*/
package jdk.nashorn.api.scripting;

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* This script file is executed by script engine at the construction
* of the engine. The functions here assume global variables "context"
* of type javax.script.ScriptContext and "engine" of the type
* jdk.nashorn.api.scripting.NashornScriptEngine.
*
**/
Object.defineProperty(this, "__noSuchProperty__", {
configurable: true,
enumerable: false,
writable: true,
value: function (name) {
'use strict';
return engine.__noSuchProperty__(this, context, name);
}
});
Object.defineProperty(this, "__noSuchMethod__", {
configurable: true,
enumerable: false,
writable: true,
value: function (name, args) {
'use strict';
return engine.__noSuchMethod__(this, context, name, args);
}
});
function print(str) {
var writer = context.getWriter();
if (! (writer instanceof java.io.PrintWriter)) {
writer = new java.io.PrintWriter(writer);
}
writer.println(String(str));
}

View File

@ -0,0 +1,428 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import java.util.HashSet;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.Assignment;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ReferenceNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.ir.TypeOverride;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.DebugLogger;
/**
* This is a post pass for Lower, that removes casts for accessors to objects in
* Scope, and replaces the accessor type with a narrower one with possible.
*
* Any node that implements TypeOverride will be subject to access specialization.
*
* TypeOverride basically means "type inference has determined that the field x
* is an object, but if you do x & 17, it can be read as an int, which we hope
* coincides with its internal representation. In that case there is no boxing
* that may or may not be removed by the JVM and less data bandwidth.
*
* Ideally this should not be a post pass, but it requires slot AND scope info, and has
* to be run where it is, which is called from {@link CodeGenerator}.
*
* @see TypeOverride
*/
final class AccessSpecializer extends NodeOperatorVisitor implements Transform {
/** Debug logger for access specialization. Enable it with --log=access:level
or -Dnashorn.specializations.debug */
private static final DebugLogger LOG = new DebugLogger("access", "nashorn.callsiteaccess.debug");
private static final boolean DEBUG = LOG.isEnabled();
@Override
public Node enter(final FunctionNode node) {
if (node.isTransformApplied(AccessSpecializer.class)) {
return null;
}
return node;
}
@Override
public Node leave(final FunctionNode node) {
node.registerTransform(AccessSpecializer.class);
return node;
}
@Override
public Node leave(final VarNode varNode) {
if (varNode.isAssignment()) {
return leaveAssign(varNode);
}
return varNode;
}
@Override
public Node leave(final CallNode callNode) {
final Node function = callNode.getFunction();
if (function instanceof ReferenceNode) {
changeType(callNode, ((ReferenceNode)function).getReference().getType());
}
return callNode;
}
@Override
public Node leaveASSIGN(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_ADD(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_BIT_AND(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_BIT_OR(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_BIT_XOR(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_DIV(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_MOD(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_MUL(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_SAR(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_SHL(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_SHR(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveASSIGN_SUB(final BinaryNode binaryNode) {
return leaveAssign(binaryNode);
}
@Override
public Node leaveCOMMALEFT(final BinaryNode binaryNode) {
return propagateResultType(binaryNode, binaryNode.lhs().getType());
}
@Override
public Node leaveCOMMARIGHT(final BinaryNode binaryNode) {
return propagateResultType(binaryNode, binaryNode.rhs().getType());
}
@Override
public Node leaveCONVERT(final UnaryNode unaryNode) {
final Type castTo = unaryNode.getType();
Node rhs = unaryNode.rhs();
// Go through all conversions until we find the first non-convert node.
while (rhs.tokenType() == TokenType.CONVERT) {
rhs = ((UnaryNode)rhs).rhs();
}
// If this node can be type changed
if (canHaveCallSiteType(rhs) && isSupportedCallSiteType(castTo)) {
/*
* Just add a callsite type and throw away the cast, appropriate
* getter/setter is selected, which will do the conversion for us
*/
changeType(rhs, castTo);
fine("*** cast: converting " + debugNode(unaryNode) + " to " + debugNode(rhs));
return rhs;
}
// Micro optimization for node hygiene and pattern detection - remove unnecessary same type cast
if (unaryNode.getType().isEquivalentTo(rhs.getType())) {
return rhs;
}
return unaryNode;
}
@Override
public Node leaveDECINC(final UnaryNode unaryNode) {
assert unaryNode.isAssignment();
final Node dest = unaryNode.getAssignmentDest();
if (canHaveCallSiteType(dest) && isSupportedCallSiteType(unaryNode.getType())) {
changeTypeInAssignment(dest, unaryNode.getType());
}
return unaryNode;
}
/**
* Is this a node that can have its type overridden. This is true for
* AccessNodes, IndexNodes and IdentNodes
*
* @param node the node to check
* @return true if node can have a callsite type
*/
private static boolean canHaveCallSiteType(final Node node) {
return node instanceof TypeOverride && ((TypeOverride)node).canHaveCallSiteType();
}
/**
* Is the specialization type supported. Currently we treat booleans as objects
* and have no special boolean type accessor, thus booleans are ignored.
* TODO - support booleans? NASHORN-590
*
* @param castTo the type to check
* @return true if call site type is supported
*/
private static boolean isSupportedCallSiteType(final Type castTo) {
return castTo.isNumeric(); // don't specializable for boolean
}
/**
* Turn a node into a covert node
*
* @param node the node
* @return the node as a convert node
*/
private static Node convert(final Node node) {
return new UnaryNode(node.getSource(),
Token.recast(node.getToken(), TokenType.CONVERT),
node);
}
private static Node leaveAssign(final Node node) {
assert node.isAssignment() : node + " is not an assignment";
final Node lhs = ((Assignment<?>)node).getAssignmentDest();
Node rhs = ((Assignment<?>)node).getAssignmentSource();
/**
* Nodes with local variable slots are assumed to be of their optimal type
* already and aren't affected here. This is not strictly true, for instance
* with doubles instead of in a bounded loop. TODO - range check: NASHORN-363
*
* This is also not strictly true for var y = x = 55; where y has no other uses
* Then y can be an int, but lower conservatively does an object of the assign to
* scope
*/
final Symbol lhsSymbol = lhs.getSymbol();
if (lhsSymbol.hasSlot() && !lhsSymbol.isScope()) {
finest(lhs.getSymbol() + " has slot!");
if (!lhs.getType().isEquivalentTo(rhs.getType())) {
finest("\tslot assignment: " +lhs.getType()+ " " +rhs.getType() + " " + debugNode(node));
final Node c = convert(rhs);
c.setSymbol(lhsSymbol);
((Assignment<?>)node).setAssignmentSource(c);
fine("*** slot assignment turned to : " + debugNode(node));
} else {
finest("aborted - type equivalence between lhs and rhs");
}
return node;
}
// e.g. __DIR__, __LINE__, __FILE__ - don't try to change these
if (lhs instanceof IdentNode && ((IdentNode)lhs).isSpecialIdentity()) {
return node;
}
/**
* Try to cast to the type of the right hand side, now pruned. E.g. an (object)17 should
* now be just 17, an int.
*/
Type castTo = rhs.getType();
// If LHS can't get a new type, neither can rhs - retain the convert
if (!canHaveCallSiteType(lhs)) {
return node;
}
// Take the narrowest type of the entire cast sequence
while (rhs.tokenType() == TokenType.CONVERT) {
rhs = ((UnaryNode)rhs).rhs();
castTo = Type.narrowest(rhs.getType(), castTo); //e.g. (object)(int) -> int even though object is outermost
}
// If castTo is wider than widestOperationType, castTo can be further slowed down
final Type widestOperationType = node.getWidestOperationType();
finest("node wants to be " + castTo + " and its widest operation is " + widestOperationType);
if (widestOperationType != castTo && Type.widest(castTo, widestOperationType) == castTo) {
info("###" + node + " castTo was " + castTo + " but could be downgraded to " + node.getWidestOperationType());
castTo = node.getWidestOperationType();
if (rhs instanceof TypeOverride) {
changeType(rhs, castTo);
}
}
/*
* If this is a self modifying op, we can't be narrower than the widest optype
* or e.g. x = x + 12 and x += 12 will turn into different things
*/
if (node.isSelfModifying()) {
castTo = Type.widest(widestOperationType, castTo);
}
// We only specialize for numerics, not for booleans.
if (isSupportedCallSiteType(castTo)) {
if (rhs.getType() != castTo) {
finest("cast was necessary, abort: " + node + " " + rhs.getType() + " != " + castTo);
return node;
}
finest("assign: " + debugNode(node));
changeTypeInAssignment(lhs, castTo);
((Assignment<?>)node).setAssignmentSource(rhs);
info("### modified to " + debugNode(node) + " (given type override " + castTo + ")");
propagateResultType(node, castTo);
}
return node;
}
private static Node propagateResultType(final Node node, final Type type) {
//warning! this CANNOT be done for non temporaries as they are used in other computations
if (isSupportedCallSiteType(type)) {
if (node.getSymbol().isTemp()) {
finest("changing temporary type: " + debugNode(node) + " to " + type);
node.getSymbol().setTypeOverride(type);
info("### node modified to " + debugNode(node) + " (given type override " + type + ")");
}
}
return node;
}
private static void changeTypeInAssignment(final Node dest, final Type newType) {
if (changeType(dest, newType)) {
finest("changed assignment " + dest + " " + dest.getSymbol());
assert !newType.isObject();
final HashSet<Node> exclude = new HashSet<>();
dest.accept(new NodeVisitor() {
private void setCanBePrimitive(final Symbol symbol) {
fine("*** can be primitive symbol " + symbol + " " + Debug.id(symbol));
symbol.setCanBePrimitive(newType);
}
@Override
public Node enter(final IdentNode identNode) {
if (!exclude.contains(identNode)) {
setCanBePrimitive(identNode.getSymbol());
}
return null;
}
@Override
public Node enter(final AccessNode accessNode) {
setCanBePrimitive(accessNode.getProperty().getSymbol());
return null;
}
@Override
public Node enter(final IndexNode indexNode) {
exclude.add(indexNode.getBase()); //prevent array base node to be flagged as primitive, but k in a[k++] is fine
return indexNode;
}
});
}
}
private static boolean changeType(final Node node, final Type newType) {
if (!node.getType().equals(newType)) {
((TypeOverride)node).setType(newType);
return true;
}
return false;
}
private static String debugNode(final Node node) {
if (DEBUG) {
return node.toString();
}
return "";
}
private static void info(final String str) {
LOG.info(str);
}
private static void fine(final String str) {
LOG.fine(str);
}
private static void finest(final String str) {
LOG.finest(str);
}
}

View File

@ -0,0 +1,199 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.MethodEmitter.Condition.EQ;
import static jdk.nashorn.internal.codegen.MethodEmitter.Condition.GE;
import static jdk.nashorn.internal.codegen.MethodEmitter.Condition.GT;
import static jdk.nashorn.internal.codegen.MethodEmitter.Condition.LE;
import static jdk.nashorn.internal.codegen.MethodEmitter.Condition.LT;
import static jdk.nashorn.internal.codegen.MethodEmitter.Condition.NE;
import jdk.nashorn.internal.codegen.MethodEmitter.Label;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.UnaryNode;
/**
* Branch optimizer for CodeGenerator. Given a jump condition this helper
* class attempts to simplify the control flow
*/
final class BranchOptimizer {
private final CodeGenerator codegen;
private final MethodEmitter method;
BranchOptimizer(final CodeGenerator codegen, final MethodEmitter method) {
this.codegen = codegen;
this.method = method;
}
void execute(final Node node, final Label label, final boolean state) {
branchOptimizer(node, label, state);
}
private void load(final Node node) {
codegen.load(node);
}
private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) {
final Node rhs = unaryNode.rhs();
switch (unaryNode.tokenType()) {
case NOT:
branchOptimizer(rhs, label, !state);
return;
case CONVERT:
if (unaryNode.getType().isBoolean()) {
branchOptimizer(rhs, label, state);
return;
}
break;
default:
break;
}
// convert to boolean
load(unaryNode);
method.convert(Type.BOOLEAN);
if (state) {
method.ifne(label);
} else {
method.ifeq(label);
}
}
private void branchOptimizer(final BinaryNode binaryNode, final Label label, final boolean state) {
final Node lhs = binaryNode.lhs();
final Node rhs = binaryNode.rhs();
switch (binaryNode.tokenType()) {
case AND:
if (state) {
final Label skip = new Label("skip");
branchOptimizer(lhs, skip, false);
branchOptimizer(rhs, label, true);
method.label(skip);
} else {
branchOptimizer(lhs, label, false);
branchOptimizer(rhs, label, false);
}
return;
case OR:
if (state) {
branchOptimizer(lhs, label, true);
branchOptimizer(rhs, label, true);
} else {
final Label skip = new Label("skip");
branchOptimizer(lhs, skip, true);
branchOptimizer(rhs, label, false);
method.label(skip);
}
return;
case EQ:
case EQ_STRICT:
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol();
load(lhs);
load(rhs);
method.conditionalJump(state ? EQ : NE, true, label);
return;
case NE:
case NE_STRICT:
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol();
load(lhs);
load(rhs);
method.conditionalJump(state ? NE : EQ, true, label);
return;
case GE:
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol();
load(lhs);
load(rhs);
method.conditionalJump(state ? GE : LT, !state, label);
return;
case GT:
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol();
load(lhs);
load(rhs);
method.conditionalJump(state ? GT : LE, !state, label);
return;
case LE:
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol();
load(lhs);
load(rhs);
method.conditionalJump(state ? LE : GT, state, label);
return;
case LT:
assert rhs.getType().isEquivalentTo(lhs.getType()) : "type mismatch: " + lhs.getSymbol() + " to " + rhs.getSymbol() + " in " + binaryNode;
load(lhs);
load(rhs);
method.conditionalJump(state ? LT : GE, state, label);
return;
default:
break;
}
load(binaryNode);
method.convert(Type.BOOLEAN);
if (state) {
method.ifne(label);
} else {
method.ifeq(label);
}
}
private void branchOptimizer(final Node node, final Label label, final boolean state) {
if (!(node instanceof TernaryNode)) {
if (node instanceof BinaryNode) {
branchOptimizer((BinaryNode)node, label, state);
return;
}
if (node instanceof UnaryNode) {
branchOptimizer((UnaryNode)node, label, state);
return;
}
}
load(node);
method.convert(Type.BOOLEAN);
if (state) {
method.ifne(label);
} else {
method.ifeq(label);
}
}
}

View File

@ -0,0 +1,620 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS;
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEINTERFACE;
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESPECIAL;
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
import static jdk.internal.org.objectweb.asm.Opcodes.H_NEWINVOKESPECIAL;
import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
import static jdk.nashorn.internal.codegen.CompilerConstants.CLINIT;
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_ARRAY_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_ARRAY_SUFFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.GET_STRING;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT;
import static jdk.nashorn.internal.codegen.CompilerConstants.SET_MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.Source;
/**
* The interface responsible for speaking to ASM, emitting classes,
* fields and methods.
* <p>
* This file contains the ClassEmitter, which is the master object
* responsible for writing byte codes. It utilizes a MethodEmitter
* for method generation, which also the NodeVisitors own, to keep
* track of the current code generator and what it is doing.
* <p>
* There is, however, nothing stopping you from using this in a
* completely self contained environment, for example in ObjectGenerator
* where there are no visitors or external hooks.
* <p>
* MethodEmitter makes it simple to generate code for methods without
* having to do arduous type checking. It maintains a type stack
* and will pick the appropriate operation for all operations sent to it
* We also allow chained called to a MethodEmitter for brevity, e.g.
* it is legal to write _new(className).dup() or
* load(slot).load(slot2).xor().store(slot3);
* <p>
* If running with assertions enabled, any type conflict, such as different
* bytecode stack sizes or operating on the wrong type will be detected
* and an error thrown.
* <p>
* There is also a very nice debug interface that can emit formatted
* bytecodes that have been written. This is enabled by setting the
* environment "nashorn.codegen.debug" to true, or --log=codegen:<level>
* <p>
* A ClassEmitter implements an Emitter - i.e. it needs to have
* well defined start and end calls for whatever it is generating. Assertions
* detect if this is not true
*
* @see Compiler
* @see CodeGenerator
*/
public class ClassEmitter implements Emitter {
/** Sanity check flag - have we started on a class? */
private boolean classStarted;
/** Sanity check flag - have we ended this emission? */
private boolean classEnded;
/**
* Sanity checks - which methods have we currently
* started for generation in this class?
*/
private final HashSet<MethodEmitter> methodsStarted;
/** The ASM classwriter that we use for all bytecode operations */
protected final ClassWriter cw;
/** The context */
protected final Context context;
/** Default flags for class generation - oublic class */
private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
/** Compile unit class name. */
private String unitClassName;
/** Set of constants access methods required. */
private Set<Class<?>> constantMethodNeeded;
/**
* Constructor - only used internally in this class as it breaks
* abstraction towards ASM or other code generator below
*
* @param context context
* @param cw ASM classwriter
*/
private ClassEmitter(final Context context, final ClassWriter cw) {
assert context != null;
this.context = context;
this.cw = cw;
this.methodsStarted = new HashSet<>();
}
/**
* Constructor
*
* @param context context
* @param className name of class to weave
* @param superClassName super class name for class
* @param interfaceNames names of interfaces implemented by this class, or null if none
*/
public ClassEmitter(final Context context, final String className, final String superClassName, final String... interfaceNames) {
this(context, new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS));
cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, superClassName, interfaceNames);
}
/**
* Constructor from the compiler
*
* @param compiler Compiler
* @param unitClassName Compile unit class name.
* @param strictMode Should we generate this method in strict mode
*/
ClassEmitter(final Compiler compiler, final String unitClassName, final boolean strictMode) {
this(compiler.getContext(),
new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
private static final String OBJECT_CLASS = "java/lang/Object";
@Override
protected String getCommonSuperClass(final String type1, final String type2) {
try {
return super.getCommonSuperClass(type1, type2);
} catch (final RuntimeException e) {
if (isScriptObject(Compiler.SCRIPTS_PACKAGE, type1) && isScriptObject(Compiler.SCRIPTS_PACKAGE, type2)) {
return className(ScriptObject.class);
}
return OBJECT_CLASS;
}
}
});
this.unitClassName = unitClassName;
this.constantMethodNeeded = new HashSet<>();
cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, unitClassName, null, Compiler.pathName(jdk.nashorn.internal.scripts.JS$.class.getName()), null);
cw.visitSource(compiler.getSource().getName(), null);
defineCommonStatics(strictMode);
}
/**
* Define the static fields common in all scripts.
* @param strictMode Should we generate this method in strict mode
*/
private void defineCommonStatics(final boolean strictMode) {
// source - used to store the source data (text) for this script. Shared across
// compile units. Set externally by the compiler.
field(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SOURCE.tag(), Source.class);
// constants - used to the constants array for this script. Shared across
// compile units. Set externally by the compiler.
field(EnumSet.of(Flag.PUBLIC, Flag.STATIC), CONSTANTS.tag(), Object[].class);
// strictMode - was this script compiled in strict mode. Set externally by the compiler.
field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.tag(), boolean.class, strictMode);
}
/**
* Define static utilities common needed in scripts. These are per compile unit
* and therefore have to be defined here and not in code gen.
*/
private void defineCommonUtilities() {
assert unitClassName != null;
if (constantMethodNeeded.contains(String.class)) {
// $getString - get the ith entry from the constants table and cast to String.
final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.tag(), String.class, int.class);
getStringMethod.begin();
getStringMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
.load(Type.INT, 0)
.arrayload()
.checkcast(String.class)
._return();
getStringMethod.end();
}
if (constantMethodNeeded.contains(PropertyMap.class)) {
// $getMap - get the ith entry from the constants table and cast to PropertyMap.
final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.tag(), PropertyMap.class, int.class);
getMapMethod.begin();
getMapMethod.loadConstants(unitClassName)
.load(Type.INT, 0)
.arrayload()
.checkcast(PropertyMap.class)
._return();
getMapMethod.end();
// $setMap - overwrite an existing map.
final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.tag(), void.class, int.class, PropertyMap.class);
setMapMethod.begin();
setMapMethod.loadConstants(unitClassName)
.load(Type.INT, 0)
.load(Type.OBJECT, 1)
.arraystore();
setMapMethod.returnVoid();
setMapMethod.end();
}
// $getXXXX$array - get the ith entry from the constants table and cast to XXXX[].
for (final Class<?> cls : constantMethodNeeded) {
if (cls.isArray()) {
defineGetArrayMethod(cls);
}
}
}
/**
* Constructs a primitive specific method for getting the ith entry from the constants table and cast.
* @param cls Array class.
*/
private void defineGetArrayMethod(final Class<?> cls) {
assert unitClassName != null;
final String methodName = getArrayMethodName(cls);
final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, cls, int.class);
getArrayMethod.begin();
getArrayMethod.getStatic(unitClassName, CONSTANTS.tag(), CONSTANTS.descriptor())
.load(Type.INT, 0)
.arrayload()
.checkcast(cls)
.dup()
.arraylength()
.invoke(staticCallNoLookup(Arrays.class, "copyOf", cls, cls, int.class))
._return();
getArrayMethod.end();
}
/**
* Generate the name of a get array from constant pool method.
* @param cls Name of array class.
* @return Method name.
*/
static String getArrayMethodName(final Class<?> cls) {
assert cls.isArray();
return GET_ARRAY_PREFIX.tag() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.tag();
}
/**
* Ensure a get constant method is issued for the class.
* @param cls Class of constant.
*/
public void needGetConstantMethod(final Class<?> cls) {
constantMethodNeeded.add(cls);
}
/**
* Inspect class name and decide whether we are generating a ScriptObject class
*
* @param scriptPrefix the script class prefix for the current script
* @param type the type to check
*
* @return true if type is ScriptObject
*/
private static boolean isScriptObject(final String scriptPrefix, final String type) {
if (type.startsWith(scriptPrefix)) {
return true;
} else if (type.equals(CompilerConstants.className(ScriptObject.class))) {
return true;
} else if (type.startsWith(Compiler.OBJECTS_PACKAGE)) {
return true;
}
return false;
}
/**
* Call at beginning of class emission
* @see Emitter
*/
@Override
public void begin() {
classStarted = true;
}
/**
* Call at end of class emission
* @see Emitter
*/
@Override
public void end() {
assert classStarted;
if (unitClassName != null) {
defineCommonUtilities();
}
cw.visitEnd();
classStarted = false;
classEnded = true;
assert methodsStarted.isEmpty() : "methodsStarted not empty " + methodsStarted;
}
/**
* Disassemble an array of byte code.
*
* @param context the context
* @param bytecode byte array representing bytecode
*/
public static void disassemble(final Context context, final byte[] bytecode) {
new ClassReader(bytecode).accept(new TraceClassVisitor(context.getErr()), 0);
}
/**
* Verify an array of byte code as a valid Java class
*
* @param context the context
* @param bytecode the bytecode array
*/
public static void verify(final Context context, final byte[] bytecode) {
context.verify(bytecode);
}
/**
* @return context used for class emission
*/
Context getContext() {
return context;
}
/**
* Call back from MethodEmitter for method start
*
* @see MethodEmitter
*
* @param method method emitter.
*/
void beginMethod(final MethodEmitter method) {
assert !methodsStarted.contains(method);
methodsStarted.add(method);
}
/**
* Call back from MethodEmitter for method end
*
* @see MethodEmitter
*
* @param method
*/
void endMethod(final MethodEmitter method) {
assert methodsStarted.contains(method);
methodsStarted.remove(method);
}
/**
* Add a new method to the class - defaults to public method
*
* @param methodName name of method
* @param rtype return type of the method
* @param ptypes parameter types the method
*
* @return method emitter to use for weaving this method
*/
public MethodEmitter method(final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
return method(DEFAULT_METHOD_FLAGS, methodName, rtype, ptypes); //TODO why public default ?
}
/**
* Add a new method to the class - defaults to public method
*
* @param methodFlags access flags for the method
* @param methodName name of method
* @param rtype return type of the method
* @param ptypes parameter types the method
*
* @return method emitter to use for weaving this method
*/
public MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, methodDescriptor(rtype, ptypes), null, null));
}
/**
* Add a new method to the class - defaults to public method
*
* @param methodName name of method
* @param descriptor descriptor of method
*
* @return method emitter to use for weaving this method
*/
public MethodEmitter method(final String methodName, final String descriptor) {
return method(DEFAULT_METHOD_FLAGS, methodName, descriptor);
}
/**
* Add a new method to the class - defaults to public method
*
* @param methodFlags access flags for the method
* @param methodName name of method
* @param descriptor descriptor of method
*
* @return method emitter to use for weaving this method
*/
public MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final String descriptor) {
return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null));
}
/**
* Add a new method to the class, representing a function node
*
* @param functionNode the function node to generate a method for
* @return method emitter to use for weaving this method
*/
public MethodEmitter method(final FunctionNode functionNode) {
final MethodVisitor mv = cw.visitMethod(
ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
functionNode.getName(),
FunctionSignature.functionSignature(functionNode),
null,
null);
return new MethodEmitter(this, mv, functionNode);
}
/**
* Start generating the <clinit> method in the class
*
* @return method emitter to use for weaving <clinit>
*/
public MethodEmitter clinit() {
return method(EnumSet.of(Flag.STATIC), CLINIT.tag(), void.class);
}
/**
* Start generating an <init>()V method in the class
*
* @return method emitter to use for weaving <init>()V
*/
public MethodEmitter init() {
return method(INIT.tag(), void.class);
}
/**
* Start generating an <init>()V method in the class
*
* @param ptypes parameter types for constructor
* @return method emitter to use for weaving <init>()V
*/
public MethodEmitter init(final Class<?>... ptypes) {
return method(INIT.tag(), void.class, ptypes);
}
/**
* Start generating an <init>(...)V method in the class
*
* @param flags access flags for the constructor
* @param ptypes parameter types for the constructor
*
* @return method emitter to use for weaving <init>(...)V
*/
public MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
return method(flags, INIT.tag(), void.class, ptypes);
}
/**
* Add a field to the class, initialized to a value
*
* @param fieldFlags flags, e.g. should it be static or public etc
* @param fieldName name of field
* @param fieldType the type of the field
* @param value the value
*
* @see ClassEmitter.Flag
*/
public final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType, final Object value) {
cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd();
}
/**
* Add a field to the class
*
* @param fieldFlags access flags for the field
* @param fieldName name of field
* @param fieldType type of the field
*
* @see ClassEmitter.Flag
*/
public final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType) {
field(fieldFlags, fieldName, fieldType, null);
}
/**
* Add a field to the class - defaults to public
*
* @param fieldName name of field
* @param fieldType type of field
*/
public final void field(final String fieldName, final Class<?> fieldType) {
field(EnumSet.of(Flag.PUBLIC), fieldName, fieldType, null);
}
/**
* Return a bytecode array from this ClassEmitter. The ClassEmitter must
* have been ended (having its end function called) for this to work.
*
* @return byte code array for generated class, null if class generation hasn't been ended with {@link ClassEmitter#end()}
*/
public byte[] toByteArray() {
assert classEnded;
if (!classEnded) {
return null;
}
return cw.toByteArray();
}
/**
* Abstraction for flags used in class emission
*
* We provide abstraction separating these from the underlying bytecode
* emitter.
*
* Flags are provided for method handles, protection levels, static/virtual
* fields/methods.
*/
public static enum Flag {
/** method handle with static access */
HANDLE_STATIC(H_INVOKESTATIC),
/** method handle with new invoke special access */
HANDLE_NEWSPECIAL(H_NEWINVOKESPECIAL),
/** method handle with invoke special access */
HANDLE_SPECIAL(H_INVOKESPECIAL),
/** method handle with invoke virtual access */
HANDLE_VIRTUAL(H_INVOKEVIRTUAL),
/** method handle with invoke interface access */
HANDLE_INTERFACE(H_INVOKEINTERFACE),
/** final access */
FINAL(ACC_FINAL),
/** static access */
STATIC(ACC_STATIC),
/** public access */
PUBLIC(ACC_PUBLIC),
/** private access */
PRIVATE(ACC_PRIVATE);
private int value;
private Flag(final int value) {
this.value = value;
}
/**
* Get the value of this flag
* @return the int value
*/
public int getValue() {
return value;
}
/**
* Return the corresponding ASM flag value for an enum set of flags
*
* @param flags enum set of flags
* @return an integer value representing the flags intrinsic values or:ed together
*/
public static int getValue(final EnumSet<Flag> flags) {
int v = 0;
for (final Flag flag : flags) {
v |= flag.getValue();
}
return v;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
/**
* Used to track split class compilation.
*/
public class CompileUnit {
/** Current class name */
private final String className;
/** Current class generator */
private final ClassEmitter classEmitter;
private long weight;
CompileUnit(final String className, final ClassEmitter classEmitter) {
this(className, classEmitter, 0L);
}
CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) {
this.className = className;
this.classEmitter = classEmitter;
this.weight = initialWeight;
}
/**
* Add weight to this compile unit
* @param w weight to add
*/
public void addWeight(final long w) {
this.weight += w;
}
/**
* Get the current weight of the compile unit.
* @return the unit's weight
*/
public long getWeight() {
return weight;
}
/**
* Check if this compile unit can hold {@code weight} more units of weight
* @param w weight to check if can be added
* @return true if weight fits in this compile unit
*/
public boolean canHold(final long w) {
return (this.weight + w) < Splitter.SPLIT_THRESHOLD;
}
/**
* Get the class emitter for this compile unit
* @return class emitter
*/
public ClassEmitter getClassEmitter() {
return classEmitter;
}
/**
* Get the class name for this compile unit
* @return the class name
*/
public String getUnitClassName() {
return className;
}
@Override
public String toString() {
return "[classname=" + className + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']';
}
}

View File

@ -0,0 +1,685 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME;
import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT;
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.debug.ASTWriter;
import jdk.nashorn.internal.ir.debug.PrintVisitor;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Parser;
import jdk.nashorn.internal.runtime.CodeInstaller;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.ECMAErrors;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.Source;
import jdk.nashorn.internal.runtime.linker.Mangler;
import jdk.nashorn.internal.runtime.options.Options;
/**
* Responsible for converting JavaScripts to java byte code. Main entry
* point for code generator
*/
public final class Compiler {
/** Compiler states available */
public enum State {
/** compiler is ready */
INITIALIZED,
/** method has been parsed */
PARSED,
/** method has been lowered */
LOWERED,
/** method has been emitted to bytecode */
EMITTED
}
/** Current context */
private final Context context;
/** Currently compiled source */
private final Source source;
/** Current error manager */
private final ErrorManager errors;
/** Names uniqueName for this compile. */
private final Namespace namespace;
/** Current function node, or null if compiling from source until parsed */
private FunctionNode functionNode;
/** Current compiler state */
private final EnumSet<State> state;
/** Name of the scripts package */
public static final String SCRIPTS_PACKAGE = "jdk/nashorn/internal/scripts";
/** Name of the objects package */
public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
/** Name of the Global object, cannot be referred to as .class, @see CodeGenerator */
public static final String GLOBAL_OBJECT = OBJECTS_PACKAGE + '/' + "Global";
/** Name of the ScriptObjectImpl, cannot be referred to as .class @see FunctionObjectCreator */
public static final String SCRIPTOBJECT_IMPL_OBJECT = OBJECTS_PACKAGE + '/' + "ScriptFunctionImpl";
/** Name of the Trampoline, cannot be referred to as .class @see FunctionObjectCreator */
public static final String TRAMPOLINE_OBJECT = OBJECTS_PACKAGE + '/' + "Trampoline";
/** Compile unit (class) table. */
private final Set<CompileUnit> compileUnits;
/** All the "complex" constants used in the code. */
private final ConstantData constantData;
static final DebugLogger LOG = new DebugLogger("compiler");
/** Script name */
private String scriptName;
/** Should we dump classes to disk and compile only? */
private final boolean dumpClass;
/** Code map class name -> byte code for all classes generated from this Source or FunctionNode */
private Map<String, byte[]> code;
/** Are we compiling in strict mode? */
private boolean strict;
/** Is this a lazy compilation - i.e. not from source, but jitting a previously parsed FunctionNode? */
private boolean isLazy;
/** Lazy jitting is disabled by default */
private static final boolean LAZY_JIT = false;
/**
* Should we use integers for literals and all operations
* that are based in constant and parameter assignment as
* long as they can be proven not to overflow? With this enabled
* var x = 17 would tag x as an integer, rather than a double,
* but as soon as it is used in an operation that may potentially
* overflow, such as an add, we conservatively widen it to double
* (number type). This is because overflow checks in the code
* are likely much more expensive that method specialization
*
* @return true if numbers should start as ints, false if they should
* start as doubles
*/
static boolean shouldUseIntegers() {
return USE_INTS;
}
private static final boolean USE_INTS;
/**
* Should we use integers for arithmetic operations as well?
* TODO: We currently generate no overflow checks so this is
* disabled
*
* @see #shouldUseIntegers()
*
* @return true if arithmetic operations should not widen integer
* operands by default.
*/
static boolean shouldUseIntegerArithmetic() {
return Compiler.shouldUseIntegers() && USE_INT_ARITH;
}
private static final boolean USE_INT_ARITH;
static {
USE_INTS = !Options.getBooleanProperty("nashorn.compiler.ints.disable");
USE_INT_ARITH = Options.getBooleanProperty("nashorn.compiler.intarithmetic");
assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
}
/**
* Factory method for compiler that should compile from source to bytecode
*
* @param source the source
* @param context context
*
* @return compiler instance
*/
public static Compiler compiler(final Source source, final Context context) {
return Compiler.compiler(source, context, context.getErrors(), context._strict);
}
/**
* Factory method to get a compiler that goes from from source to bytecode
*
* @param source source code
* @param context context
* @param errors error manager
* @param strict compilation in strict mode?
*
* @return compiler instance
*/
public static Compiler compiler(final Source source, final Context context, final ErrorManager errors, final boolean strict) {
return new Compiler(source, context, errors, strict);
}
/**
* Factory method to get a compiler that goes from FunctionNode (parsed) to bytecode
* Requires previous compiler for state
*
* @param compiler primordial compiler
* @param functionNode functionNode to compile
*
* @return compiler
*/
public static Compiler compiler(final Compiler compiler, final FunctionNode functionNode) {
assert false : "lazy jit - not implemented";
final Compiler newCompiler = new Compiler(compiler);
newCompiler.state.add(State.PARSED);
newCompiler.functionNode = functionNode;
newCompiler.isLazy = true;
return compiler;
}
private Compiler(final Compiler compiler) {
this(compiler.source, compiler.context, compiler.errors, compiler.strict);
}
/**
* Constructor
*
* @param source the source to compile
* @param context context
* @param errors error manager
* @param strict compile in strict mode
*/
private Compiler(final Source source, final Context context, final ErrorManager errors, final boolean strict) {
this.source = source;
this.context = context;
this.errors = errors;
this.strict = strict;
this.namespace = new Namespace(context.getNamespace());
this.compileUnits = new HashSet<>();
this.constantData = new ConstantData();
this.state = EnumSet.of(State.INITIALIZED);
this.dumpClass = context._compile_only && context._dest_dir != null;
}
private String scriptsPackageName() {
return dumpClass ? "" : (SCRIPTS_PACKAGE + '/');
}
private int nextCompileUnitIndex() {
return compileUnits.size() + 1;
}
private String firstCompileUnitName() {
return scriptsPackageName() + scriptName;
}
private String nextCompileUnitName() {
return firstCompileUnitName() + '$' + nextCompileUnitIndex();
}
private CompileUnit addCompileUnit(final long initialWeight) {
return addCompileUnit(nextCompileUnitName(), initialWeight);
}
private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
compileUnits.add(compileUnit);
LOG.info("Added compile unit " + compileUnit);
return compileUnit;
}
private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
final ClassEmitter classEmitter = new ClassEmitter(this, unitClassName, strict);
final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight);
classEmitter.begin();
final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE));
initMethod.begin();
initMethod.load(Type.OBJECT, 0);
initMethod.newInstance(jdk.nashorn.internal.scripts.JS$.class);
initMethod.returnVoid();
initMethod.end();
return compileUnit;
}
/**
* Perform compilation
*
* @return true if successful, false otherwise - if false check the error manager
*/
public boolean compile() {
assert state.contains(State.INITIALIZED);
/** do we need to parse source? */
if (!state.contains(State.PARSED)) {
assert this.functionNode == null;
this.functionNode = new Parser(this, strict).parse(RUN_SCRIPT.tag());
state.add(State.PARSED);
debugPrintParse();
if (errors.hasErrors() || context._parse_only) {
return false;
}
assert !isLazy;
//tag lazy nodes for later code generation and trampolines
functionNode.accept(new NodeVisitor() {
@Override
public Node enter(final FunctionNode node) {
if (LAZY_JIT) {
node.setIsLazy(!node.isScript());
}
return node;
}
});
} else {
assert isLazy;
functionNode.accept(new NodeVisitor() {
@Override
public Node enter(final FunctionNode node) {
node.setIsLazy(false);
return null; //TODO do we want to do this recursively? then return "node" instead
}
});
}
assert functionNode != null;
final boolean oldStrict = strict;
try {
strict |= functionNode.isStrictMode();
if (!state.contains(State.LOWERED)) {
debugPrintAST();
functionNode.accept(new Lower(this));
state.add(State.LOWERED);
if (errors.hasErrors()) {
return false;
}
}
scriptName = computeNames();
// Main script code always goes to this compile unit. Note that since we start this with zero weight
// and add script code last this class may end up slightly larger than others, but reserving one class
// just for the main script seems wasteful.
final CompileUnit scriptCompileUnit = addCompileUnit(firstCompileUnitName(), 0l);
new Splitter(this, functionNode, scriptCompileUnit).split();
assert functionNode.getCompileUnit() == scriptCompileUnit;
/** Compute compile units */
assert strict == functionNode.isStrictMode() : "strict == " + strict + " but functionNode == " + functionNode.isStrictMode();
if (functionNode.isStrictMode()) {
strict = true;
}
try {
final CodeGenerator codegen = new CodeGenerator(this);
functionNode.accept(codegen);
codegen.generateScopeCalls();
debugPrintAST();
debugPrintParse();
} catch (final VerifyError e) {
if (context._verify_code || context._print_code) {
context.getErr().println(e.getClass().getSimpleName() + ": " + e.getMessage());
if (context._dump_on_error) {
e.printStackTrace(context.getErr());
}
} else {
throw e;
}
}
state.add(State.EMITTED);
code = new TreeMap<>();
for (final CompileUnit compileUnit : compileUnits) {
final ClassEmitter classEmitter = compileUnit.getClassEmitter();
classEmitter.end();
if (!errors.hasErrors()) {
final byte[] bytecode = classEmitter.toByteArray();
if (bytecode != null) {
code.put(compileUnit.getUnitClassName(), bytecode);
debugDisassemble();
debugVerify();
}
}
}
if (code.isEmpty()) {
return false;
}
try {
dumpClassFiles();
} catch (final IOException e) {
throw new RuntimeException(e);
}
return true;
} finally {
strict = oldStrict;
}
}
/**
* Install compiled classes into a given loader
* @param installer that takes the generated classes and puts them in the system
* @return root script class - if there are several compile units they will also be installed
*/
public Class<?> install(final CodeInstaller installer) {
assert state.contains(State.EMITTED);
assert scriptName != null;
Class<?> rootClass = null;
for (final Entry<String, byte[]> entry : code.entrySet()) {
final String className = entry.getKey();
LOG.info("Installing class " + className);
final byte[] bytecode = entry.getValue();
final Class<?> clazz = installer.install(Compiler.binaryName(className), bytecode);
if (rootClass == null && firstCompileUnitName().equals(className)) {
rootClass = clazz;
}
try {
//use reflection to write source and constants table to installed classes
clazz.getField(SOURCE.tag()).set(null, source);
clazz.getField(CONSTANTS.tag()).set(null, constantData.toArray());
} catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
LOG.info("Root class: " + rootClass);
return rootClass;
}
/**
* Find a unit that will hold a node of the specified weight.
*
* @param weight Weight of a node
* @return Unit to hold node.
*/
CompileUnit findUnit(final long weight) {
for (final CompileUnit unit : compileUnits) {
if (unit.canHold(weight)) {
unit.addWeight(weight);
return unit;
}
}
return addCompileUnit(weight);
}
/**
* Generate a uniqueName name. Public as {@link Parser} is using this to
* create symbols in a different package
*
* @param name to base unique name on
* @return unique name
*/
public String uniqueName(final String name) {
return namespace.uniqueName(name);
}
/**
* Internal function to compute reserved names and base names for class to
* be generated
*
* @return scriptName
*/
private String computeNames() {
// Reserve internally used names.
addReservedNames();
if (dumpClass) {
// get source file name and remove ".js"
final String baseName = getSource().getName();
final int index = baseName.lastIndexOf(".js");
if (index != -1) {
return baseName.substring(0, index);
}
return baseName;
}
return namespace.getParent().uniqueName(
DEFAULT_SCRIPT_NAME.tag() +
'$' +
safeSourceName(source) +
(isLazy ? CompilerConstants.LAZY.tag() : "")
);
}
private static String safeSourceName(final Source source) {
String baseName = new File(source.getName()).getName();
final int index = baseName.lastIndexOf(".js");
if (index != -1) {
baseName = baseName.substring(0, index);
}
baseName = baseName.replace('.', '_').replace('-', '_');
final String mangled = Mangler.mangle(baseName);
baseName = mangled != null ? mangled : baseName;
return baseName;
}
static void verify(final Context context, final byte[] code) {
context.verify(code);
}
/**
* Fill in the namespace with internally reserved names.
*/
private void addReservedNames() {
namespace.uniqueName(SCOPE.tag());
namespace.uniqueName(THIS.tag());
}
/**
* Get the constant data for this Compiler
*
* @return the constant data
*/
public ConstantData getConstantData() {
return constantData;
}
/**
* Get the Context used for Compilation
* @see Context
* @return the context
*/
public Context getContext() {
return context;
}
/**
* Get the Source being compiled
* @see Source
* @return the source
*/
public Source getSource() {
return source;
}
/**
* Get the error manager used for this compiler
* @return the error manager
*/
public ErrorManager getErrors() {
return errors;
}
/**
* Get the namespace used for this Compiler
* @see Namespace
* @return the namespace
*/
public Namespace getNamespace() {
return namespace;
}
/*
* Debugging
*/
/**
* Print the AST before or after lowering, see --print-ast, --print-lower-ast
*/
private void debugPrintAST() {
assert functionNode != null;
if (context._print_lower_ast && state.contains(State.LOWERED) ||
context._print_ast && !state.contains(State.LOWERED)) {
context.getErr().println(new ASTWriter(functionNode));
}
}
/**
* Print the parsed code before or after lowering, see --print-parse, --print-lower-parse
*/
private boolean debugPrintParse() {
if (errors.hasErrors()) {
return false;
}
assert functionNode != null;
if (context._print_lower_parse && state.contains(State.LOWERED) ||
context._print_parse && !state.contains(State.LOWERED)) {
final PrintVisitor pv = new PrintVisitor();
functionNode.accept(pv);
context.getErr().print(pv);
context.getErr().flush();
}
return true;
}
private void debugDisassemble() {
assert code != null;
if (context._print_code) {
for (final Map.Entry<String, byte[]> entry : code.entrySet()) {
context.getErr().println("CLASS: " + entry.getKey());
context.getErr().println();
ClassEmitter.disassemble(context, entry.getValue());
context.getErr().println("======");
}
}
}
private void debugVerify() {
if (context._verify_code) {
for (final Map.Entry<String, byte[]> entry : code.entrySet()) {
Compiler.verify(context, entry.getValue());
}
}
}
/**
* Implements the "-d" option - dump class files from script to specified output directory
*
* @throws IOException if classes cannot be written
*/
private void dumpClassFiles() throws IOException {
if (context._dest_dir == null) {
return;
}
assert code != null;
for (final Entry<String, byte[]> entry : code.entrySet()) {
final String className = entry.getKey();
final String fileName = className.replace('.', File.separatorChar) + ".class";
final int index = fileName.lastIndexOf(File.separatorChar);
if (index != -1) {
final File dir = new File(fileName.substring(0, index));
if (!dir.exists() && !dir.mkdirs()) {
throw new IOException(ECMAErrors.getMessage("io.error.cant.write", dir.toString()));
}
}
final byte[] bytecode = entry.getValue();
final File outFile = new File(context._dest_dir, fileName);
try (final FileOutputStream fos = new FileOutputStream(outFile)) {
fos.write(bytecode);
}
}
}
/**
* Convert a package/class name to a binary name.
*
* @param name Package/class name.
* @return Binary name.
*/
public static String binaryName(final String name) {
return name.replace('/', '.');
}
/**
* Convert a binary name to a package/class name.
*
* @param name Binary name.
* @return Package/class name.
*/
public static String pathName(final String name) {
return name.replace('.', '/');
}
}

View File

@ -0,0 +1,687 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.Source;
/**
* This class represents constant names of variables, methods and fields in
* the compiler
*/
public enum CompilerConstants {
/** the __FILE__ variable */
__FILE__,
/** the __DIR__ variable */
__DIR__,
/** the __LINE__ variable */
__LINE__,
/** lazy prefix for classes of jitted methods */
LAZY("Lazy"),
/** leaf tag used for functions that require no scope */
LEAF("__leaf__"),
/** constructor name */
INIT("<init>"),
/** static initializer name */
CLINIT("<clinit>"),
/** eval name */
EVAL("eval"),
/** source name and class */
SOURCE("source", Source.class),
/** constants name and class */
CONSTANTS("constants", Object[].class),
/** strict mode field name and type */
STRICT_MODE("strictMode", boolean.class),
/** default script name */
DEFAULT_SCRIPT_NAME("Script"),
/** function prefix for anonymous functions */
FUNCTION_PREFIX("function$"),
/** method name for Java method that is script entry point */
RUN_SCRIPT("runScript"),
/** this name and slot */
THIS("this", 0),
/** this debugger symbol */
THIS_DEBUGGER("__this__"),
/** scope name, type and slot */
SCOPE("__scope__", ScriptObject.class, 2),
/** the return value variable name were intermediate results are stored for scripts */
SCRIPT_RETURN("__return__"),
/** the callee value variable when necessary */
CALLEE("__callee__", ScriptFunction.class, 1),
/** the varargs variable when necessary */
VARARGS("__varargs__"),
/** the arguments vector when necessary and the slot */
ARGUMENTS("arguments", Object.class, 2),
/** prefix for iterators for for (x in ...) */
ITERATOR_PREFIX("$iter"),
/** prefix for tag variable used for switch evaluation */
SWITCH_TAG_PREFIX("$tag"),
/** prefix for all exceptions */
EXCEPTION_PREFIX("$exception"),
/** prefix for quick slots generated in Store */
QUICK_PREFIX("$quick"),
/** prefix for temporary variables */
TEMP_PREFIX("$temp"),
/** prefix for literals */
LITERAL_PREFIX("$lit"),
/** prefix for map */
MAP("$map", 1),
/** prefix for regexps */
REGEX_PREFIX("$regex"),
/** init scope */
INIT_SCOPE("$scope", 2),
/** init arguments */
INIT_ARGUMENTS("$arguments", 3),
/** prefix for all ScriptObject subclasses with fields, @see ObjectGenerator */
JS_OBJECT_PREFIX("JO$"),
/** name for allocate method in JO$ objects */
ALLOCATE("allocate"),
/** prefix for split methods, @see Splitter */
SPLIT_PREFIX("$split"),
/** prefix for split array method and slot */
SPLIT_ARRAY_ARG("split_array", 3),
/** get string from constant pool */
GET_STRING("$getString"),
/** get map */
GET_MAP("$getMap"),
/** get map */
SET_MAP("$setMap"),
/** get array prefix */
GET_ARRAY_PREFIX("$get"),
/** get array suffix */
GET_ARRAY_SUFFIX("$array");
private final String tag;
private final Class<?> type;
private final int slot;
private CompilerConstants() {
this.tag = name();
this.type = null;
this.slot = -1;
}
private CompilerConstants(final String tag) {
this(tag, -1);
}
private CompilerConstants(final String tag, final int slot) {
this(tag, null, slot);
}
private CompilerConstants(final String tag, final Class<?> type) {
this(tag, type, -1);
}
private CompilerConstants(final String tag, final Class<?> type, final int slot) {
this.tag = tag;
this.type = type;
this.slot = slot;
}
/**
* Return the tag for this compile constant. Deliberately avoiding "name" here
* not to conflate with enum implementation. This is the master string for the
* constant - every constant has one.
*
* @return the tag
*/
public final String tag() {
return tag;
}
/**
* Return the type for this compile constant
*
* @return type for this constant's instances, or null if N/A
*/
public final Class<?> type() {
return type;
}
/**
* Return the slot for this compile constant
*
* @return byte code slot where constant is stored or -1 if N/A
*/
public final int slot() {
return slot;
}
/**
* Return a descriptor for this compile constant. Only relevant if it has
* a type
*
* @return descriptor the descriptor
*/
public final String descriptor() {
assert type != null : " asking for descriptor of typeless constant";
return typeDescriptor(type);
}
/**
* Get the internal class name for a type
*
* @param type a type
* @return the internal name for this type
*/
public static String className(final Class<?> type) {
return Type.getInternalName(type);
}
/**
* Get the method descriptor for a given method type collection
*
* @param rtype return type
* @param ptypes parameter types
*
* @return internal descriptor for this method
*/
public static String methodDescriptor(final Class<?> rtype, final Class<?>... ptypes) {
return Type.getMethodDescriptor(rtype, ptypes);
}
/**
* Get the type descriptor for a type
*
* @param clazz a type
*
* @return the internal descriptor for this type
*/
public static String typeDescriptor(final Class<?> clazz) {
return Type.getDescriptor(clazz);
}
/**
* Create a call representing a void constructor for a given type. Don't
* attempt to look this up at compile time
*
* @param clazz the class
*
* @return Call representing void constructor for type
*/
public static Call constructorNoLookup(final Class<?> clazz) {
return specialCallNoLookup(clazz, INIT.tag(), void.class);
}
/**
* Create a call representing a constructor for a given type. Don't
* attempt to look this up at compile time
*
* @param className the type class name
* @param ptypes the parameter types for the constructor
*
* @return Call representing constructor for type
*/
public static Call constructorNoLookup(final String className, final Class<?>... ptypes) {
return specialCallNoLookup(className, INIT.tag(), methodDescriptor(void.class, ptypes));
}
/**
* Create a call representing a constructor for a given type. Don't
* attempt to look this up at compile time
*
* @param clazz the class name
* @param ptypes the parameter types for the constructor
*
* @return Call representing constructor for type
*/
public static Call constructorNoLookup(final Class<?> clazz, final Class<?>... ptypes) {
return specialCallNoLookup(clazz, INIT.tag(), void.class, ptypes);
}
/**
* Create a call representing an invokespecial to a given method. Don't
* attempt to look this up at compile time
*
* @param className the class name
* @param name the method name
* @param desc the descriptor
*
* @return Call representing specified invokespecial call
*/
public static Call specialCallNoLookup(final String className, final String name, final String desc) {
return new Call(null, className, name, desc) {
@Override
public MethodEmitter invoke(final MethodEmitter method) {
return method.invokeSpecial(className, name, descriptor);
}
};
}
/**
* Create a call representing an invokespecial to a given method. Don't
* attempt to look this up at compile time
*
* @param clazz the class
* @param name the method name
* @param rtype the return type
* @param ptypes the parameter types
*
* @return Call representing specified invokespecial call
*/
public static Call specialCallNoLookup(final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return specialCallNoLookup(className(clazz), name, methodDescriptor(rtype, ptypes));
}
/**
* Create a call representing an invokestatic to a given method. Don't
* attempt to look this up at compile time
*
* @param className the class name
* @param name the method name
* @param desc the descriptor
*
* @return Call representing specified invokestatic call
*/
public static Call staticCallNoLookup(final String className, final String name, final String desc) {
return new Call(null, className, name, desc) {
@Override
public MethodEmitter invoke(final MethodEmitter method) {
return method.invokeStatic(className, name, descriptor);
}
};
}
/**
* Create a call representing an invokestatic to a given method. Don't
* attempt to look this up at compile time
*
* @param clazz the class
* @param name the method name
* @param rtype the return type
* @param ptypes the parameter types
*
* @return Call representing specified invokestatic call
*/
public static Call staticCallNoLookup(final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return staticCallNoLookup(className(clazz), name, methodDescriptor(rtype, ptypes));
}
/**
* Create a call representing an invokevirtual to a given method. Don't
* attempt to look this up at compile time
*
* @param clazz the class
* @param name the method name
* @param rtype the return type
* @param ptypes the parameter types
*
* @return Call representing specified invokevirtual call
*/
public static Call virtualCallNoLookup(final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return new Call(null, className(clazz), name, methodDescriptor(rtype, ptypes)) {
@Override
public MethodEmitter invoke(final MethodEmitter method) {
return method.invokeVirtual(className, name, descriptor);
}
};
}
/**
* Create a call representing an invokeinterface to a given method. Don't
* attempt to look this up at compile time
*
* @param clazz the class
* @param name the method name
* @param rtype the return type
* @param ptypes the parameter types
*
* @return Call representing specified invokeinterface call
*/
public static Call interfaceCallNoLookup(final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return new Call(null, className(clazz), name, methodDescriptor(rtype, ptypes)) {
@Override
public MethodEmitter invoke(final MethodEmitter method) {
return method.invokeInterface(className, name, descriptor);
}
};
}
/**
* Create a FieldAccess representing a virtual field, that can be subject to put
* or get operations
*
* @param className name of the class where the field is a member
* @param name name of the field
* @param desc type descriptor of the field
*
* @return a field access object giving access code generation method for the virtual field
*/
public static FieldAccess virtualField(final String className, final String name, final String desc) {
return new FieldAccess(className, name, desc) {
@Override
public MethodEmitter get(final MethodEmitter method) {
return method.getField(className, name, descriptor);
}
@Override
public void put(final MethodEmitter method) {
method.putField(className, name, descriptor);
}
};
}
/**
* Create a FieldAccess representing a virtual field, that can be subject to put
* or get operations
*
* @param clazz class where the field is a member
* @param name name of the field
* @param type type of the field
*
* @return a field access object giving access code generation method for the virtual field
*/
public static FieldAccess virtualField(final Class<?> clazz, final String name, final Class<?> type) {
return virtualField(className(clazz), name, typeDescriptor(type));
}
/**
* Create a FieldAccess representing a static field, that can be subject to put
* or get operations
*
* @param className name of the class where the field is a member
* @param name name of the field
* @param desc type descriptor of the field
*
* @return a field access object giving access code generation method for the static field
*/
public static FieldAccess staticField(final String className, final String name, final String desc) {
return new FieldAccess(className, name, desc) {
@Override
public MethodEmitter get(final MethodEmitter method) {
return method.getStatic(className, name, descriptor);
}
@Override
public void put(final MethodEmitter method) {
method.putStatic(className, name, descriptor);
}
};
}
/**
* Create a FieldAccess representing a static field, that can be subject to put
* or get operations
*
* @param clazz class where the field is a member
* @param name name of the field
* @param type type of the field
*
* @return a field access object giving access code generation method for the virtual field
*/
public static FieldAccess staticField(final Class<?> clazz, final String name, final Class<?> type) {
return staticField(className(clazz), name, typeDescriptor(type));
}
/**
* Create a static call, looking up the method handle for it at the same time
*
* @param clazz the class
* @param name the name of the method
* @param rtype the return type of the method
* @param ptypes the parameter types of the method
*
* @return the call object representing the static call
*/
public static Call staticCall(final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return staticCall(MethodHandles.publicLookup(), clazz, name, rtype, ptypes);
}
/**
* Create a static call, given an explicit lookup, looking up the method handle for it at the same time
*
* @param lookup the lookup
* @param clazz the class
* @param name the name of the method
* @param rtype the return type
* @param ptypes the parameter types
*
* @return the call object representing the static call
*/
public static Call staticCall(final MethodHandles.Lookup lookup, final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return new Call(MH.findStatic(lookup, clazz, name, MH.type(rtype, ptypes)), className(clazz), name, methodDescriptor(rtype, ptypes)) {
@Override
public MethodEmitter invoke(final MethodEmitter method) {
return method.invokeStatic(className, name, descriptor);
}
};
}
/**
* Create a virtual call, looking up the method handle for it at the same time
*
* @param clazz the class
* @param name the name of the method
* @param rtype the return type of the method
* @param ptypes the parameter types of the method
*
* @return the call object representing the virtual call
*/
public static Call virtualCall(final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return virtualCall(MethodHandles.publicLookup(), clazz, name, rtype, ptypes);
}
/**
* Create a virtual call, given an explicit lookup, looking up the method handle for it at the same time
*
* @param lookup the lookup
* @param clazz the class
* @param name the name of the method
* @param rtype the return type
* @param ptypes the parameter types
*
* @return the call object representing the virtual call
*/
public static Call virtualCall(final MethodHandles.Lookup lookup, final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
return new Call(MH.findVirtual(lookup, clazz, name, MH.type(rtype, ptypes)), className(clazz), name, methodDescriptor(rtype, ptypes)) {
@Override
public MethodEmitter invoke(final MethodEmitter method) {
return method.invokeVirtual(className, name, descriptor);
}
};
}
/**
* Private class representing an access. This can generate code into a method code or
* a field access.
*/
private abstract static class Access {
protected final MethodHandle methodHandle;
protected final String className;
protected final String name;
protected final String descriptor;
/**
* Constructor
*
* @param methodHandle methodHandle or null if none
* @param className class name for access
* @param name field or method name for access
* @param descriptor descriptor for access field or method
*/
protected Access(final MethodHandle methodHandle, final String className, final String name, final String descriptor) {
this.methodHandle = methodHandle;
this.className = className;
this.name = name;
this.descriptor = descriptor;
}
/**
* Get the method handle, or null if access hasn't been looked up
*
* @return method handle
*/
public MethodHandle methodHandle() {
return methodHandle;
}
/**
* Get the class name of the access
*
* @return the class name
*/
public String className() {
return className;
}
/**
* Get the field name or method name of the access
*
* @return the name
*/
public String name() {
return name;
}
/**
* Get the descriptor of the method or field of the access
*
* @return the descriptor
*/
public String descriptor() {
return descriptor;
}
}
/**
* Field access - this can be used for generating code for static or
* virtual field accesses
*/
public abstract static class FieldAccess extends Access {
/**
* Constructor
*
* @param className name of the class where the field is
* @param name name of the field
* @param descriptor descriptor of the field
*/
protected FieldAccess(final String className, final String name, final String descriptor) {
super(null, className, name, descriptor);
}
/**
* Generate get code for the field
*
* @param emitter a method emitter
*
* @return the method emitter
*/
protected abstract MethodEmitter get(final MethodEmitter emitter);
/**
* Generate put code for the field
*
* @param emitter a method emitter
*/
protected abstract void put(final MethodEmitter emitter);
}
/**
* Call - this can be used for generating code for different types of calls
*/
public abstract static class Call extends Access {
/**
* Constructor
*
* @param className class name for the method of the call
* @param name method name
* @param descriptor method descriptor
*/
protected Call(final String className, final String name, final String descriptor) {
super(null, className, name, descriptor);
}
/**
* Constructor
*
* @param methodHandle method handle for the call if resolved
* @param className class name for the method of the call
* @param name method name
* @param descriptor method descriptor
*/
protected Call(final MethodHandle methodHandle, final String className, final String name, final String descriptor) {
super(methodHandle, className, name, descriptor);
}
/**
* Generate invocation code for the method
*
* @param emitter a method emitter
*
* @return the method emitter
*/
protected abstract MethodEmitter invoke(final MethodEmitter emitter);
}
}

View File

@ -0,0 +1,165 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Manages constants needed by code generation. Objects are maintained in an
* interning maps to remove duplicates.
*/
public class ConstantData {
/** Constant table. */
final List<Object> constants;
/** Constant table string interning map. */
final Map<String, Integer> stringMap;
/** Constant table object interning map. */
final Map<Object, Integer> objectMap;
private static class ArrayWrapper {
private final Object array;
private final int hashCode;
public ArrayWrapper(final Object array) {
this.array = array;
this.hashCode = calcHashCode();
}
/**
* Calculate a shallow hashcode for the array.
* @return Hashcode with elements factored in.
*/
private int calcHashCode() {
final Class<?> cls = array.getClass();
if (cls == Object[].class) {
return Arrays.hashCode((Object[])array);
} else if (cls == double[].class) {
return Arrays.hashCode((double[])array);
} if (cls == long[].class) {
return Arrays.hashCode((long[])array);
} if (cls == int[].class) {
return Arrays.hashCode((int[])array);
}
throw new AssertionError("ConstantData doesn't support " + cls);
}
@Override
public boolean equals(final Object other) {
if (!(other instanceof ArrayWrapper)) {
return false;
}
final Object otherArray = ((ArrayWrapper)other).array;
if (array == otherArray) {
return true;
}
final Class<?> cls = array.getClass();
if (cls == otherArray.getClass()) {
if (cls == Object[].class) {
return Arrays.equals((Object[])array, (Object[])otherArray);
} else if (cls == double[].class) {
return Arrays.equals((double[])array, (double[])otherArray);
} else if (cls == long[].class) {
return Arrays.equals((long[])array, (long[])otherArray);
} else if (cls == int[].class) {
return Arrays.equals((int[])array, (int[])otherArray);
}
}
return false;
}
@Override
public int hashCode() {
return hashCode;
}
}
/**
* Constructor
*/
ConstantData() {
this.constants = new ArrayList<>();
this.stringMap = new HashMap<>();
this.objectMap = new HashMap<>();
}
/**
* Add a string to the constant data
*
* @param string the string to add
* @return the index in the constant pool that the string was given
*/
public int add(final String string) {
final Integer value = stringMap.get(string);
if (value != null) {
return value.intValue();
}
constants.add(string);
final int index = constants.size() - 1;
stringMap.put(string, index);
return index;
}
/**
* Add an object to the constant data
*
* @param object the string to add
* @return the index in the constant pool that the object was given
*/
public int add(final Object object) {
final Object entry = object.getClass().isArray() ? new ArrayWrapper(object) : object;
final Integer value = objectMap.get(entry);
if (value != null) {
return value.intValue();
}
constants.add(object);
final int index = constants.size() - 1;
objectMap.put(entry, index);
return index;
}
Object[] toArray() {
return constants.toArray();
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
/**
* Interface for anything that interacts with a low level bytecode
* generation module, for example ASM.
* <p>
* This is pretty generic, i.e. it can be a ClassEmitter, MethodEmitter
* or potentially even more fine grained stuff.
*
*/
public interface Emitter {
/**
* Register the start of emission for this CodeEmitter
*/
public void begin();
/**
* Register the end of emission for this CodeEmitter.
* This is typically required before generated code can
* be requested from it
*/
public void end();
}

View File

@ -0,0 +1,196 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jdk.nashorn.internal.ir.Symbol;
/**
* Tracks the variable area state.
*
*/
public final class Frame {
/** Previous frame. */
private Frame previous;
/** Current variables. */
private final ArrayList<Symbol> symbols;
/** Number of slots in previous frame. */
private int baseCount;
/** Number of slots in this frame. */
private int count;
/**
* Constructor.
*
* @param previous frame, the parent variable frame
*/
public Frame(final Frame previous) {
this.previous = previous;
this.symbols = new ArrayList<>();
this.baseCount = getBaseCount();
this.count = 0;
}
/**
* Copy constructor
* @param frame
* @param symbols
*/
private Frame(final Frame frame, final List<Symbol> symbols) {
this.previous = frame.getPrevious() == null ? null : new Frame(frame.getPrevious(), frame.getPrevious().getSymbols());
this.symbols = new ArrayList<>(frame.getSymbols());
this.baseCount = frame.getBaseCount();
this.count = frame.getCount();
}
/**
* Copy the frame
*
* @return a new frame with the identical contents
*/
public Frame copy() {
return new Frame(this, getSymbols());
}
/**
* Add a new variable to the frame.
* @param symbol Symbol representing variable.
*/
public void addSymbol(final Symbol symbol) {
final int slot = symbol.getSlot();
if (slot < 0) {
symbols.add(symbol);
count += symbol.slotCount();
}
}
/**
* Realign slot numbering prior to code generation.
* @return Number of slots in frame.
*/
public int realign() {
baseCount = getBaseCount();
count = 0;
for (final Symbol symbol : symbols) {
if (symbol.hasSlot()) {
symbol.setSlot(baseCount + count);
count += symbol.slotCount();
}
}
return count;
}
/**
* Return the slot count of previous frames.
* @return Number of slots in previous frames.
*/
private int getBaseCount() {
return previous != null ? previous.getSlotCount() : 0;
}
/**
* Determine the number of slots to top of frame.
* @return Number of slots in total.
*/
public int getSlotCount() {
return baseCount + count;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
Frame f = this;
boolean hasPrev = false;
int pos = 0;
do {
if (hasPrev) {
sb.append("\n");
}
sb.append("#").
append(pos++).
append(" {baseCount:").
append(baseCount).
append(", ").
append("count:").
append(count).
append("} ");
for (final Symbol var : f.getSymbols()) {
sb.append('[').
append(var.toString()).
append(' ').
append(var.hashCode()).
append("] ");
}
f = f.getPrevious();
hasPrev = true;
} while (f != null);
return sb.toString();
}
/**
* Get variable count for this frame
* @return variable count
*/
public int getCount() {
return count;
}
/**
* Get previous frame
* @return previous frame
*/
public Frame getPrevious() {
return previous;
}
/**
* Set previous frame
* @param previous previous frame
*/
public void setPrevious(final Frame previous) {
this.previous = previous;
}
/**
* Get symbols in frame
* @return a list of symbols in this frame
*/
public List<Symbol> getSymbols() {
return Collections.unmodifiableList(symbols);
}
}

View File

@ -0,0 +1,209 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
/**
* Class that generates function signatures for dynamic calls
*/
public final class FunctionSignature {
/** parameter types that ASM can understand */
private final Type[] paramTypes;
/** return type that ASM can understand */
private final Type returnType;
/** valid Java descriptor string for function */
private final String descriptor;
/**
* Constructor
*
* Create a FunctionSignature given arguments as AST Nodes
*
* @param hasSelf does the function have a self slot?
* @param retType what is the return type
* @param args argument list of AST Nodes
*/
public FunctionSignature(final boolean hasSelf, final Type retType, final List<? extends Node> args) {
this(hasSelf, false, retType, FunctionSignature.typeArray(args));
}
/**
* Constructor
*
* Create a FunctionSignature given arguments as AST Nodes
*
* @param hasSelf does the function have a self slot?
* @param hasCallee does the function need a callee variable
* @param retType what is the return type
* @param args argument list of AST Nodes
*/
public FunctionSignature(final boolean hasSelf, final boolean hasCallee, final Type retType, final List<? extends Node> args) {
this(hasSelf, hasCallee, retType, FunctionSignature.typeArray(args));
}
/**
* Constructor
*
* Create a FunctionSignature given arguments as AST Nodes
*
* @param hasSelf does the function have a self slot?
* @param retType what is the return type
* @param nArgs number of arguments
*/
public FunctionSignature(final boolean hasSelf, final Type retType, final int nArgs) {
this(hasSelf, false, retType, FunctionSignature.objectArgs(nArgs));
}
/**
* Constructor
*
* Create a FunctionSignature given argument types only
*
* @param hasSelf does the function have a self slot?
* @param hasCallee does the function have a callee slot?
* @param retType what is the return type
* @param argTypes argument list of AST Nodes
*/
public FunctionSignature(final boolean hasSelf, final boolean hasCallee, final Type retType, final Type... argTypes) {
final boolean isVarArg;
int count = 1;
if (argTypes == null) {
isVarArg = true;
} else {
isVarArg = argTypes.length > LinkerCallSite.ARGLIMIT;
count = isVarArg ? 1 : argTypes.length;
}
int first = 0;
if (hasSelf) {
count++;
first++;
}
if (hasCallee) {
count++;
first++;
}
paramTypes = new Type[count];
if (hasSelf) {
paramTypes[THIS.slot()] = Type.OBJECT;
}
if (hasCallee) {
paramTypes[CALLEE.slot()] = Type.typeFor(ScriptFunction.class);
}
if (isVarArg) {
paramTypes[first] = Type.OBJECT_ARRAY;
} else if (argTypes != null) {
for (int i = first, j = 0; i < count; i++, j++) {
paramTypes[i] = argTypes[j];
if (paramTypes[i].isObject()) {
paramTypes[i] = Type.OBJECT; //TODO: for now, turn java/lang/String into java/lang/Object as we aren't as specific.
}
}
} else {
assert false : "isVarArgs cannot be false when argTypes are null";
}
returnType = retType;
descriptor = Type.getMethodDescriptor(returnType, paramTypes);
}
/**
* Internal function that converts an array of nodes to their Types
*
* @param args node arg list
*
* @return the array of types
*/
private static Type[] typeArray(final List<? extends Node> args) {
if (args == null) {
return null;
}
final Type[] typeArray = new Type[args.size()];
int pos = 0;
for (final Node arg : args) {
typeArray[pos++] = arg.getType();
}
return typeArray;
}
@Override
public String toString() {
return descriptor;
}
/**
* @return the number of param types
*/
public int size() {
return paramTypes.length;
}
/**
* Returns the generic signature of the function being compiled.
*
* @param functionNode function being compiled.
* @return function signature.
*/
public static String functionSignature(final FunctionNode functionNode) {
return new FunctionSignature(
true,
functionNode.needsCallee(),
functionNode.getReturnType(),
(functionNode.isVarArg() && !functionNode.isScript()) ?
null :
functionNode.getParameters()).toString();
}
private static Type[] objectArgs(final int nArgs) {
final Type[] array = new Type[nArgs];
for (int i = 0; i < nArgs; i++) {
array[i] = Type.OBJECT;
}
return array;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import java.util.HashMap;
/**
* A name space hierarchy, where each level holds a name directory with
* names that may be unique for each level.
*/
public class Namespace {
/** Parent namespace. */
private final Namespace parent;
/** Name directory - version count for each name */
private final HashMap<String, Integer> directory;
/**
* Constructor
*/
public Namespace() {
this(null);
}
/**
* Constructor
*
* @param parent parent name space
*/
public Namespace(final Namespace parent) {
this.parent = parent;
directory = new HashMap<>();
}
/**
* Return the parent Namespace of this space.
*
* @return parent name space
*/
public Namespace getParent() {
return parent;
}
private HashMap<String, Integer> getDirectory() {
return directory;
}
/**
* Create a uniqueName name in the namespace in the form base$n where n varies
* .
* @param base Base of name. Base will be returned if uniqueName.
*
* @return Generated uniqueName name.
*/
public String uniqueName(final String base) {
for (Namespace namespace = this; namespace != null; namespace = namespace.getParent()) {
final HashMap<String, Integer> namespaceDirectory = namespace.getDirectory();
final Integer counter = namespaceDirectory.get(base);
if (counter != null) {
final int count = counter + 1;
namespaceDirectory.put(base, count);
return base + "$" + count;
}
}
directory.put(base, 0);
return base;
}
@Override
public String toString() {
return directory.toString();
}
}

View File

@ -0,0 +1,701 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import static jdk.nashorn.internal.codegen.types.Type.BOOLEAN;
import static jdk.nashorn.internal.codegen.types.Type.INT;
import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.util.HashMap;
import java.util.Map;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.RuntimeNode.Request;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.linker.Lookup;
import jdk.nashorn.internal.runtime.linker.MethodHandleFactory;
/**
* Optimistic call site that assumes its Object arguments to be of a boxed type.
* Gradually reverts to wider boxed types if the assumption for the RuntimeNode
* is proven wrong. Finally reverts to the generic ScriptRuntime method.
*
* This is used from the CodeGenerator when we have a runtime node, but 1 or more
* primitive arguments. This class generated appropriate specializations, for example
* {@code Object a === int b} is a good idea to specialize to {@code ((Integer)a).intValue() == b}
* surrounded by catch blocks that will try less narrow specializations
*/
public class RuntimeCallSite extends MutableCallSite {
static final Call BOOTSTRAP = staticCallNoLookup(RuntimeCallSite.class, "bootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
private static final MethodHandle NEXT = findOwnMH("next", MethodHandle.class);
private final RuntimeNode.Request request;
private String name;
/**
* A specialized runtime node, i.e. on where we know at least one more specific type than object
*/
static final class SpecializedRuntimeNode {
private static final char REQUEST_SEPARATOR = ':';
private final RuntimeNode.Request request;
private final Type[] parameterTypes;
private final Type returnType;
/**
* Constructor.
*
* @param request runtime node request to specialize
* @param parameterTypes parameter types of the call site
* @param returnType return type of the call site
*/
SpecializedRuntimeNode(final RuntimeNode.Request request, final Type[] parameterTypes, final Type returnType) {
this.request = request;
this.parameterTypes = parameterTypes;
this.returnType = returnType;
}
/**
* The first type to try to use for this genrated runtime node
*
* @return a type
*/
public Type firstTypeGuess() {
Type widest = Type.UNKNOWN;
for (final Type type : parameterTypes) {
if (type.isObject()) {
continue;
}
widest = Type.widest(type, widest);
}
widest = Type.widest(widest, firstTypeGuessForObject(request));
return widest;
}
private static Type firstTypeGuessForObject(final Request request) {
switch (request) {
case ADD:
return INT;
default:
return BOOLEAN;
}
}
Request getRequest() {
return request;
}
Type[] getParameterTypes() {
return parameterTypes;
}
Type getReturnType() {
return returnType;
}
private static char descFor(final Type type) {
if (type.isObject()) {
return 'O';
}
return type.getDescriptor().charAt(0);
}
@Override
public boolean equals(final Object other) {
if (other instanceof SpecializedRuntimeNode) {
final SpecializedRuntimeNode otherNode = (SpecializedRuntimeNode)other;
if (!otherNode.getReturnType().equals(getReturnType())) {
return false;
}
if (getParameterTypes().length != otherNode.getParameterTypes().length) {
return false;
}
for (int i = 0; i < getParameterTypes().length; i++) {
if (!Type.areEquivalent(getParameterTypes()[i], otherNode.getParameterTypes()[i])) {
return false;
}
}
return otherNode.getRequest().equals(getRequest());
}
return false;
}
@Override
public int hashCode() {
int hashCode = getRequest().toString().hashCode();
hashCode ^= getReturnType().hashCode();
for (final Type type : getParameterTypes()) {
hashCode ^= type.hashCode();
}
return hashCode;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append(getRequest().toString());
sb.append(REQUEST_SEPARATOR);
sb.append(descFor(getReturnType()));
for (final Type type : getParameterTypes()) {
sb.append(descFor(type));
}
return sb.toString();
}
String getName(final Type extraType) {
return toString() + "_" + descFor(extraType);
}
String getInitialName() {
return getName(firstTypeGuess());
}
}
/**
* Constructor
*
* @param type method type for call site
* @param name name of runtime call
*/
RuntimeCallSite(final MethodType type, final String name) {
super(type);
this.name = name;
this.request = Request.valueOf(name.substring(0, name.indexOf(SpecializedRuntimeNode.REQUEST_SEPARATOR)));
setTarget(makeMethod(name));
}
/**
* Bootstrapper for a specialized Runtime call
*
* @param lookup lookup
* @param initialName initial name for callsite
* @param type method type for call site
*
* @return callsite for a runtime node
*/
public static CallSite bootstrap(final MethodHandles.Lookup lookup, final String initialName, final MethodType type) {
return new RuntimeCallSite(type, initialName);
}
private String nextName(final String requestName) {
if (requestName.equals(request.toString())) {
return null;
}
final char[] c = requestName.toCharArray();
final int last = c.length - 1;
if (c[last - 1] != '_') {
return null;
}
switch (c[last]) {
case 'Z':
c[last] = 'I';
break;
case 'I':
c[last] = 'J';
break;
case 'J':
c[last] = 'D';
break;
case 'D':
default:
return request.toString();
}
return new String(c);
}
private boolean isSpecialized(final String requestName) {
return nextName(requestName) != null;
}
private MethodHandle makeMethod(final String requestName) {
MethodHandle mh;
if (isSpecialized(requestName)) {
final Class<?> boxedType;
final Class<?> primitiveType;
switch (requestName.charAt(requestName.length() - 1)) {
case 'Z':
boxedType = Boolean.class;
primitiveType = int.class;
break;
case 'I':
boxedType = Integer.class;
primitiveType = int.class;
break;
case 'J':
boxedType = Long.class;
primitiveType = long.class;
break;
case 'D':
boxedType = Number.class;
primitiveType = double.class;
break;
default:
throw new RuntimeException("should not reach here");
}
final boolean isStrictCmp = (request == Request.EQ_STRICT || request == Request.NE_STRICT);
if (isStrictCmp &&
(boxedType != Boolean.class &&
(type().parameterType(0) == boolean.class ||
type().parameterType(1) == boolean.class))) {
// number and boolean are never strictly equal, e.g. 0 !== false
mh = MH.dropArguments(MH.constant(boolean.class, request == Request.NE_STRICT), 0, type().parameterArray());
} else {
mh = METHODS.get(request.name().replace("_STRICT", "") + primitiveType.getSimpleName());
// unbox objects
for (int i = 0; i < type().parameterCount(); i++) {
if (!type().parameterType(i).isPrimitive()) {
mh = MH.filterArguments(mh, i, UNBOX.get(boxedType));
}
}
mh = Lookup.filterReturnType(mh, type().returnType());
mh = MH.explicitCastArguments(mh, type());
}
final MethodHandle fallback = MH.foldArguments(MethodHandles.exactInvoker(type()), MH.bindTo(NEXT, this));
MethodHandle guard;
if (type().parameterType(0).isPrimitive()) {
guard = MH.insertArguments(
MH.dropArguments(CHECKCAST, 1, type().parameterType(0)), 0, boxedType);
} else if (type().parameterType(1).isPrimitive()) {
guard = MH.insertArguments(
MH.dropArguments(CHECKCAST, 2, type().parameterType(1)), 0, boxedType);
} else {
assert !type().parameterType(0).isPrimitive() && !type().parameterType(1).isPrimitive();
guard = MH.insertArguments(CHECKCAST2, 0, boxedType);
}
if (request == Request.ADD && boxedType == Integer.class) {
// int add needs additional overflow check
MethodHandle addcheck = ADDCHECK;
for (int i = 0; i < type().parameterCount(); i++) {
if (!type().parameterType(i).isPrimitive()) {
addcheck = MH.filterArguments(addcheck, i, UNBOX.get(boxedType));
}
}
addcheck = MH.explicitCastArguments(addcheck, type().changeReturnType(boolean.class));
guard = MH.guardWithTest(guard, addcheck,
MH.dropArguments(MH.constant(boolean.class, false), 0, type().parameterArray()));
}
return MH.guardWithTest(guard, mh, fallback);
}
// generic fallback
return MH.explicitCastArguments(Lookup.filterReturnType(GENERIC_METHODS.get(request.name()), type().returnType()), type());
}
/**
* This is public just so that the generated specialization code can
* use it to get the next wider typed method
*
* Do not call directly
*
* @return next wider specialization method for this RuntimeCallSite
*/
public MethodHandle next() {
this.name = nextName(name);
final MethodHandle next = makeMethod(name);
setTarget(next);
return next;
}
@Override
public String toString() {
return super.toString() + " " + name;
}
/** Method cache */
private static final Map<String, MethodHandle> METHODS;
/** Generic method cache */
private static final Map<String, MethodHandle> GENERIC_METHODS;
/** Unbox cache */
private static final Map<Class<?>, MethodHandle> UNBOX;
private static final MethodHandle CHECKCAST = findOwnMH("checkcast", boolean.class, Class.class, Object.class);
private static final MethodHandle CHECKCAST2 = findOwnMH("checkcast", boolean.class, Class.class, Object.class, Object.class);
private static final MethodHandle ADDCHECK = findOwnMH("ADDcheck", boolean.class, int.class, int.class);
/**
* Build maps of correct boxing operations
*/
static {
UNBOX = new HashMap<>();
UNBOX.put(Boolean.class, findOwnMH("unboxZ", int.class, Object.class));
UNBOX.put(Integer.class, findOwnMH("unboxI", int.class, Object.class));
UNBOX.put(Long.class, findOwnMH("unboxJ", long.class, Object.class));
UNBOX.put(Number.class, findOwnMH("unboxD", double.class, Object.class));
METHODS = new HashMap<>();
for (final Request req : Request.values()) {
if (req.canSpecialize()) {
if (req.name().endsWith("_STRICT")) {
continue;
}
final boolean isCmp = Request.isComparison(req);
METHODS.put(req.name() + "int", findOwnMH(req.name(), (isCmp ? boolean.class : int.class), int.class, int.class));
METHODS.put(req.name() + "long", findOwnMH(req.name(), (isCmp ? boolean.class : long.class), long.class, long.class));
METHODS.put(req.name() + "double", findOwnMH(req.name(), (isCmp ? boolean.class : double.class), double.class, double.class));
}
}
GENERIC_METHODS = new HashMap<>();
for (final Request req : Request.values()) {
if (req.canSpecialize()) {
GENERIC_METHODS.put(req.name(), MH.findStatic(MethodHandles.lookup(), ScriptRuntime.class, req.name(),
MH.type(req.getReturnType().getTypeClass(), Object.class, Object.class)));
}
}
}
/**
* Specialized version of != operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a != b
*/
public static boolean NE(final int a, final int b) {
return a != b;
}
/**
* Specialized version of != operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a != b
*/
public static boolean NE(final double a, final double b) {
return a != b;
}
/**
* Specialized version of != operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a != b
*/
public static boolean NE(final long a, final long b) {
return a != b;
}
/**
* Specialized version of == operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a == b
*/
public static boolean EQ(final int a, final int b) {
return a == b;
}
/**
* Specialized version of == operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a == b
*/
public static boolean EQ(final double a, final double b) {
return a == b;
}
/**
* Specialized version of == operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a == b
*/
public static boolean EQ(final long a, final long b) {
return a == b;
}
/**
* Specialized version of < operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a < b
*/
public static boolean LT(final int a, final int b) {
return a < b;
}
/**
* Specialized version of < operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a < b
*/
public static boolean LT(final double a, final double b) {
return a < b;
}
/**
* Specialized version of < operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a < b
*/
public static boolean LT(final long a, final long b) {
return a < b;
}
/**
* Specialized version of <= operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a <= b
*/
public static boolean LE(final int a, final int b) {
return a <= b;
}
/**
* Specialized version of <= operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a <= b
*/
public static boolean LE(final double a, final double b) {
return a <= b;
}
/**
* Specialized version of <= operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a <= b
*/
public static boolean LE(final long a, final long b) {
return a <= b;
}
/**
* Specialized version of > operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a > b
*/
public static boolean GT(final int a, final int b) {
return a > b;
}
/**
* Specialized version of > operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a > b
*/
public static boolean GT(final double a, final double b) {
return a > b;
}
/**
* Specialized version of > operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a > b
*/
public static boolean GT(final long a, final long b) {
return a > b;
}
/**
* Specialized version of >= operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a >= b
*/
public static boolean GE(final int a, final int b) {
return a >= b;
}
/**
* Specialized version of >= operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a >= b
*/
public static boolean GE(final double a, final double b) {
return a >= b;
}
/**
* Specialized version of >= operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a >= b
*/
public static boolean GE(final long a, final long b) {
return a >= b;
}
/**
* Specialized version of + operator for two int arguments. Do not call directly.
* @param a int
* @param b int
* @return a + b
*/
public static int ADD(final int a, final int b) {
return a + b;
}
/**
* Specialized version of + operator for two long arguments. Do not call directly.
* @param a long
* @param b long
* @return a + b
*/
public static long ADD(final long a, final long b) {
return a + b;
}
/**
* Specialized version of + operator for two double arguments. Do not call directly.
* @param a double
* @param b double
* @return a + b
*/
public static double ADD(final double a, final double b) {
return a + b;
}
/**
* Check that ints are addition compatible, i.e. their sum is equal to the sum
* of them cast to long. Otherwise the addition will overflow. Do not call directly.
*
* @param a int
* @param b int
*
* @return true if addition does not overflow
*/
public static boolean ADDcheck(final int a, final int b) {
return (a + b == (long)a + (long)b);
}
/**
* Checkcast used for specialized ops. Do not call directly
*
* @param type to to check against
* @param obj object to check for type
*
* @return true if type check holds
*/
public static boolean checkcast(final Class<?> type, final Object obj) {
return type.isInstance(obj);
}
/**
* Checkcast used for specialized ops. Do not call directly
*
* @param type type to check against
* @param objA first object to check against type
* @param objB second object to check against type
*
* @return true if type check holds for both objects
*/
public static boolean checkcast(final Class<?> type, final Object objA, final Object objB) {
return type.isInstance(objA) && type.isInstance(objB);
}
/**
* Unbox a java.lang.Boolean. Do not call directly
* @param obj object to cast to int and unbox
* @return an int value for the boolean, 1 is true, 0 is false
*/
public static int unboxZ(final Object obj) {
return (boolean)obj ? 1 : 0;
}
/**
* Unbox a java.lang.Integer. Do not call directly
* @param obj object to cast to int and unbox
* @return an int
*/
public static int unboxI(final Object obj) {
return (int)obj;
}
/**
* Unbox a java.lang.Long. Do not call directly
* @param obj object to cast to long and unbox
* @return a long
*/
public static long unboxJ(final Object obj) {
return (long)obj;
}
/**
* Unbox a java.lang.Number. Do not call directly
* @param obj object to cast to Number and unbox
* @return a double
*/
public static double unboxD(final Object obj) {
return ((Number)obj).doubleValue();
}
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
try {
return MH.findStatic(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
} catch (final MethodHandleFactory.LookupException e) {
return MH.findVirtual(MethodHandles.lookup(), RuntimeCallSite.class, name, MH.type(rtype, types));
}
}
}

View File

@ -0,0 +1,192 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import java.util.Arrays;
import java.util.EnumSet;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.ScriptObject;
/**
* A scope call or get operation that can be shared by several callsites. This generates a static
* method that wraps the invokedynamic instructions to get or call scope variables.
* The rationale for this is that initial linking of invokedynamic callsites is expensive,
* so by sharing them we can reduce startup overhead and allow very large scripts to run that otherwise wouldn't.
*
* <p>Static methods generated by this class expect two parameters in addition to the parameters of the
* function call: The current scope object and the depth of the target scope relative to the scope argument
* for when this is known at compile-time (fast-scope access).</p>
*
* <p>The second argument may be -1 for non-fast-scope symbols, in which case the scope chain is checked
* for each call. This may cause callsite invalidation when the shared method is used from different
* scopes, but such sharing of non-fast scope calls may still be necessary for very large scripts.</p>
*
* <p>Scope calls must not be shared between normal callsites and callsites contained in a <tt>with</tt>
* statement as this condition is not handled by current guards and will cause a runtime error.</p>
*/
public class SharedScopeCall {
/** Threshold for using shared scope calls with fast scope access. */
public static final int FAST_SCOPE_CALL_THRESHOLD = 4;
/** Threshold for using shared scope calls with slow scope access. */
public static final int SLOW_SCOPE_CALL_THRESHOLD = 500;
/** Threshold for using shared scope gets with fast scope access. */
public static final int FAST_SCOPE_GET_THRESHOLD = 200;
final Type valueType;
final Symbol symbol;
final Type returnType;
final Type[] paramTypes;
final int flags;
final boolean isCall;
private CompileUnit compileUnit;
private String methodName;
private String staticSignature;
/**
* Constructor.
*
* @param symbol the symbol
* @param valueType the type of the value
* @param returnType the return type
* @param paramTypes the function parameter types
* @param flags the callsite flags
*/
SharedScopeCall(final Symbol symbol, final Type valueType, final Type returnType, final Type[] paramTypes, final int flags) {
this.symbol = symbol;
this.valueType = valueType;
this.returnType = returnType;
this.paramTypes = paramTypes;
this.flags = flags;
// If paramTypes is not null this is a call, otherwise it's just a get.
this.isCall = paramTypes != null;
}
@Override
public int hashCode() {
return symbol.hashCode() ^ returnType.hashCode() ^ Arrays.hashCode(paramTypes) ^ flags;
}
@Override
public boolean equals(final Object obj) {
if (obj instanceof SharedScopeCall) {
final SharedScopeCall c = (SharedScopeCall) obj;
return symbol.equals(c.symbol)
&& flags == c.flags
&& returnType.equals(c.returnType)
&& Arrays.equals(paramTypes, c.paramTypes);
}
return false;
}
/**
* Set the compile unit and method name.
* @param compileUnit the compile unit
* @param compiler the compiler to generate a unique method name
*/
protected void setClassAndName(final CompileUnit compileUnit, final Compiler compiler) {
this.compileUnit = compileUnit;
this.methodName = compiler.uniqueName("scopeCall");
}
/**
* Generate the invoke instruction for this shared scope call.
* @param method the method emitter
*/
public void generateInvoke(final MethodEmitter method) {
method.invokeStatic(compileUnit.getUnitClassName(), methodName, getStaticSignature());
}
/**
* Generate the method that implements the scope get or call.
*/
protected void generateScopeCall() {
final ClassEmitter classEmitter = compileUnit.getClassEmitter();
final EnumSet<ClassEmitter.Flag> methodFlags = EnumSet.of(ClassEmitter.Flag.STATIC);
// This method expects two fixed parameters in addition to any parameters that may be
// passed on to the function: A ScriptObject representing the caller's current scope object,
// and an int specifying the distance to the target scope containing the symbol we want to
// access, or -1 if this is not known at compile time (e.g. because of a "with" or "eval").
final MethodEmitter method = classEmitter.method(methodFlags, methodName, getStaticSignature());
method.begin();
// Load correct scope by calling getProto() on the scope argument as often as specified
// by the second argument.
final MethodEmitter.Label parentLoopStart = new MethodEmitter.Label("parent_loop_start");
final MethodEmitter.Label parentLoopDone = new MethodEmitter.Label("parent_loop_done");
method.load(Type.OBJECT, 0);
method.label(parentLoopStart);
method.load(Type.INT, 1);
method.iinc(1, -1);
method.ifle(parentLoopDone);
method.invoke(ScriptObject.GET_PROTO);
method._goto(parentLoopStart);
method.label(parentLoopDone);
method.dynamicGet(valueType, symbol.getName(), flags, isCall);
// If this is a get we're done, otherwise call the value as function.
if (isCall) {
method.convert(Type.OBJECT);
// ScriptFunction will see CALLSITE_SCOPE and will bind scope accordingly.
method.loadNull();
int slot = 2;
for (final Type type : paramTypes) {
method.load(type, slot++);
if (type == Type.NUMBER || type == Type.LONG) slot++;
}
method.dynamicCall(returnType, paramTypes.length, flags);
}
method._return(returnType);
method.end();
}
private String getStaticSignature() {
if (staticSignature == null) {
if (paramTypes == null) {
staticSignature = Type.getMethodDescriptor(returnType, Type.typeFor(ScriptObject.class), Type.INT);
} else {
final Type[] params = new Type[paramTypes.length + 2];
params[0] = Type.typeFor(ScriptObject.class);
params[1] = Type.INT;
int i = 2;
for (Type type : paramTypes) {
if (type.isObject()) {
type = Type.OBJECT;
}
params[i++] = type;
}
staticSignature = Type.getMethodDescriptor(returnType, params);
}
}
return staticSignature;
}
}

View File

@ -0,0 +1,403 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.SPLIT_PREFIX;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.BreakNode;
import jdk.nashorn.internal.ir.ContinueNode;
import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* Split the IR into smaller compile units.
*/
public class Splitter extends NodeVisitor {
/** Current compiler. */
private final Compiler compiler;
/** IR to be broken down. */
private final FunctionNode functionNode;
/** Compile unit for the main script. */
private final CompileUnit scriptCompileUnit;
/** Cache for calculated block weights. */
private final Map<Node, Long> weightCache = new HashMap<>();
/** Weight threshold for when to start a split. */
public static final long SPLIT_THRESHOLD = 32 * 1024;
/**
* Constructor.
*
* @param compiler the compiler
* @param functionNode function node to split
*/
public Splitter(final Compiler compiler, final FunctionNode functionNode, final CompileUnit compileUnit) {
this.compiler = compiler;
this.functionNode = functionNode;
this.scriptCompileUnit = compileUnit;
}
/**
* Execute the split
*/
void split() {
long weight = WeighNodes.weigh(functionNode);
if (weight >= SPLIT_THRESHOLD) {
functionNode.accept(this);
if (functionNode.isSplit()) {
// Weight has changed so weigh again, this time using block weight cache
weight = WeighNodes.weigh(functionNode, weightCache);
}
if (weight >= SPLIT_THRESHOLD) {
weight = splitBlock(functionNode);
}
if (functionNode.isSplit()) {
functionNode.accept(new SplitFlowAnalyzer());
}
}
assert functionNode.getCompileUnit() == null : "compile unit already set";
if (functionNode.isScript()) {
assert scriptCompileUnit != null : "script compile unit is null";
functionNode.setCompileUnit(scriptCompileUnit);
scriptCompileUnit.addWeight(weight + WeighNodes.FUNCTION_WEIGHT);
} else {
functionNode.setCompileUnit(findUnit(weight));
}
// Recursively split nested functions
final List<FunctionNode> functions = functionNode.getFunctions();
for (final FunctionNode function : functions) {
new Splitter(compiler, function, scriptCompileUnit).split();
}
}
/**
* Override this logic to look up compile units in a different way
* @param weight weight needed
* @return compile unit
*/
protected CompileUnit findUnit(final long weight) {
return compiler.findUnit(weight);
}
/**
* Split a block into sub methods.
*
* @param block Block or function to split.
*
* @return new weight for the resulting block.
*/
private long splitBlock(final Block block) {
functionNode.setIsSplit();
final List<Node> splits = new ArrayList<>();
List<Node> statements = new ArrayList<>();
long statementsWeight = 0;
for (final Node statement : block.getStatements()) {
final long weight = WeighNodes.weigh(statement, weightCache);
if (statementsWeight + weight >= SPLIT_THRESHOLD || statement.isTerminal()) {
if (!statements.isEmpty()) {
splits.add(createBlockSplitNode(block, statements, statementsWeight));
statements = new ArrayList<>();
statementsWeight = 0;
}
}
if (statement.isTerminal()) {
splits.add(statement);
} else {
statements.add(statement);
statementsWeight += weight;
}
}
if (!statements.isEmpty()) {
splits.add(createBlockSplitNode(block, statements, statementsWeight));
}
block.setStatements(splits);
return WeighNodes.weigh(block, weightCache);
}
/**
* Create a new split node from statements contained in a parent block.
*
* @param parent Parent block.
* @param statements Statements to include.
*
* @return New split node.
*/
private SplitNode createBlockSplitNode(final Block parent, final List<Node> statements, final long weight) {
final Source source = parent.getSource();
final long token = parent.getToken();
final int finish = parent.getFinish();
final String name = compiler.uniqueName(SPLIT_PREFIX.tag());
final Block newBlock = new Block(source, token, finish, parent, functionNode);
newBlock.setFrame(new Frame(parent.getFrame()));
newBlock.setStatements(statements);
final SplitNode splitNode = new SplitNode(name, functionNode, newBlock);
splitNode.setCompileUnit(compiler.findUnit(weight + WeighNodes.FUNCTION_WEIGHT));
return splitNode;
}
@Override
public Node enter(final Block block) {
if (block.isCatchBlock()) {
return null;
}
final long weight = WeighNodes.weigh(block, weightCache);
if (weight < SPLIT_THRESHOLD) {
weightCache.put(block, weight);
return null;
}
return block;
}
@Override
public Node leave(final Block block) {
assert !block.isCatchBlock();
// Block was heavier than SLIT_THRESHOLD in enter, but a sub-block may have
// been split already, so weigh again before splitting.
long weight = WeighNodes.weigh(block, weightCache);
if (weight >= SPLIT_THRESHOLD) {
weight = splitBlock(block);
}
weightCache.put(block, weight);
return block;
}
@SuppressWarnings("rawtypes")
@Override
public Node leave(final LiteralNode literal) {
long weight = WeighNodes.weigh(literal);
if (weight < SPLIT_THRESHOLD) {
return literal;
}
functionNode.setIsSplit();
if (literal instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode) literal;
final Node[] value = arrayLiteralNode.getValue();
final int[] postsets = arrayLiteralNode.getPostsets();
final List<ArrayUnit> units = new ArrayList<>();
long totalWeight = 0;
int lo = 0;
for (int i = 0; i < postsets.length; i++) {
final int postset = postsets[i];
final Node element = value[postset];
weight = WeighNodes.weigh(element);
totalWeight += weight;
if (totalWeight >= SPLIT_THRESHOLD) {
final CompileUnit unit = compiler.findUnit(totalWeight - weight);
units.add(new ArrayUnit(unit, lo, i));
lo = i;
totalWeight = weight;
}
}
if (lo != postsets.length) {
final CompileUnit unit = compiler.findUnit(totalWeight);
units.add(new ArrayUnit(unit, lo, postsets.length));
}
arrayLiteralNode.setUnits(units);
}
return literal;
}
@Override
public Node enter(final FunctionNode node) {
final List<Node> statements = node.getStatements();
for (final Node statement : statements) {
statement.accept(this);
}
return null;
}
static class SplitFlowAnalyzer extends NodeVisitor {
/** Stack of visited Split nodes, deepest node first. */
private final Deque<SplitNode> splitStack;
/** Map of possible jump targets to containing split node */
private final Map<Node,SplitNode> targetNodes = new HashMap<>();
SplitFlowAnalyzer() {
this.splitStack = new LinkedList<>();
}
@Override
public Node enter(final LabelNode labelNode) {
registerJumpTarget(labelNode.getBreakNode());
registerJumpTarget(labelNode.getContinueNode());
return labelNode;
}
@Override
public Node enter(final WhileNode whileNode) {
registerJumpTarget(whileNode);
return whileNode;
}
@Override
public Node enter(final DoWhileNode doWhileNode) {
registerJumpTarget(doWhileNode);
return doWhileNode;
}
@Override
public Node enter(final ForNode forNode) {
registerJumpTarget(forNode);
return forNode;
}
@Override
public Node enter(final SwitchNode switchNode) {
registerJumpTarget(switchNode);
return switchNode;
}
@Override
public Node enter(final ReturnNode returnNode) {
for (final SplitNode split : splitStack) {
split.setHasReturn(true);
}
return returnNode;
}
@Override
public Node enter(final ContinueNode continueNode) {
searchJumpTarget(continueNode.getTargetNode(), continueNode.getTargetLabel());
return continueNode;
}
@Override
public Node enter(final BreakNode breakNode) {
searchJumpTarget(breakNode.getTargetNode(), breakNode.getTargetLabel());
return breakNode;
}
@Override
public Node enter(final SplitNode splitNode) {
splitStack.addFirst(splitNode);
return splitNode;
}
@Override
public Node leave(final SplitNode splitNode) {
assert splitNode == splitStack.peekFirst();
splitStack.removeFirst();
return splitNode;
}
/**
* Register the split node containing a potential jump target.
* @param targetNode a potential target node.
*/
private void registerJumpTarget(final Node targetNode) {
final SplitNode splitNode = splitStack.peekFirst();
if (splitNode != null) {
targetNodes.put(targetNode, splitNode);
}
}
/**
* Check if a jump target is outside the current split node and its parent split nodes.
* @param targetNode the jump target node.
* @param targetLabel the jump target label.
*/
private void searchJumpTarget(final Node targetNode, final MethodEmitter.Label targetLabel) {
final SplitNode targetSplit = targetNodes.get(targetNode);
// Note that targetSplit may be null, indicating that targetNode is in top level method.
// In this case we have to add the external jump target to all split nodes.
for (final SplitNode split : splitStack) {
if (split == targetSplit) {
break;
}
final List<MethodEmitter.Label> externalTargets = split.getExternalTargets();
if (!externalTargets.contains(targetLabel)) {
split.addExternalTarget(targetLabel);
}
}
}
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
/**
* Code generation transform
*/
public interface Transform {
//empty
}

View File

@ -0,0 +1,308 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen;
import java.util.List;
import java.util.Map;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.BreakNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ContinueNode;
import jdk.nashorn.internal.ir.DoWhileNode;
import jdk.nashorn.internal.ir.ExecuteNode;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode;
import jdk.nashorn.internal.ir.LiteralNode.ArrayLiteralNode.ArrayUnit;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.ReferenceNode;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.ThrowNode;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.TokenType;
/**
* Computes the "byte code" weight of an AST segment. This is used
* for Splitting too large class files
*/
public class WeighNodes extends NodeVisitor {
/*
* Weight constants.
*/
static final long FUNCTION_WEIGHT = 40;
private static final long ACCESS_WEIGHT = 4;
private static final long ADD_WEIGHT = 10;
private static final long BREAK_WEIGHT = 1;
private static final long CALL_WEIGHT = 10;
private static final long CATCH_WEIGHT = 10;
private static final long CONTINUE_WEIGHT = 1;
private static final long IF_WEIGHT = 2;
private static final long LITERAL_WEIGHT = 10;
private static final long LOOP_WEIGHT = 4;
private static final long REFERENCE_WEIGHT = 20;
private static final long RETURN_WEIGHT = 2;
private static final long SPLIT_WEIGHT = 40;
private static final long SWITCH_WEIGHT = 8;
private static final long THROW_WEIGHT = 2;
private static final long VAR_WEIGHT = 40;
private static final long WITH_WEIGHT = 8;
/** Accumulated weight. */
private long weight;
/** Optional cache for weight of block nodes. */
private final Map<Node, Long> weightCache;
/*
* Constructor
*
* @param weightCache cache of already calculated block weights
*/
private WeighNodes(final Map<Node, Long> weightCache) {
super(null, null);
this.weightCache = weightCache;
}
static long weigh(final Node node) {
final WeighNodes weighNodes = new WeighNodes(null);
node.accept(weighNodes);
return weighNodes.weight;
}
static long weigh(final Node node, final Map<Node, Long> weightCache) {
final WeighNodes weighNodes = new WeighNodes(weightCache);
node.accept(weighNodes);
return weighNodes.weight;
}
@Override
public Node leave(final AccessNode accessNode) {
weight += ACCESS_WEIGHT;
return accessNode;
}
@Override
public Node leave(final BinaryNode binaryNode) {
final TokenType tokenType = binaryNode.tokenType();
if (tokenType == TokenType.ADD || tokenType == TokenType.ASSIGN_ADD) {
weight += ADD_WEIGHT;
} else {
weight += 1;
}
return binaryNode;
}
@Override
public Node enter(final Block block) {
if (weightCache != null && weightCache.containsKey(block)) {
weight += weightCache.get(block);
return null;
}
return block;
}
@Override
public Node leave(final BreakNode breakNode) {
weight += BREAK_WEIGHT;
return breakNode;
}
@Override
public Node leave(final CallNode callNode) {
weight += CALL_WEIGHT;
return callNode;
}
@Override
public Node leave(final CatchNode catchNode) {
weight += CATCH_WEIGHT;
return catchNode;
}
@Override
public Node leave(final ContinueNode continueNode) {
weight += CONTINUE_WEIGHT;
return continueNode;
}
@Override
public Node leave(final DoWhileNode doWhileNode) {
weight += LOOP_WEIGHT;
return doWhileNode;
}
@Override
public Node leave(final ExecuteNode executeNode) {
return executeNode;
}
@Override
public Node leave(final ForNode forNode) {
weight += LOOP_WEIGHT;
return forNode;
}
@Override
public Node enter(final FunctionNode functionNode) {
final List<Node> statements = functionNode.getStatements();
for (final Node statement : statements) {
statement.accept(this);
}
return null;
}
@Override
public Node leave(final IdentNode identNode) {
weight += ACCESS_WEIGHT + identNode.getName().length() * 2;
return identNode;
}
@Override
public Node leave(final IfNode ifNode) {
weight += IF_WEIGHT;
return ifNode;
}
@Override
public Node leave(final IndexNode indexNode) {
weight += ACCESS_WEIGHT;
return indexNode;
}
@SuppressWarnings("rawtypes")
@Override
public Node enter(final LiteralNode literalNode) {
weight += LITERAL_WEIGHT;
if (literalNode instanceof ArrayLiteralNode) {
final ArrayLiteralNode arrayLiteralNode = (ArrayLiteralNode)literalNode;
final Node[] value = arrayLiteralNode.getValue();
final int[] postsets = arrayLiteralNode.getPostsets();
final List<ArrayUnit> units = arrayLiteralNode.getUnits();
if (units == null) {
for (final int postset : postsets) {
final Node element = value[postset];
if (element != null) {
element.accept(this);
}
}
}
return null;
}
return literalNode;
}
@Override
public Node leave(final PropertyNode propertyNode) {
weight += LITERAL_WEIGHT;
return propertyNode;
}
@Override
public Node leave(final ReferenceNode referenceNode) {
weight += REFERENCE_WEIGHT;
return referenceNode;
}
@Override
public Node leave(final ReturnNode returnNode) {
weight += RETURN_WEIGHT;
return returnNode;
}
@Override
public Node leave(final RuntimeNode runtimeNode) {
weight += CALL_WEIGHT;
return runtimeNode;
}
@Override
public Node enter(final SplitNode splitNode) {
weight += SPLIT_WEIGHT;
return null;
}
@Override
public Node leave(final SwitchNode switchNode) {
weight += SWITCH_WEIGHT;
return switchNode;
}
@Override
public Node leave(final ThrowNode throwNode) {
weight += THROW_WEIGHT;
return throwNode;
}
@Override
public Node leave(final TryNode tryNode) {
weight += THROW_WEIGHT;
return tryNode;
}
@Override
public Node leave(final VarNode varNode) {
weight += VAR_WEIGHT;
return varNode;
}
@Override
public Node leave(final WhileNode whileNode) {
weight += LOOP_WEIGHT;
return whileNode;
}
@Override
public Node leave(final WithNode withNode) {
weight += WITH_WEIGHT;
return withNode;
}
}

View File

@ -0,0 +1,186 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.objects;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.types.Type.OBJECT;
import java.util.Iterator;
import java.util.List;
import jdk.nashorn.internal.codegen.CodeGenerator;
import jdk.nashorn.internal.codegen.MethodEmitter;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
/**
* Analyze an object's characteristics for appropriate code generation. This
* is used for functions and for objects. A field object take a set of values which
* to assign to the various fields in the object. This is done by the generated code
*
* @param <T> the value type for the fields being written on object creation, e.g. Node
* @see jdk.nashorn.internal.ir.Node
*/
public abstract class FieldObjectCreator<T> extends ObjectCreator {
/** array of corresponding values to symbols (null for no values) */
private final List<T> values;
/** call site flags to be used for invocations */
private final int callSiteFlags;
/**
* Constructor
*
* @param codegen code generator
* @param keys keys for fields in object
* @param symbols symbols for fields in object
* @param values list of values corresponding to keys
*/
public FieldObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<T> values) {
this(codegen, keys, symbols, values, false, false);
}
/**
* Constructor
*
* @param codegen code generator
* @param keys keys for fields in object
* @param symbols symbols for fields in object
* @param values values (or null where no value) to be written to the fields
* @param isScope is this a scope object
* @param isVarArg is this a vararg object
*/
public FieldObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final List<T> values, final boolean isScope, final boolean isVarArg) {
super(codegen, keys, symbols, isScope, isVarArg);
this.values = values;
this.callSiteFlags = codegen.getCallSiteFlags();
}
/**
* Construct an object.
*
* @param method the method emitter
*/
@Override
public void makeObject(final MethodEmitter method) {
makeMap();
method._new(getClassName()).dup(); // create instance
loadMap(method); //load the map
if (isScope()) {
method.loadScope();
if (isVarArg()) {
method.loadArguments();
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class, ARGUMENTS.type()));
} else {
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class, ScriptObject.class));
}
} else {
method.invoke(constructorNoLookup(getClassName(), PropertyMap.class));
}
// Set values.
final Iterator<Symbol> symbolIter = symbols.iterator();
final Iterator<String> keyIter = keys.iterator();
final Iterator<T> valueIter = values.iterator();
while (symbolIter.hasNext()) {
final Symbol symbol = symbolIter.next();
final String key = keyIter.next();
final T value = valueIter.next();
if (symbol != null && value != null) {
final int index = ArrayIndex.getArrayIndexNoThrow(key);
if (index < 0) {
putField(method, key, symbol.getFieldIndex(), value);
} else {
putSlot(method, index, value);
}
}
}
}
/**
* Technique for loading an initial value. Defined by anonymous subclasses in code gen.
*
* @param value Value to load.
*/
protected abstract void loadValue(T value);
/**
* Determine the type of a value. Defined by anonymous subclasses in code gen.
*
* @param value Value to inspect.
*
* @return Value type.
*/
protected abstract Type getValueType(T value);
/**
* Store a value in a field of the generated class object.
*
* @param method Script method.
* @param key Property key.
* @param fieldIndex Field number.
* @param value Value to store.
*/
private void putField(final MethodEmitter method, final String key, final int fieldIndex, final T value) {
method.dup();
loadValue(value);
final Type valueType = getValueType(value);
// for example when we have a with scope
if (valueType.isObject() || valueType.isBoolean()) {
method.convert(OBJECT);
}
method.convert(OBJECT);
method.putField(getClassName(), ObjectClassGenerator.getFieldName(fieldIndex, Type.OBJECT), typeDescriptor(Object.class));
}
/**
* Store a value in an indexed slot of a generated class object.
*
* @param method Script method.
* @param index Slot index.
* @param value Value to store.
*/
private void putSlot(final MethodEmitter method, final int index, final T value) {
method.dup();
method.load(index);
loadValue(value);
method.dynamicSetIndex(callSiteFlags);
}
}

View File

@ -0,0 +1,133 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.objects;
import static jdk.nashorn.internal.codegen.ClassEmitter.Flag.HANDLE_STATIC;
import static jdk.nashorn.internal.codegen.Compiler.SCRIPTOBJECT_IMPL_OBJECT;
import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import java.lang.invoke.MethodHandle;
import java.util.EnumSet;
import java.util.List;
import jdk.nashorn.internal.codegen.CodeGenerator;
import jdk.nashorn.internal.codegen.FunctionSignature;
import jdk.nashorn.internal.codegen.MethodEmitter;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.Source;
/**
* Analyze a function object's characteristics for appropriate code
* generation. This generates code for the instantiation of ScriptFunction:s
*/
public class FunctionObjectCreator extends ObjectCreator {
private final FunctionNode functionNode;
/**
* Constructor
*
* @param codegen the code generator
* @param functionNode the function node to turn into a ScriptFunction implementation
* @param keys initial keys for the object map
* @param symbols corresponding initial symbols for object map
*/
public FunctionObjectCreator(final CodeGenerator codegen, final FunctionNode functionNode, final List<String> keys, final List<Symbol> symbols) {
super(codegen, keys, symbols, false, false);
this.functionNode = functionNode;
}
private void loadHandle(final MethodEmitter method, final String signature) {
method.loadHandle(functionNode.getCompileUnit().getUnitClassName(), functionNode.getName(), signature, EnumSet.of(HANDLE_STATIC)); // function
}
/**
* Emit code for creating the object
*
* @param method the method emitter
*/
@Override
public void makeObject(final MethodEmitter method) {
makeMap();
final IdentNode identNode = functionNode.getIdent();
final String signature = new FunctionSignature(true, functionNode.needsCallee(), functionNode.getReturnType(), functionNode.isVarArg() ? null : functionNode.getParameters()).toString();
final long firstToken = functionNode.getFirstToken();
final long lastToken = functionNode.getLastToken();
final int position = Token.descPosition(firstToken);
final int length = Token.descPosition(lastToken) - position + Token.descLength(lastToken);
final long token = Token.toDesc(TokenType.FUNCTION, position, length);
/*
* Instantiate the function object, must be referred to by name as
* class is not available at compile time
*/
method._new(SCRIPTOBJECT_IMPL_OBJECT).dup();
method.load(functionNode.isAnonymous() ? "" : identNode.getName());
loadHandle(method, signature);
method.loadScope();
method.getStatic(compileUnit.getUnitClassName(), SOURCE.tag(), SOURCE.descriptor());
method.load(token);
method.loadHandle(getClassName(), ALLOCATE.tag(), methodDescriptor(ScriptObject.class, PropertyMap.class), EnumSet.of(HANDLE_STATIC));
/*
* Emit code for the correct property map for the object
*/
loadMap(method);
/*
* Invoke the constructor
*/
method.load(functionNode.needsCallee());
method.load(functionNode.isStrictMode());
method.invoke(constructorNoLookup(SCRIPTOBJECT_IMPL_OBJECT,
String.class,
MethodHandle.class,
ScriptObject.class,
Source.class,
long.class,
MethodHandle.class,
PropertyMap.class,
boolean.class,
boolean.class));
if (functionNode.isVarArg()) {
method.dup();
method.load(functionNode.getParameters().size());
method.invoke(ScriptFunction.SET_ARITY);
}
}
}

View File

@ -0,0 +1,163 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.objects;
import static jdk.nashorn.internal.codegen.objects.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
import static jdk.nashorn.internal.codegen.objects.ObjectClassGenerator.PRIMITIVE_TYPE;
import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.List;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.Property;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
/**
* Class that creates PropertyMap sent to script object constructors.
*/
public class MapCreator {
/** Object structure for objects associated with this map */
private final Class<?> structure;
/** key set for object map */
private final String[] keys;
/** corresponding symbol set for object map */
private final Symbol[] symbols;
/**
* Constructor
*
* @param structure structure to generate map for (a JO$ subclass)
* @param keys list of keys for map
* @param symbols list of symbols for map
*/
public MapCreator(final Class<?> structure, final List<String> keys, final List<Symbol> symbols) {
final int size = keys.size();
this.structure = structure;
this.keys = keys.toArray(new String[size]);
this.symbols = symbols.toArray(new Symbol[size]);
}
/**
* Constructs a property map based on a set of fields.
*
* @param isVarArg is this a vararg object map
*
* @return New map populated with accessor properties.
*/
public PropertyMap makeMap(final boolean isVarArg) {
final List<Property> properties = new ArrayList<>();
assert keys != null;
for (int i = 0; i < keys.length; i++) {
final String key = keys[i];
final Symbol symbol = symbols[i];
if (symbol != null && !ArrayIndex.isIndexKey(key)) {
final Property property = initHandle(key, symbol.getFieldIndex(), symbol, isVarArg);
properties.add(property);
}
}
return PropertyMap.newMap(structure, properties);
}
private Property initHandle(final String key, final int fieldIndex, final Symbol symbol, final boolean isVarArg) {
assert symbol != null;
final boolean isParam = symbol.isParam();
final String fieldNameObject = ObjectClassGenerator.getFieldName(fieldIndex, Type.OBJECT);
final String fieldNamePrimitive = ObjectClassGenerator.getFieldName(fieldIndex, ObjectClassGenerator.PRIMITIVE_TYPE);
MethodHandle primitiveGetter = null;
MethodHandle primitiveSetter = null;
MethodHandle objectGetter;
MethodHandle objectSetter;
final MethodHandles.Lookup lookup = MethodHandles.lookup();
if (isParam && isVarArg) {
final MethodHandle arguments = MH.getter(MethodHandles.lookup(), structure, "arguments", Object.class);
final MethodHandle argumentsSO = MH.asType(arguments, arguments.type().changeReturnType(ScriptObject.class));
objectGetter = MH.insertArguments(MH.filterArguments(ScriptObject.GET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, fieldIndex);
objectSetter = MH.insertArguments(MH.filterArguments(ScriptObject.SET_ARGUMENT.methodHandle(), 0, argumentsSO), 1, fieldIndex);
} else {
objectGetter = MH.getter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
objectSetter = MH.setter(lookup, structure, fieldNameObject, Type.OBJECT.getTypeClass());
if (!OBJECT_FIELDS_ONLY) {
primitiveGetter = MH.getter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
primitiveSetter = MH.setter(lookup, structure, fieldNamePrimitive, PRIMITIVE_TYPE.getTypeClass());
}
}
return new AccessorProperty(key, getPropertyFlags(symbol, isVarArg), objectGetter, objectSetter, primitiveGetter, primitiveSetter);
}
/**
* Compute property flags given local state of a field. Maybe be overridden and extended,
* as is the case in {@link ObjectMapCreator}
*
* @param symbol symbol to check
* @param isVarArg is this a vararg
*
* @return flags to use for fields
*/
protected int getPropertyFlags(final Symbol symbol, final boolean isVarArg) {
final boolean isParam = symbol.isParam();
int flags = 0;
if (isParam || isVarArg) {
flags |= Property.IS_ALWAYS_OBJECT;
if (isParam) {
flags |= Property.IS_PARAMETER;
}
}
if (symbol.isScope()) {
flags |= Property.NOT_CONFIGURABLE;
}
if (symbol.canBePrimitive()) {
flags |= Property.CAN_BE_PRIMITIVE;
}
if (symbol.canBeUndefined()) {
flags |= Property.CAN_BE_UNDEFINED;
}
return flags;
}
}

View File

@ -0,0 +1,762 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.objects;
import static jdk.nashorn.internal.codegen.Compiler.SCRIPTS_PACKAGE;
import static jdk.nashorn.internal.codegen.CompilerConstants.ALLOCATE;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_ARGUMENTS;
import static jdk.nashorn.internal.codegen.CompilerConstants.INIT_SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.JS_OBJECT_PREFIX;
import static jdk.nashorn.internal.codegen.CompilerConstants.MAP;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.constructorNoLookup;
import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import jdk.nashorn.internal.codegen.ClassEmitter;
import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
import jdk.nashorn.internal.codegen.MethodEmitter;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.DebugLogger;
import jdk.nashorn.internal.runtime.FunctionScope;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.options.Options;
/**
* Generates the ScriptObject subclass structure with fields for a user objects.
*/
public final class ObjectClassGenerator {
/**
* Marker for scope parameters.
*/
public static final String SCOPE_MARKER = "P";
/**
* Debug field logger
* Should we print debugging information for fields when they are generated and getters/setters are called?
*/
public static final DebugLogger LOG = new DebugLogger("fields", "nashorn.fields.debug");
/**
* is field debugging enabled. Several modules in codegen and properties use this, hence
* public access.
*/
public static final boolean DEBUG_FIELDS = LOG.isEnabled();
/**
* Should the runtime only use java.lang.Object slots for fields? If this is false, the representation
* will be a primitive 64-bit long value used for all primitives and a java.lang.Object for references.
* This introduces a larger number of method handles in the system, as we need to have different getters
* and setters for the different fields. Currently this introduces significant overhead in Hotspot.
*
* This is engineered to plug into the TaggedArray implementation, when it's done.
*/
public static final boolean OBJECT_FIELDS_ONLY = !Options.getBooleanProperty("nashorn.fields.dual");
/** The field types in the system */
private static final List<Type> FIELD_TYPES = new LinkedList<>();
/** What type is the primitive type in dual representation */
public static final Type PRIMITIVE_TYPE = Type.LONG;
/**
* The list of field types that we support - one type creates one field. This is currently either
* LONG + OBJECT or just OBJECT for classic mode.
*/
static {
if (!OBJECT_FIELDS_ONLY) {
System.err.println("WARNING!!! Running with primitive fields - there is untested functionality!");
FIELD_TYPES.add(PRIMITIVE_TYPE);
}
FIELD_TYPES.add(Type.OBJECT);
}
/** The context */
private final Context context;
/**
* The list of available accessor types in width order. This order is used for type guesses narrow->wide
* in the dual--fields world
*/
public static final List<Type> ACCESSOR_TYPES = Collections.unmodifiableList(
Arrays.asList(
Type.INT,
Type.LONG,
Type.NUMBER,
Type.OBJECT));
//these are hard coded for speed and so that we can switch on them
private static final int TYPE_INT_INDEX = 0; //getAccessorTypeIndex(int.class);
private static final int TYPE_LONG_INDEX = 1; //getAccessorTypeIndex(long.class);
private static final int TYPE_DOUBLE_INDEX = 2; //getAccessorTypeIndex(double.class);
private static final int TYPE_OBJECT_INDEX = 3; //getAccessorTypeIndex(Object.class);
/**
* Constructor
*
* @param context a context
*/
public ObjectClassGenerator(final Context context) {
this.context = context;
assert context != null;
}
/**
* Given a type of an accessor, return its index in [0..getNumberOfAccessorTypes())
*
* @param type the type
*
* @return the accessor index, or -1 if no accessor of this type exists
*/
public static int getAccessorTypeIndex(final Type type) {
return getAccessorTypeIndex(type.getTypeClass());
}
/**
* Given a class of an accessor, return its index in [0..getNumberOfAccessorTypes())
*
* Note that this is hardcoded with respect to the dynamic contents of the accessor
* types array for speed. Hotspot got stuck with this as 5% of the runtime in
* a benchmark when it looped over values and increased an index counter. :-(
*
* @param type the type
*
* @return the accessor index, or -1 if no accessor of this type exists
*/
public static int getAccessorTypeIndex(final Class<?> type) {
if (type == int.class) {
return 0;
} else if (type == long.class) {
return 1;
} else if (type == double.class) {
return 2;
} else if (!type.isPrimitive()) {
return 3;
}
return -1;
}
/**
* Return the number of accessor types available.
*
* @return number of accessor types in system
*/
public static int getNumberOfAccessorTypes() {
return ACCESSOR_TYPES.size();
}
/**
* Return the accessor type based on its index in [0..getNumberOfAccessorTypes())
* Indexes are ordered narrower->wider / optimistic->pessimistic. Invalidations always
* go to a type of higher index
*
* @param index accessor type index
*
* @return a type corresponding to the index.
*/
public static Type getAccessorType(final int index) {
return ACCESSOR_TYPES.get(index);
}
/**
* Returns the class name for JavaScript objects with fieldCount fields.
*
* @param fieldCount Number of fields to allocate.
*
* @return The class name.
*/
public static String getClassName(final int fieldCount) {
return fieldCount != 0 ? SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount :
SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag();
}
/**
* Returns the class name for JavaScript scope with fieldCount fields and
* paramCount parameters.
*
* @param fieldCount Number of fields to allocate.
* @param paramCount Number of parameters to allocate
*
* @return The class name.
*/
public static String getClassName(final int fieldCount, final int paramCount) {
return SCRIPTS_PACKAGE + '/' + JS_OBJECT_PREFIX.tag() + fieldCount + SCOPE_MARKER + paramCount;
}
/**
* Returns the name of a field based on number and type.
*
* @param fieldIndex Ordinal of field.
* @param type Type of field.
*
* @return The field name.
*/
public static String getFieldName(final int fieldIndex, final Type type) {
return type.getDescriptor().substring(0, 1) + fieldIndex;
}
/**
* In the world of Object fields, we also have no undefined SwitchPoint, to reduce as much potential
* MethodHandle overhead as possible. In that case, we explicitly need to assign undefined to fields
* when we initialize them.
*
* @param init constructor to generate code in
* @param className name of class
* @param fieldNames fields to initialize to undefined, where applicable
*/
private static void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) {
if (fieldNames.isEmpty()) {
return;
}
// always initialize fields to undefined, even with --dual-fields. Then it's ok to
// remember things like "widest set type" in properties, and if it's object, don't
// add any special "return undefined" getters, saving an invalidation
init.load(Type.OBJECT, THIS.slot());
init.loadUndefined(Type.OBJECT);
final Iterator<String> iter = fieldNames.iterator();
while (iter.hasNext()) {
final String fieldName = iter.next();
if (iter.hasNext()) {
init.dup2();
}
init.putField(className, fieldName, Type.OBJECT.getDescriptor());
}
}
/**
* Generate the byte codes for a JavaScript object class or scope.
* Class name is a function of number of fields and number of param
* fields
*
* @param descriptor Descriptor pulled from class name.
*
* @return Byte codes for generated class.
*/
public byte[] generate(final String descriptor) {
final String[] counts = descriptor.split(SCOPE_MARKER);
final int fieldCount = Integer.valueOf(counts[0]);
if (counts.length == 1) {
return generate(fieldCount);
}
final int paramCount = Integer.valueOf(counts[1]);
return generate(fieldCount, paramCount);
}
/**
* Generate the byte codes for a JavaScript object class with fieldCount fields.
*
* @param fieldCount Number of fields in the JavaScript object.
*
* @return Byte codes for generated class.
*/
public byte[] generate(final int fieldCount) {
final String className = getClassName(fieldCount);
final String superName = className(ScriptObject.class);
final ClassEmitter classEmitter = newClassEmitter(className, superName);
final List<String> initFields = addFields(classEmitter, fieldCount);
final MethodEmitter init = newInitMethod(classEmitter);
initializeToUndefined(init, className, initFields);
init.returnVoid();
init.end();
newEmptyInit(classEmitter, className);
newAllocate(classEmitter, className);
return toByteArray(classEmitter);
}
/**
* Generate the byte codes for a JavaScript scope class with fieldCount fields
* and paramCount parameters.
*
* @param fieldCount Number of fields in the JavaScript scope.
* @param paramCount Number of parameters in the JavaScript scope
* .
* @return Byte codes for generated class.
*/
public byte[] generate(final int fieldCount, final int paramCount) {
final String className = getClassName(fieldCount, paramCount);
final String superName = className(FunctionScope.class);
final ClassEmitter classEmitter = newClassEmitter(className, superName);
final List<String> initFields = addFields(classEmitter, fieldCount);
final MethodEmitter init = newInitScopeMethod(classEmitter);
initializeToUndefined(init, className, initFields);
init.returnVoid();
init.end();
final MethodEmitter initWithArguments = newInitScopeWithArgumentsMethod(classEmitter);
initializeToUndefined(initWithArguments, className, initFields);
initWithArguments.returnVoid();
initWithArguments.end();
return toByteArray(classEmitter);
}
/**
* Generates the needed fields.
*
* @param classEmitter Open class emitter.
* @param fieldCount Number of fields.
*
* @return List fields that need to be initialized.
*/
private static List<String> addFields(final ClassEmitter classEmitter, final int fieldCount) {
final List<String> initFields = new LinkedList<>();
for (int i = 0; i < fieldCount; i++) {
for (final Type type : FIELD_TYPES) {
final String fieldName = getFieldName(i, type);
classEmitter.field(fieldName, type.getTypeClass());
if (type == Type.OBJECT) {
initFields.add(fieldName);
}
}
}
return initFields;
}
/**
* Allocate and initialize a new class emitter.
*
* @param className Name of JavaScript class.
*
* @return Open class emitter.
*/
private ClassEmitter newClassEmitter(final String className, final String superName) {
final ClassEmitter classEmitter = new ClassEmitter(context, className, superName);
classEmitter.begin();
return classEmitter;
}
/**
* Allocate and initialize a new <init> method.
*
* @param classEmitter Open class emitter.
*
* @return Open method emitter.
*/
private static MethodEmitter newInitMethod(final ClassEmitter classEmitter) {
final MethodEmitter init = classEmitter.init(PropertyMap.class);
init.begin();
init.load(Type.OBJECT, THIS.slot());
init.load(Type.OBJECT, MAP.slot());
init.invoke(constructorNoLookup(ScriptObject.class, PropertyMap.class));
return init;
}
/**
* Allocate and initialize a new <init> method for scopes.
* @param classEmitter Open class emitter.
* @return Open method emitter.
*/
private static MethodEmitter newInitScopeMethod(final ClassEmitter classEmitter) {
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class);
init.begin();
init.load(Type.OBJECT, THIS.slot());
init.load(Type.OBJECT, MAP.slot());
init.load(Type.OBJECT, INIT_SCOPE.slot());
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class));
return init;
}
/**
* Allocate and initialize a new <init> method for scopes with arguments.
* @param classEmitter Open class emitter.
* @return Open method emitter.
*/
private static MethodEmitter newInitScopeWithArgumentsMethod(final ClassEmitter classEmitter) {
final MethodEmitter init = classEmitter.init(PropertyMap.class, ScriptObject.class, Object.class);
init.begin();
init.load(Type.OBJECT, THIS.slot());
init.load(Type.OBJECT, MAP.slot());
init.load(Type.OBJECT, INIT_SCOPE.slot());
init.load(Type.OBJECT, INIT_ARGUMENTS.slot());
init.invoke(constructorNoLookup(FunctionScope.class, PropertyMap.class, ScriptObject.class, Object.class));
return init;
}
/**
* Add an empty <init> method to the JavaScript class.
*
* @param classEmitter Open class emitter.
* @param className Name of JavaScript class.
*/
private static void newEmptyInit(final ClassEmitter classEmitter, final String className) {
final MethodEmitter emptyInit = classEmitter.init();
emptyInit.begin();
emptyInit.load(Type.OBJECT, THIS.slot());
emptyInit.loadNull();
emptyInit.invoke(constructorNoLookup(className, PropertyMap.class));
emptyInit.returnVoid();
emptyInit.end();
}
/**
* Add an empty <init> method to the JavaScript class.
*
* @param classEmitter Open class emitter.
* @param className Name of JavaScript class.
*/
private static void newAllocate(final ClassEmitter classEmitter, final String className) {
final MethodEmitter allocate = classEmitter.method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), ALLOCATE.tag(), ScriptObject.class, PropertyMap.class);
allocate.begin();
allocate._new(className);
allocate.dup();
allocate.load(Type.typeFor(PropertyMap.class), 0);
allocate.invoke(constructorNoLookup(className, PropertyMap.class));
allocate._return();
allocate.end();
}
/**
* Collects the byte codes for a generated JavaScript class.
*
* @param classEmitter Open class emitter.
* @return Byte codes for the class.
*/
private byte[] toByteArray(final ClassEmitter classEmitter) {
classEmitter.end();
final byte[] code = classEmitter.toByteArray();
if (context != null && context._print_code) {
ClassEmitter.disassemble(context, code);
}
if (context != null && context._verify_code) {
ClassEmitter.verify(context, code);
}
return code;
}
/** Double to long bits, used with --dual-fields for primitive double values */
private static final MethodHandle PACK_DOUBLE =
MH.explicitCastArguments(MH.findStatic(MethodHandles.publicLookup(), Double.class, "doubleToRawLongBits", MH.type(long.class, double.class)), MH.type(long.class, double.class));
/** double bits to long, used with --dual-fields for primitive double values */
private static MethodHandle UNPACK_DOUBLE =
MH.findStatic(MethodHandles.publicLookup(), Double.class, "longBitsToDouble", MH.type(double.class, long.class));
/** object conversion quickies with JS semantics - used for return value and parameter filter */
private static MethodHandle[] CONVERT_OBJECT = {
JSType.TO_INT32.methodHandle(),
JSType.TO_UINT32.methodHandle(),
JSType.TO_NUMBER.methodHandle(),
null
};
/**
* Given a primitiveGetter (optional for non dual fields) and an objectSetter that retrieve
* the primitive and object version of a field respectively, return one with the correct
* method type and the correct filters. For example, if the value is stored as a double
* and we want an Object getter, in the dual fields world we'd pick the primitiveGetter,
* which reads a long, use longBitsToDouble on the result to unpack it, and then change the
* return type to Object, boxing it. In the objects only world there are only object fields,
* primtives are boxed when asked for them and we don't need to bother with primitive encoding
* (or even undefined, which if forType==null) representation, so we just return whatever is
* in the object field. The object field is always initiated to Undefined, so here, where we have
* the representation for Undefined in all our bits, this is not a problem.
* <p>
* Representing undefined in a primitive is hard, for an int there aren't enough bits, for a long
* we could limit the width of a representation, and for a double (as long as it is stored as long,
* as all NaNs will turn into QNaN on ia32, which is one bit pattern, we should use a special NaN).
* Naturally we could have special undefined values for all types which mean "go look in a wider field",
* but the guards needed on every getter took too much time.
* <p>
* To see how this is used, look for example in {@link AccessorProperty#getGetter}
* <p>
* @param forType representation of the underlying type in the field, null if undefined
* @param type type to retrieve it as
* @param primitiveGetter getter to read the primitive version of this field (null if Objects Only)
* @param objectGetter getter to read the object version of this field
*
* @return getter for the given representation that returns the given type
*/
public static MethodHandle createGetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveGetter, final MethodHandle objectGetter) {
final int fti = forType == null ? -1 : getAccessorTypeIndex(forType);
final int ti = getAccessorTypeIndex(type);
if (fti == TYPE_OBJECT_INDEX || OBJECT_FIELDS_ONLY) {
if (ti == TYPE_OBJECT_INDEX) {
return objectGetter;
}
return MH.filterReturnValue(objectGetter, CONVERT_OBJECT[ti]);
}
assert !OBJECT_FIELDS_ONLY;
if (forType == null) {
return GET_UNDEFINED[ti];
}
final MethodType pmt = primitiveGetter.type();
switch (fti) {
case TYPE_INT_INDEX:
case TYPE_LONG_INDEX:
switch (ti) {
case TYPE_INT_INDEX:
//get int while an int, truncating cast of long value
return MH.explicitCastArguments(primitiveGetter, pmt.changeReturnType(int.class));
case TYPE_LONG_INDEX:
return primitiveGetter;
default:
return MH.asType(primitiveGetter, pmt.changeReturnType(type));
}
case TYPE_DOUBLE_INDEX:
final MethodHandle getPrimitiveAsDouble = MH.filterReturnValue(primitiveGetter, UNPACK_DOUBLE);
switch (ti) {
case TYPE_INT_INDEX:
case TYPE_LONG_INDEX:
return MH.explicitCastArguments(getPrimitiveAsDouble, pmt.changeReturnType(type));
case TYPE_DOUBLE_INDEX:
return getPrimitiveAsDouble;
default:
return MH.asType(getPrimitiveAsDouble, pmt.changeReturnType(Object.class));
}
default:
assert false;
return null;
}
}
private static final MethodHandle IS_TYPE_GUARD = findOwnMH("isType", boolean.class, Class.class, Object.class);
@SuppressWarnings("unused")
private static boolean isType(final Class<?> boxedForType, final Object x) {
return x.getClass() == boxedForType;
}
private static Class<? extends Number> getBoxedType(final Class<?> forType) {
if (forType == int.class) {
return Integer.class;
}
if (forType == long.class) {
return Long.class;
}
if (forType == double.class) {
return Double.class;
}
assert false;
return null;
}
/**
* If we are setting boxed types (because the compiler couldn't determine which they were) to
* a primitive field, we can reuse the primitive field getter, as long as we are setting an element
* of the same boxed type as the primitive type representation
*
* @param forType the current type
* @param primitiveSetter primitive setter for the current type with an element of the current type
* @param objectSetter the object setter
*
* @return method handle that checks if the element to be set is of the currenttype, even though it's boxed
* and instead of using the generic object setter, that would blow up the type and invalidate the map,
* unbox it and call the primitive setter instead
*/
public static MethodHandle createGuardBoxedPrimitiveSetter(final Class<?> forType, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
final Class<? extends Number> boxedForType = getBoxedType(forType);
//object setter that checks for primitive if current type is primitive
return MH.guardWithTest(
MH.insertArguments(
MH.dropArguments(
IS_TYPE_GUARD,
1,
Object.class),
0,
boxedForType),
MH.asType(
primitiveSetter,
objectSetter.type()),
objectSetter);
}
/**
* This is similar to the {@link ObjectClassGenerator#createGetter} function. Performs
* the necessary operations to massage a setter operand of type {@code type} to
* fit into the primitive field (if primitive and dual fields is enabled) or into
* the object field (box if primitive and dual fields is disabled)
*
* @param forType representation of the underlying object
* @param type representation of field to write, and setter signature
* @param primitiveSetter setter that writes to the primitive field (null if Objects Only)
* @param objectSetter setter that writes to the object field
*
* @return the setter for the given representation that takes a {@code type}
*/
public static MethodHandle createSetter(final Class<?> forType, final Class<?> type, final MethodHandle primitiveSetter, final MethodHandle objectSetter) {
assert forType != null;
final int fti = getAccessorTypeIndex(forType);
final int ti = getAccessorTypeIndex(type);
if (fti == TYPE_OBJECT_INDEX || OBJECT_FIELDS_ONLY) {
if (ti == TYPE_OBJECT_INDEX) {
return objectSetter;
}
return MH.asType(objectSetter, objectSetter.type().changeParameterType(1, type));
}
assert !OBJECT_FIELDS_ONLY;
final MethodType pmt = primitiveSetter.type();
switch (fti) {
case TYPE_INT_INDEX:
case TYPE_LONG_INDEX:
switch (ti) {
case TYPE_INT_INDEX:
return MH.asType(primitiveSetter, pmt.changeParameterType(1, int.class));
case TYPE_LONG_INDEX:
return primitiveSetter;
case TYPE_DOUBLE_INDEX:
return MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE);
default:
return objectSetter;
}
case TYPE_DOUBLE_INDEX:
if (ti == TYPE_OBJECT_INDEX) {
return objectSetter;
}
return MH.asType(MH.filterArguments(primitiveSetter, 1, PACK_DOUBLE), pmt.changeParameterType(1, type));
default:
assert false;
return null;
}
}
//
// Provide generic getters and setters for undefined types. If a type is undefined, all
// and marshals the set to the correct setter depending on the type of the value being set.
// Note that there are no actual undefined versions of int, long and double in JavaScript,
// but executing toInt32, toLong and toNumber always returns a working result, 0, 0L or NaN
//
/** The value of Undefined cast to an int32 */
public static final int UNDEFINED_INT = 0;
/** The value of Undefined cast to a long */
public static final long UNDEFINED_LONG = 0L;
/** The value of Undefined cast to a double */
public static final double UNDEFINED_DOUBLE = Double.NaN;
/**
* Compute type name for correct undefined getter
* @param type the type
* @return name of getter
*/
private static String typeName(final Type type) {
String name = type.getTypeClass().getName();
final int dot = name.lastIndexOf('.');
if (dot != -1) {
name = name.substring(dot + 1);
}
return Character.toUpperCase(name.charAt(0)) + name.substring(1);
}
/**
* Handles for undefined getters of the different types
*/
private static final MethodHandle[] GET_UNDEFINED = new MethodHandle[ObjectClassGenerator.getNumberOfAccessorTypes()];
/**
* Used to wrap getters for undefined values, where this matters. Currently only in dual fields.
* If an object starts out as undefined it needs special getters until it has been assigned
* something the first time
*
* @param returnType type to cast the undefined to
*
* @return undefined as returnType
*/
public static MethodHandle getUndefined(final Class<?> returnType) {
return GET_UNDEFINED[ObjectClassGenerator.getAccessorTypeIndex(returnType)];
}
static {
int pos = 0;
for (final Type type : ACCESSOR_TYPES) {
GET_UNDEFINED[pos++] = findOwnMH("getUndefined" + typeName(type), type.getTypeClass(), Object.class);
}
}
@SuppressWarnings("unused")
private static int getUndefinedInt(final Object obj) {
return UNDEFINED_INT;
}
@SuppressWarnings("unused")
private static long getUndefinedLong(final Object obj) {
return UNDEFINED_LONG;
}
@SuppressWarnings("unused")
private static double getUndefinedDouble(final Object obj) {
return UNDEFINED_DOUBLE;
}
@SuppressWarnings("unused")
private static Object getUndefinedObject(final Object obj) {
return ScriptRuntime.UNDEFINED;
}
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findStatic(MethodHandles.lookup(), ObjectClassGenerator.class, name, MH.type(rtype, types));
}
}

View File

@ -0,0 +1,175 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.objects;
import java.util.List;
import jdk.nashorn.internal.codegen.CodeGenerator;
import jdk.nashorn.internal.codegen.CompileUnit;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.MethodEmitter;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.PropertyMap;
/**
* Base class for object creation code generation.
*/
public abstract class ObjectCreator {
/** Compile unit for this ObjectCreator, see CompileUnit */
protected final CompileUnit compileUnit;
/** List of keys to initiate in this ObjectCreator */
protected final List<String> keys;
/** List of symbols to initiate in this ObjectCreator */
protected final List<Symbol> symbols;
/** Code generator */
protected final CodeGenerator codegen;
private final boolean isScope;
private final boolean isVarArg;
private int fieldCount;
private int paramCount;
private String fieldObjectClassName;
private Class<?> fieldObjectClass;
private PropertyMap propertyMap;
/**
* Constructor
*
* @param codegen the code generator
* @param keys the keys
* @param symbols the symbols corresponding to keys, same index
* @param isScope is this object scope
* @param isVarArg is this object var arg
*/
protected ObjectCreator(final CodeGenerator codegen, final List<String> keys, final List<Symbol> symbols, final boolean isScope, final boolean isVarArg) {
this.codegen = codegen;
this.compileUnit = codegen.getCurrentCompileUnit();
this.keys = keys;
this.symbols = symbols;
this.isScope = isScope;
this.isVarArg = isVarArg;
countFields();
findClass();
}
/**
* Tally the number of fields and parameters.
*/
private void countFields() {
for (final Symbol symbol : this.symbols) {
if (symbol != null) {
if (isVarArg() && symbol.isParam()) {
symbol.setFieldIndex(paramCount++);
} else {
symbol.setFieldIndex(fieldCount++);
}
}
}
}
/**
* Locate (or indirectly create) the object container class.
*/
private void findClass() {
fieldObjectClassName = isScope() ?
ObjectClassGenerator.getClassName(fieldCount, paramCount) :
ObjectClassGenerator.getClassName(fieldCount);
try {
this.fieldObjectClass = Context.forStructureClass(Compiler.binaryName(fieldObjectClassName));
} catch (final ClassNotFoundException e) {
throw new AssertionError("Nashorn has encountered an internal error. Structure can not be created.");
}
}
/**
* Generate code for making the object.
* @param method Script method.
*/
public abstract void makeObject(final MethodEmitter method);
/**
* Create a new MapCreator
* @param clazz type of MapCreator
* @return map creator instantiated by type
*/
protected MapCreator newMapCreator(final Class<?> clazz) {
return new MapCreator(clazz, keys, symbols);
}
/**
* Construct the property map appropriate for the object.
*/
protected void makeMap() {
if (keys.isEmpty()) { //empty map
propertyMap = PropertyMap.newMap(fieldObjectClass);
return;
}
propertyMap = newMapCreator(fieldObjectClass).makeMap(isVarArg());
}
/**
* Emit the correct map for the object.
* @param method method emitter
* @return the method emitter
*/
protected MethodEmitter loadMap(final MethodEmitter method) {
codegen.loadConstant(propertyMap);
return method;
}
/**
* Get the class name for the object class,
* e.g. {@code com.nashorn.oracle.scripts.JO$2P0}
*
* @return script class name
*/
public String getClassName() {
return fieldObjectClassName;
}
/**
* Is this a scope object
* @return true if scope
*/
protected boolean isScope() {
return isScope;
}
/**
* Is this a vararg object
* @return true if vararg
*/
protected boolean isVarArg() {
return isVarArg;
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.objects;
import java.util.List;
import jdk.nashorn.internal.ir.Symbol;
import jdk.nashorn.internal.runtime.Property;
/**
* This map creator is used to guarantee that all properties start out as
* object types. Only semantically significant in the -Dnashorn.fields.dual=true world,
* where we want to avoid invalidation upon initialization e.g. for var x = {a:"str"};
*/
public class ObjectMapCreator extends MapCreator {
/**
* Constructor
*
* @param structure structure for object class
* @param keys keys in object
* @param symbols symbols in object corresponding to keys
*/
public ObjectMapCreator(final Class<?> structure, final List<String> keys, final List<Symbol> symbols) {
super(structure, keys, symbols);
}
@Override
protected int getPropertyFlags(final Symbol symbol, final boolean isVarArg) {
return super.getPropertyFlags(symbol, isVarArg) | Property.IS_ALWAYS_OBJECT;
}
}

View File

@ -0,0 +1,107 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
import static jdk.internal.org.objectweb.asm.Opcodes.AALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.AASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.ANEWARRAY;
import static jdk.internal.org.objectweb.asm.Opcodes.ARRAYLENGTH;
import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* This is an array type, i.e. OBJECT_ARRAY, NUMBER_ARRAY.
*/
public class ArrayType extends ObjectType implements BytecodeArrayOps {
/**
* Constructor
*
* @param clazz the Java class representation of the array
*/
protected ArrayType(final Class<?> clazz) {
super(clazz);
}
/**
* Get the element type of the array elements e.g. for OBJECT_ARRAY, this is OBJECT
*
* @return the element type
*/
public Type getElementType() {
return Type.typeFor(getTypeClass().getComponentType());
}
@Override
public void astore(final MethodVisitor method) {
method.visitInsn(AASTORE);
}
@Override
public Type aload(final MethodVisitor method) {
method.visitInsn(AALOAD);
return getElementType();
}
@Override
public Type arraylength(final MethodVisitor method) {
method.visitInsn(ARRAYLENGTH);
return INT;
}
@Override
public Type newarray(final MethodVisitor method) {
method.visitTypeInsn(ANEWARRAY, getElementType().getInternalName());
return this;
}
@Override
public Type newarray(final MethodVisitor method, final int dims) {
method.visitMultiANewArrayInsn(getInternalName(), dims);
return this;
}
@Override
public Type load(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(ALOAD, slot);
return this;
}
@Override
public String toString() {
return "array<elementType=" + getElementType().getTypeClass().getSimpleName() + '>';
}
@Override
public Type convert(final MethodVisitor method, final Type to) {
assert to.isObject();
assert !to.isArray() || ((ArrayType)to).getElementType() == getElementType();
return to;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
/**
* This class represents a numeric type that can be used for bit operations.
*/
public abstract class BitwiseType extends NumericType implements BytecodeBitwiseOps {
/**
* Constructor
*
* @param name name of type
* @param clazz Java class used to represent type
* @param weight weight of type
* @param slots number of bytecode slots this type takes up
*/
protected BitwiseType(final String name, final Class<?> clazz, final int weight, final int slots) {
super(name, clazz, weight, slots);
}
}

View File

@ -0,0 +1,161 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.objects.ObjectClassGenerator;
import jdk.nashorn.internal.runtime.JSType;
/**
* The boolean type class
*/
public final class BooleanType extends Type {
private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Boolean.class, "valueOf", Boolean.class, boolean.class);
/**
* Constructor
*/
protected BooleanType() {
super("boolean", boolean.class, 1, 1);
}
@Override
public Type nextWider() {
return INT;
}
@Override
public Class<?> getBoxedType() {
return Boolean.class;
}
@Override
public Type loadUndefined(final MethodVisitor method) {
method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_INT);
return BOOLEAN;
}
@Override
public Type loadEmpty(final MethodVisitor method) {
assert false : "unsupported operation";
return null;
}
@Override
public void _return(final MethodVisitor method) {
method.visitInsn(IRETURN);
}
@Override
public Type load(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(ILOAD, slot);
return BOOLEAN;
}
@Override
public void store(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(ISTORE, slot);
}
@Override
public Type ldc(final MethodVisitor method, final Object c) {
assert c instanceof Boolean;
method.visitInsn((Boolean) c ? ICONST_1 : ICONST_0);
return BOOLEAN;
}
@Override
public Type convert(final MethodVisitor method, final Type to) {
if (isEquivalentTo(to)) {
return to;
}
if (to.isNumber()) {
convert(method, OBJECT);
invokeStatic(method, JSType.TO_NUMBER);
} else if (to.isInteger()) {
return to; // do nothing.
} else if (to.isLong()) {
convert(method, OBJECT);
invokeStatic(method, JSType.TO_UINT32);
} else if (to.isLong()) {
convert(method, OBJECT);
invokeStatic(method, JSType.TO_LONG);
} else if (to.isString()) {
invokeStatic(method, VALUE_OF);
invokeStatic(method, JSType.TO_PRIMITIVE);
invokeStatic(method, JSType.TO_STRING);
} else if (to.isObject()) {
invokeStatic(method, VALUE_OF);
} else {
assert false : "Illegal conversion " + this + " -> " + to;
}
return to;
}
@Override
public Type add(final MethodVisitor method) {
assert false : "unsupported operation";
return null;
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* Array operations, not supported by all ops
*/
interface BytecodeArrayOps {
/**
* Load an array element given that the array and its index are already on
* the stack
*
* @param method method visitor
* @return the array element type
*
*/
Type aload(MethodVisitor method);
/**
* Store an array element given that the array and its index and the element
* are on the stack
*
* @param method method visitor
*/
void astore(MethodVisitor method);
/**
* Generate an array length operation
*
* @param method method method visitor
* @return length of the array
*/
Type arraylength(MethodVisitor method);
/**
* Create a new array of this array type and length on stack
*
* @param method method visitor
* @return the type of the array
*/
Type newarray(MethodVisitor method);
/**
* Create a new multi array of this array type and allocate the number of
* dimensions given
*
* @param method method visitor
* @param dims number of dimensions
* @return the type of the new array
*/
Type newarray(MethodVisitor method, int dims);
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* Bitwise operations not supported by all types
*/
interface BytecodeBitwiseOps {
/**
* Pop and logically shift the two values on top of the stack (steps, value)
* right and push the result on the stack
*
* @param method method visitor
* @return result type
*/
Type shr(MethodVisitor method);
/**
* Pop and arithmetically shift of the two values on top of the stack
* (steps, value) right and push the result on the stack
*
* @param method method visitor
* @return result type
*/
Type sar(MethodVisitor method);
/**
* Pop and logically shift of the two values on top of the stack (steps,
* value) left and push the result on the stack
*
* @param method method visitor
* @return result type
*/
Type shl(MethodVisitor method);
/**
* Pop and AND the two values on top of the stack and push the result on the
* stack
*
* @param method method visitor
* @return result type
*/
Type and(MethodVisitor method);
/**
* Pop and OR the two values on top of the stack and push the result on the
* stack
*
* @param method method visitor
* @return result type
*/
Type or(MethodVisitor method);
/**
* Pop and XOR the two values on top of the stack and push the result on the
* stack
*
* @param method method visitor
* @return result type
*/
Type xor(MethodVisitor method);
/**
* Comparison with int return value, e.g. LCMP.
*
* @param method the method visitor
* @return int return value
*/
Type cmp(MethodVisitor method);
}

View File

@ -0,0 +1,93 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* Numeric operations, not supported by all types
*/
interface BytecodeNumericOps {
/**
* Pop and negate the value on top of the stack and push the result
*
* @param method method visitor
*
* @return result type
*/
Type neg(MethodVisitor method);
/**
* Pop two values on top of the stack and subtract the first from the
* second, pushing the result on the stack
*
* @param method method visitor
*
* @return result type
*/
Type sub(MethodVisitor method);
/**
* Pop and multiply the two values on top of the stack and push the result
* on the stack
*
* @param method method visitor
*
* @return result type
*/
Type mul(MethodVisitor method);
/**
* Pop two values on top of the stack and divide the first with the second,
* pushing the result on the stack
*
* @param method method visitor
*
* @return result type
*/
Type div(MethodVisitor method);
/**
* Pop two values on top of the stack and compute the modulo of the first
* with the second, pushing the result on the stack
*
* @param method method visitor
*
* @return result type
*/
Type rem(MethodVisitor method);
/**
* Comparison with int return value, e.g. LCMP, DCMP.
*
* @param method the method visitor
* @param isCmpG is this a double style cmpg
*
* @return int return value
*/
Type cmp(MethodVisitor method, boolean isCmpG);
}

View File

@ -0,0 +1,158 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
import jdk.internal.org.objectweb.asm.MethodVisitor;
/**
* Interface for byte code generation for all runtime types. Each
* type implements this interface and provides the type specific
* operations to do the generic things described herein.
*
* The bytecode ops are coupled to a MethodVisitor from ASM for
* byte code generation. They know nothing about our MethodGenerator,
* which is the abstraction for working with Nashorn JS types
* For exmaple, anything like "two or one slots" for a type, which
* is represented in bytecode and ASM, is abstracted away in the
* MethodGenerator. There you just say "dup" or "store".
*
* @see Type
* @see MethodVisitor
*/
interface BytecodeOps {
/**
* Duplicate top entry of stack. If a too large depth is
* given, so that there are no possible bytecode instructions
* available to generate the dup sequence, null is returned.
*
* @param method method visitor
* @param depth how far should the copy be pushed down
*
* @return the type at the top of the stack or null
*/
Type dup(MethodVisitor method, int depth);
/**
* Pop an entry of this type from the top of the bytecode
* stack. This works regardless of what category this type
* is
*
* @param method method visitor
*
* @return the popped type
*/
Type pop(MethodVisitor method);
/**
* Swap this type with the bytecode stack with the one below
* Generate appropriate code no matter the categories of the
* two types
*
* @param method method visitor
* @param other the type below this one on the stack
*
* @return the other type
*/
Type swap(MethodVisitor method, Type other);
/**
* Pop two values on top of the stack and add the
* first to the second, pushing the result on the stack
*
* @param method method visitor
* @return result type
*/
Type add(MethodVisitor method);
/**
* Load a variable from a local slot to the stack
*
* @param method method visitor
* @param slot the slot to load
*
* @return the type that was loaded
*/
Type load(MethodVisitor method, int slot);
/**
* Store a variable from the stack to a local slot
*
* @param method method visitor
* @param slot the slot to store to
*/
void store(MethodVisitor method, int slot);
/**
* Load a constant to the stack.
*
* @param method method visitor
* @param c the value of the constant
*
* @return the type at the top of the stack after load
*/
Type ldc(MethodVisitor method, Object c);
/**
* Load the "undefined" value to the stack. Note that
* there may be different representations of this for
* e.g. doubles and objects. Abstraction removes this
*
* @param method method visitor.
*
* @return the undefined type at the top of the stack
*/
Type loadUndefined(MethodVisitor method);
/**
* Load the "empty" value to the stack.
*
* @param method method visitor.
* @return the undefined type at the top of the stack
*/
Type loadEmpty(MethodVisitor method);
/**
* Generate code that pops and casts the element on top of the
* stack to another type, given as parameter
*
* @param method method visitor
* @param to the type to cast to
*
* @return the to type
*/
Type convert(MethodVisitor method, Type to);
/**
* Return the parameter on top of the stack
* from a method
*
* @param method the method visitor
*/
void _return(MethodVisitor method);
}

View File

@ -0,0 +1,260 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
import static jdk.internal.org.objectweb.asm.Opcodes.BIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.I2D;
import static jdk.internal.org.objectweb.asm.Opcodes.I2L;
import static jdk.internal.org.objectweb.asm.Opcodes.IADD;
import static jdk.internal.org.objectweb.asm.Opcodes.IAND;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_2;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_3;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_4;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_5;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_M1;
import static jdk.internal.org.objectweb.asm.Opcodes.IDIV;
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.IMUL;
import static jdk.internal.org.objectweb.asm.Opcodes.INEG;
import static jdk.internal.org.objectweb.asm.Opcodes.IOR;
import static jdk.internal.org.objectweb.asm.Opcodes.IREM;
import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.ISHL;
import static jdk.internal.org.objectweb.asm.Opcodes.ISHR;
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.ISUB;
import static jdk.internal.org.objectweb.asm.Opcodes.IUSHR;
import static jdk.internal.org.objectweb.asm.Opcodes.IXOR;
import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.objects.ObjectClassGenerator;
/**
* Type class: INT
*/
class IntType extends BitwiseType {
private static final CompilerConstants.Call TO_STRING = staticCallNoLookup(Integer.class, "toString", String.class, int.class);
private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Integer.class, "valueOf", Integer.class, int.class);
protected IntType() {
super("int", int.class, 2, 1);
}
@Override
public Type nextWider() {
return LONG;
}
@Override
public Class<?> getBoxedType() {
return Integer.class;
}
@Override
public Type ldc(final MethodVisitor method, final Object c) {
assert c instanceof Integer;
final int value = ((Integer) c).intValue();
switch (value) {
case -1:
method.visitInsn(ICONST_M1);
break;
case 0:
method.visitInsn(ICONST_0);
break;
case 1:
method.visitInsn(ICONST_1);
break;
case 2:
method.visitInsn(ICONST_2);
break;
case 3:
method.visitInsn(ICONST_3);
break;
case 4:
method.visitInsn(ICONST_4);
break;
case 5:
method.visitInsn(ICONST_5);
break;
default:
if (value == (byte) value) {
method.visitIntInsn(BIPUSH, value);
} else if (value == (short) value) {
method.visitIntInsn(SIPUSH, value);
} else {
method.visitLdcInsn(c);
}
break;
}
return Type.INT;
}
@Override
public Type convert(final MethodVisitor method, final Type to) {
if (to.isEquivalentTo(this)) {
return to;
}
if (to.isNumber()) {
method.visitInsn(I2D);
} else if (to.isLong()) {
method.visitInsn(I2L);
} else if (to.isBoolean()) {
//nop
} else if (to.isString()) {
invokeStatic(method, TO_STRING);
} else if (to.isObject()) {
invokeStatic(method, VALUE_OF);
} else {
assert false : "Illegal conversion " + this + " -> " + to;
}
return to;
}
@Override
public Type add(final MethodVisitor method) {
method.visitInsn(IADD);
return INT;
}
@Override
public Type shr(final MethodVisitor method) {
method.visitInsn(IUSHR);
return INT;
}
@Override
public Type sar(final MethodVisitor method) {
method.visitInsn(ISHR);
return INT;
}
@Override
public Type shl(final MethodVisitor method) {
method.visitInsn(ISHL);
return INT;
}
@Override
public Type and(final MethodVisitor method) {
method.visitInsn(IAND);
return INT;
}
@Override
public Type or(final MethodVisitor method) {
method.visitInsn(IOR);
return INT;
}
@Override
public Type xor(final MethodVisitor method) {
method.visitInsn(IXOR);
return INT;
}
@Override
public Type load(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(ILOAD, slot);
return INT;
}
@Override
public void store(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(ISTORE, slot);
}
@Override
public Type sub(final MethodVisitor method) {
method.visitInsn(ISUB);
return INT;
}
@Override
public Type mul(final MethodVisitor method) {
method.visitInsn(IMUL);
return INT;
}
@Override
public Type div(final MethodVisitor method) {
method.visitInsn(IDIV);
return INT;
}
@Override
public Type rem(final MethodVisitor method) {
method.visitInsn(IREM);
return INT;
}
@Override
public Type neg(final MethodVisitor method) {
method.visitInsn(INEG);
return INT;
}
@Override
public void _return(final MethodVisitor method) {
method.visitInsn(IRETURN);
}
@Override
public Type loadUndefined(final MethodVisitor method) {
method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_INT);
return INT;
}
@Override
public Type loadEmpty(final MethodVisitor method) {
assert false : "unsupported operation";
return null;
}
@Override
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
assert false : "unsupported operation";
return null;
}
@Override
public Type cmp(final MethodVisitor method) {
assert false : "unsupported operation";
return null;
}
}

View File

@ -0,0 +1,229 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
import static jdk.internal.org.objectweb.asm.Opcodes.L2D;
import static jdk.internal.org.objectweb.asm.Opcodes.L2I;
import static jdk.internal.org.objectweb.asm.Opcodes.LADD;
import static jdk.internal.org.objectweb.asm.Opcodes.LAND;
import static jdk.internal.org.objectweb.asm.Opcodes.LCMP;
import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.LCONST_1;
import static jdk.internal.org.objectweb.asm.Opcodes.LDIV;
import static jdk.internal.org.objectweb.asm.Opcodes.LLOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.LMUL;
import static jdk.internal.org.objectweb.asm.Opcodes.LNEG;
import static jdk.internal.org.objectweb.asm.Opcodes.LOR;
import static jdk.internal.org.objectweb.asm.Opcodes.LREM;
import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.LSHL;
import static jdk.internal.org.objectweb.asm.Opcodes.LSHR;
import static jdk.internal.org.objectweb.asm.Opcodes.LSTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.LSUB;
import static jdk.internal.org.objectweb.asm.Opcodes.LUSHR;
import static jdk.internal.org.objectweb.asm.Opcodes.LXOR;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.objects.ObjectClassGenerator;
/**
* Type class: LONG
*/
class LongType extends BitwiseType {
private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Long.class, "valueOf", Long.class, long.class);
protected LongType(final String name) {
super(name, long.class, 3, 2);
}
protected LongType() {
this("long");
}
@Override
public Type nextWider() {
return NUMBER;
}
@Override
public Class<?> getBoxedType() {
return Long.class;
}
@Override
public Type cmp(final MethodVisitor method) {
method.visitInsn(LCMP);
return INT;
}
@Override
public Type load(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(LLOAD, slot);
return LONG;
}
@Override
public void store(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(LSTORE, slot);
}
@Override
public Type ldc(final MethodVisitor method, final Object c) {
assert c instanceof Long;
final long value = (Long) c;
if (value == 0L) {
method.visitInsn(LCONST_0);
} else if (value == 1L) {
method.visitInsn(LCONST_1);
} else {
method.visitLdcInsn(c);
}
return Type.LONG;
}
@Override
public Type convert(final MethodVisitor method, final Type to) {
if (isEquivalentTo(to)) {
return to;
}
if (to.isNumber()) {
method.visitInsn(L2D);
} else if (to.isInteger()) {
method.visitInsn(L2I);
} else if (to.isBoolean()) {
method.visitInsn(L2I);
} else if (to.isObject()) {
invokeStatic(method, VALUE_OF);
} else {
assert false : "Illegal conversion " + this + " -> " + to;
}
return to;
}
@Override
public Type add(final MethodVisitor method) {
method.visitInsn(LADD);
return LONG;
}
@Override
public Type sub(final MethodVisitor method) {
method.visitInsn(LSUB);
return LONG;
}
@Override
public Type mul(final MethodVisitor method) {
method.visitInsn(LMUL);
return LONG;
}
@Override
public Type div(final MethodVisitor method) {
method.visitInsn(LDIV);
return LONG;
}
@Override
public Type rem(final MethodVisitor method) {
method.visitInsn(LREM);
return LONG;
}
@Override
public Type shr(final MethodVisitor method) {
method.visitInsn(LUSHR);
return LONG;
}
@Override
public Type sar(final MethodVisitor method) {
method.visitInsn(LSHR);
return LONG;
}
@Override
public Type shl(final MethodVisitor method) {
method.visitInsn(LSHL);
return LONG;
}
@Override
public Type and(final MethodVisitor method) {
method.visitInsn(LAND);
return LONG;
}
@Override
public Type or(final MethodVisitor method) {
method.visitInsn(LOR);
return LONG;
}
@Override
public Type xor(final MethodVisitor method) {
method.visitInsn(LXOR);
return LONG;
}
@Override
public Type neg(final MethodVisitor method) {
method.visitInsn(LNEG);
return LONG;
}
@Override
public void _return(final MethodVisitor method) {
method.visitInsn(LRETURN);
}
@Override
public Type loadUndefined(final MethodVisitor method) {
method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_LONG);
return LONG;
}
@Override
public Type loadEmpty(final MethodVisitor method) {
assert false : "unsupported operation";
return null;
}
@Override
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
return cmp(method);
}
}

View File

@ -0,0 +1,177 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
import static jdk.internal.org.objectweb.asm.Opcodes.DADD;
import static jdk.internal.org.objectweb.asm.Opcodes.DCMPG;
import static jdk.internal.org.objectweb.asm.Opcodes.DCMPL;
import static jdk.internal.org.objectweb.asm.Opcodes.DCONST_0;
import static jdk.internal.org.objectweb.asm.Opcodes.DCONST_1;
import static jdk.internal.org.objectweb.asm.Opcodes.DDIV;
import static jdk.internal.org.objectweb.asm.Opcodes.DLOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.DMUL;
import static jdk.internal.org.objectweb.asm.Opcodes.DNEG;
import static jdk.internal.org.objectweb.asm.Opcodes.DREM;
import static jdk.internal.org.objectweb.asm.Opcodes.DRETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.DSTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.DSUB;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.objects.ObjectClassGenerator;
import jdk.nashorn.internal.runtime.JSType;
class NumberType extends NumericType {
private static final CompilerConstants.Call VALUE_OF = staticCallNoLookup(Double.class, "valueOf", Double.class, double.class);
protected NumberType() {
super("double", double.class, 4, 2);
}
@Override
public Type nextWider() {
return OBJECT;
}
@Override
public Class<?> getBoxedType() {
return Double.class;
}
@Override
public Type cmp(final MethodVisitor method, final boolean isCmpG) {
method.visitInsn(isCmpG ? DCMPG : DCMPL);
return INT;
}
@Override
public Type load(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(DLOAD, slot);
return NUMBER;
}
@Override
public void store(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(DSTORE, slot);
}
@Override
public Type loadUndefined(final MethodVisitor method) {
method.visitLdcInsn(ObjectClassGenerator.UNDEFINED_DOUBLE);
return NUMBER;
}
@Override
public Type loadEmpty(final MethodVisitor method) {
assert false : "unsupported operation";
return null;
}
@Override
public Type ldc(final MethodVisitor method, final Object c) {
assert c instanceof Double;
final double value = (Double) c;
if (Double.doubleToLongBits(value) == 0L) { // guard against -0.0
method.visitInsn(DCONST_0);
} else if (value == 1.0) {
method.visitInsn(DCONST_1);
} else {
method.visitLdcInsn(value);
}
return NUMBER;
}
@Override
public Type convert(final MethodVisitor method, final Type to) {
if (isEquivalentTo(to)) {
return null;
}
if (to.isInteger()) {
invokeStatic(method, JSType.TO_INT32_D);
} else if (to.isLong()) {
invokeStatic(method, JSType.TO_INT64_D);
} else if (to.isBoolean()) {
invokeStatic(method, JSType.TO_BOOLEAN_D);
} else if (to.isString()) {
invokeStatic(method, JSType.TO_STRING_D);
} else if (to.isObject()) {
invokeStatic(method, VALUE_OF);
} else {
assert false : "Illegal conversion " + this + " -> " + to;
}
return to;
}
@Override
public Type add(final MethodVisitor method) {
method.visitInsn(DADD);
return NUMBER;
}
@Override
public Type sub(final MethodVisitor method) {
method.visitInsn(DSUB);
return NUMBER;
}
@Override
public Type mul(final MethodVisitor method) {
method.visitInsn(DMUL);
return NUMBER;
}
@Override
public Type div(final MethodVisitor method) {
method.visitInsn(DDIV);
return NUMBER;
}
@Override
public Type rem(final MethodVisitor method) {
method.visitInsn(DREM);
return NUMBER;
}
@Override
public Type neg(final MethodVisitor method) {
method.visitInsn(DNEG);
return NUMBER;
}
@Override
public void _return(final MethodVisitor method) {
method.visitInsn(DRETURN);
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
/**
* This is a numeric type, i.e. NUMBER, LONG, INT, INT32.
*/
public abstract class NumericType extends Type implements BytecodeNumericOps {
/**
* Constructor
*
* @param name name of type
* @param clazz Java class used to represent type
* @param weight weight of type
* @param slots number of bytecode slots this type takes up
*/
protected NumericType(final String name, final Class<?> clazz, final int weight, final int slots) {
super(name, clazz, weight, slots);
}
}

View File

@ -0,0 +1,174 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL;
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN;
import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST;
import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC;
import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import java.lang.invoke.MethodHandle;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.Undefined;
/**
* Type class: OBJECT This is the object type, used for all object types. It can
* contain a class that is a more specialized object
*/
class ObjectType extends Type {
protected ObjectType() {
this(Object.class);
}
protected ObjectType(final Class<?> clazz) {
super("object",
clazz,
clazz == Object.class ? Type.MAX_WEIGHT : 10,
1);
}
@Override
public String toString() {
return "object" + (getTypeClass() != Object.class ? "<type=" + getTypeClass().getSimpleName() + '>' : "");
}
@Override
public Type add(final MethodVisitor method) {
invokeStatic(method, ScriptRuntime.ADD);
return Type.OBJECT;
}
@Override
public Type load(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(ALOAD, slot);
if (slot == CompilerConstants.THIS.slot()) {
return Type.THIS;
}
return Type.OBJECT;
}
@Override
public void store(final MethodVisitor method, final int slot) {
assert slot != -1;
method.visitVarInsn(ASTORE, slot);
}
@Override
public Type loadUndefined(final MethodVisitor method) {
method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "UNDEFINED", typeDescriptor(Undefined.class));
return OBJECT;
}
@Override
public Type loadEmpty(final MethodVisitor method) {
method.visitFieldInsn(GETSTATIC, className(ScriptRuntime.class), "EMPTY", typeDescriptor(Undefined.class));
return OBJECT;
}
@Override
public Type ldc(final MethodVisitor method, final Object c) {
if (c == null) {
method.visitInsn(ACONST_NULL);
} else if (c instanceof Undefined) {
return loadUndefined(method);
} else if (c instanceof String) {
method.visitLdcInsn(c);
return STRING;
} else if (c instanceof Handle) {
method.visitLdcInsn(c);
return Type.typeFor(MethodHandle.class);
} else {
assert false : "implementation missing for class " + c.getClass() + " value=" + c;
}
return OBJECT;
}
@Override
public Type convert(final MethodVisitor method, final Type to) {
final boolean toString = to.isString();
if (!toString) {
if (to.isArray()) {
final Type elemType = ((ArrayType)to).getElementType();
//note that if this an array, things won't work. see {link @ArrayType} subclass.
//we also have the unpleasant case of NativeArray which looks like an Object, but is
//an array to the type system. This is treated specially at the known load points
if (elemType.isString()) {
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(String[].class));
} else if (elemType.isNumber()) {
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(double[].class));
} else if (elemType.isLong()) {
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(long[].class));
} else if (elemType.isInteger()) {
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(int[].class));
} else {
method.visitTypeInsn(CHECKCAST, CompilerConstants.className(Object[].class));
}
return to;
} else if (to.isObject()) {
return to;
}
} else if (isString()) {
return to;
}
if (to.isInteger()) {
invokeStatic(method, JSType.TO_INT32);
} else if (to.isNumber()) {
invokeStatic(method, JSType.TO_NUMBER);
} else if (to.isLong()) {
invokeStatic(method, JSType.TO_INT64);
} else if (to.isBoolean()) {
invokeStatic(method, JSType.TO_BOOLEAN);
} else if (to.isString()) {
invokeStatic(method, JSType.TO_PRIMITIVE);
invokeStatic(method, JSType.TO_STRING);
} else {
assert false : "Illegal conversion " + this + " -> " + to + " " + isString() + " " + toString;
}
return to;
}
@Override
public void _return(final MethodVisitor method) {
method.visitInsn(ARETURN);
}
}

View File

@ -0,0 +1,882 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.codegen.types;
import static jdk.internal.org.objectweb.asm.Opcodes.DALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.DASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP2;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP2_X1;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP2_X2;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X1;
import static jdk.internal.org.objectweb.asm.Opcodes.DUP_X2;
import static jdk.internal.org.objectweb.asm.Opcodes.IALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.IASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.LASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.NEWARRAY;
import static jdk.internal.org.objectweb.asm.Opcodes.POP;
import static jdk.internal.org.objectweb.asm.Opcodes.POP2;
import static jdk.internal.org.objectweb.asm.Opcodes.SWAP;
import static jdk.internal.org.objectweb.asm.Opcodes.T_DOUBLE;
import static jdk.internal.org.objectweb.asm.Opcodes.T_INT;
import java.lang.invoke.MethodHandle;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
/**
* This is the representation of a JavaScript type, disassociated from java
* Classes, with the basis for conversion weight, mapping to ASM types
* and implementing the ByteCodeOps interface which tells this type
* how to generate code for various operations.
*
* Except for ClassEmitter, this is the only class that has to know
* about the underlying byte code generation system.
*
* The different types know how to generate bytecode for the different
* operations, inherited from BytecodeOps, that they support. This avoids
* if/else chains depending on type in several cases and allows for
* more readable and shorter code
*
* The Type class also contains logic used by the type inference and
* for comparing types against each other, as well as the concepts
* of narrower to wider types. The widest type is an object. Ideally we
* would like as narrow types as possible for code to be efficient, e.g
* INTs rather than OBJECTs
*/
public abstract class Type implements Comparable<Type>, BytecodeOps {
/** Human readable name for type */
private final String name;
/** Descriptor for type */
private final String descriptor;
/** The "weight" of the type. Used for picking widest/least specific common type */
private final int weight;
/** How many bytecode slots does this type occupy */
private final int slots;
/** The class for this type */
private final Class<?> clazz;
/** Weights are used to decide which types are "wider" than other types */
protected static final int MIN_WEIGHT = -1;
/** Set way below Integer.MAX_VALUE to prevent overflow when adding weights. Objects are still heaviest. */
protected static final int MAX_WEIGHT = 20;
/**
* Constructor
*
* @param clazz class for type
* @param weight weight - higher is more generic
* @param slots how many bytecode slots the type takes up
*/
Type(final String name, final Class<?> clazz, final int weight, final int slots) {
this.name = name;
this.clazz = clazz;
this.descriptor = Type.getDescriptor(clazz);
this.weight = weight;
assert weight >= MIN_WEIGHT && weight <= MAX_WEIGHT : "illegal type weight: " + weight;
this.slots = slots;
}
/**
* Return an internal descriptor for a type
*
* @param type the type
* @return descriptor string
*/
public static String getDescriptor(final Class<?> type) {
return jdk.internal.org.objectweb.asm.Type.getDescriptor(type);
}
/**
* Get the weight of this type - use this e.g. for sorting method descriptors
* @return the weight
*/
public int getWeight() {
return weight;
}
/**
* Get the Class representing this type
* @return the class for this type
*/
public Class<?> getTypeClass() {
return clazz;
}
/**
* For specialization, return the next, slightly more difficulty, type
* to test.
*
* @return the next Type
*/
public Type nextWider() {
return null;
}
/**
* Get the boxed type for this class
* @return the boxed version of this type or null if N/A
*/
public Class<?> getBoxedType() {
assert !getTypeClass().isPrimitive();
return null;
}
/**
* Generate a method descriptor given a return type and a param array
*
* @param returnType return type
* @param types parameters
*
* @return a descriptor string
*/
public static String getMethodDescriptor(final Type returnType, final Type... types) {
final jdk.internal.org.objectweb.asm.Type[] itypes = new jdk.internal.org.objectweb.asm.Type[types.length];
for (int i = 0; i < types.length; i++) {
itypes[i] = types[i].getInternalType();
}
return jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(returnType.getInternalType(), itypes);
}
/**
* Generate a method descriptor given a return type and a param array
*
* @param returnType return type
* @param types parameters
*
* @return a descriptor string
*/
public static String getMethodDescriptor(final Class<?> returnType, final Class<?>... types) {
final jdk.internal.org.objectweb.asm.Type[] itypes = new jdk.internal.org.objectweb.asm.Type[types.length];
for (int i = 0; i < types.length; i++) {
itypes[i] = getInternalType(types[i]);
}
return jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(getInternalType(returnType), itypes);
}
/**
* Return the type for an internal type, package private - do not use
* outside code gen
*
* @param itype internal type
* @return Nashorn type
*/
@SuppressWarnings("fallthrough")
static Type typeFor(final jdk.internal.org.objectweb.asm.Type itype) {
switch (itype.getSort()) {
case jdk.internal.org.objectweb.asm.Type.BOOLEAN:
return BOOLEAN;
case jdk.internal.org.objectweb.asm.Type.INT:
return INT;
case jdk.internal.org.objectweb.asm.Type.LONG:
return LONG;
case jdk.internal.org.objectweb.asm.Type.DOUBLE:
return NUMBER;
case jdk.internal.org.objectweb.asm.Type.OBJECT:
return OBJECT;
case jdk.internal.org.objectweb.asm.Type.VOID:
return null;
case jdk.internal.org.objectweb.asm.Type.ARRAY:
switch (itype.getElementType().getSort()) {
case jdk.internal.org.objectweb.asm.Type.DOUBLE:
return NUMBER_ARRAY;
case jdk.internal.org.objectweb.asm.Type.INT:
return INT_ARRAY;
case jdk.internal.org.objectweb.asm.Type.LONG:
return LONG_ARRAY;
default:
assert false;
case jdk.internal.org.objectweb.asm.Type.OBJECT:
return OBJECT_ARRAY;
}
default:
assert false : "Unknown itype : " + itype + " sort " + itype.getSort();
break;
}
return null;
}
/**
* Get the return type for a method
*
* @param methodDescriptor method descriptor
* @return return type
*/
public static Type getMethodReturnType(final String methodDescriptor) {
return Type.typeFor(jdk.internal.org.objectweb.asm.Type.getReturnType(methodDescriptor));
}
/**
* Get type array representing arguments of a method in order
*
* @param methodDescriptor method descriptor
* @return parameter type array
*/
public static Type[] getMethodArguments(final String methodDescriptor) {
final jdk.internal.org.objectweb.asm.Type itypes[] = jdk.internal.org.objectweb.asm.Type.getArgumentTypes(methodDescriptor);
final Type types[] = new Type[itypes.length];
for (int i = 0; i < itypes.length; i++) {
types[i] = Type.typeFor(itypes[i]);
}
return types;
}
static jdk.internal.org.objectweb.asm.Type getInternalType(final String className) {
return jdk.internal.org.objectweb.asm.Type.getType(className);
}
private jdk.internal.org.objectweb.asm.Type getInternalType() {
return jdk.internal.org.objectweb.asm.Type.getType(getTypeClass());
}
private static jdk.internal.org.objectweb.asm.Type getInternalType(final Class<?> type) {
return jdk.internal.org.objectweb.asm.Type.getType(type);
}
static void invokeStatic(final MethodVisitor method, final Call call) {
method.visitMethodInsn(INVOKESTATIC, call.className(), call.name(), call.descriptor());
}
/**
* Get the internal JVM name of a type
* @return the internal name
*/
public String getInternalName() {
return jdk.internal.org.objectweb.asm.Type.getInternalName(getTypeClass());
}
/**
* Get the internal JVM name of type type represented by a given Java class
* @param clazz the class
* @return the internal name
*/
public static String getInternalName(final Class<?> clazz) {
return jdk.internal.org.objectweb.asm.Type.getInternalName(clazz);
}
/**
* Determines whether a type is the UNKNOWN type, i.e. not set yet
* Used for type inference.
*
* @return true if UNKNOWN, false otherwise
*/
public boolean isUnknown() {
return this.equals(Type.UNKNOWN);
}
/**
* Determines whether a type is the BOOLEAN type
* @return true if BOOLEAN, false otherwise
*/
public boolean isBoolean() {
return this.equals(Type.BOOLEAN);
}
/**
* Determines whether a type is the INT type
* @return true if INTEGER, false otherwise
*/
public boolean isInteger() {
return this.equals(Type.INT);
}
/**
* Determines whether a type is the LONG type
* @return true if LONG, false otherwise
*/
public boolean isLong() {
return this.equals(Type.LONG);
}
/**
* Determines whether a type is the NUMBER type
* @return true if NUMBER, false otherwise
*/
public boolean isNumber() {
return this.equals(Type.NUMBER);
}
/**
* Determines whether a type is numeric, i.e. NUMBER,
* INT, LONG.
*
* @return true if numeric, false otherwise
*/
public boolean isNumeric() {
return this instanceof NumericType;
}
/**
* Determines whether a type is an array type, i.e.
* OBJECT_ARRAY or NUMBER_ARRAY (for now)
*
* @return true if an array type, false otherwise
*/
public boolean isArray() {
return this instanceof ArrayType;
}
/**
* Determines if a type takes up two bytecode slots or not
*
* @return true if type takes up two bytecode slots rather than one
*/
public boolean isCategory2() {
return getSlots() == 2;
}
/**
* Determines whether a type is an OBJECT type, e.g. OBJECT, STRING,
* NUMBER_ARRAY etc.
*
* @return true if object type, false otherwise
*/
public boolean isObject() {
return this instanceof ObjectType;
}
/**
* Determines whether a type is a STRING type
*
* @return true if object type, false otherwise
*/
public boolean isString() {
return this.equals(Type.STRING);
}
/**
* Determine if two types are equivalent, i.e. need no conversion
*
* @param type the second type to check
*
* @return true if types are equivalent, false otherwise
*/
public boolean isEquivalentTo(final Type type) {
return this.weight() == type.weight() || (isObject() && type.isObject());
}
/**
* Determine if a type can be assigned to from another
*
* @param type0 the first type to check
* @param type1 the second type to check
*
* @return true if type1 can be written to type2, false otherwise
*/
public static boolean isAssignableFrom(final Type type0, final Type type1) {
if (type0.isObject() && type1.isObject()) {
return type0.weight() >= type1.weight();
}
return type0.weight() == type1.weight();
}
/**
* Determine if this type is assignable from another type
* @param type the type to check against
*
* @return true if "type" can be written to this type, false otherwise
*/
public boolean isAssignableFrom(final Type type) {
return Type.isAssignableFrom(this, type);
}
/**
* Determines is this type is equivalent to another, i.e. needs no conversion
* to be assigned to it.
*
* @param type0 the first type to check
* @param type1 the second type to check
*
* @return true if this type is equivalent to type, false otherwise
*/
public static boolean areEquivalent(final Type type0, final Type type1) {
return type0.isEquivalentTo(type1);
}
/**
* Determine the number of bytecode slots a type takes up
*
* @return the number of slots for this type, 1 or 2.
*/
public int getSlots() {
return slots;
}
/**
* Returns the widest or most common of two types
*
* @param type0 type one
* @param type1 type two
*
* @return the widest type
*/
public static Type widest(final Type type0, final Type type1) {
if (type0.isArray() && type1.isArray()) {
return ((ArrayType)type0).getElementType() == ((ArrayType)type1).getElementType() ? type0 : Type.OBJECT;
} else if (type0.isArray() != type1.isArray()) {
return Type.OBJECT; //array and non array is always object, widest(Object[], int) NEVER returns Object[], which has most weight. that does not make sense
}
return type0.weight() > type1.weight() ? type0 : type1;
}
/**
* Returns the narrowest or least common of two types
*
* @param type0 type one
* @param type1 type two
*
* @return the widest type
*/
public static Type narrowest(final Type type0, final Type type1) {
return type0.weight() < type1.weight() ? type0 : type1;
}
/**
* Returns the widest or most common of two types, but no wider than "limit"
*
* @param type0 type one
* @param type1 type two
* @param limit limiting type
*
* @return the widest type, but no wider than limit
*/
public static Type widest(final Type type0, final Type type1, final Type limit) {
final Type type = Type.widest(type0, type1);
if (type.weight() > limit.weight()) {
return limit;
}
return type;
}
/**
* Returns the widest or most common of two types, but no narrower than "limit"
*
* @param type0 type one
* @param type1 type two
* @param limit limiting type
*
* @return the widest type, but no wider than limit
*/
public static Type narrowest(final Type type0, final Type type1, final Type limit) {
final Type type = type0.weight() < type1.weight() ? type0 : type1;
if (type.weight() < limit.weight()) {
return limit;
}
return type;
}
/**
* Returns the narrowest of this type and another
*
* @param other type to compare against
*
* @return the widest type
*/
public Type narrowest(final Type other) {
return Type.narrowest(this, other);
}
/**
* Returns the widest of this type and another
*
* @param other type to compare against
*
* @return the widest type
*/
public Type widest(final Type other) {
return Type.widest(this, other);
}
/**
* Returns the weight of a type, used for type comparison
* between wider and narrower types
*
* @return the weight
*/
int weight() {
return weight;
}
/**
* Return the descriptor of a type, used for e.g. signature
* generation
*
* @return the descriptor
*/
public String getDescriptor() {
return descriptor;
}
@Override
public String toString() {
return name;
}
/**
* Return the (possibly cached) Type object for this class
*
* @param clazz the class to check
*
* @return the Type representing this class
*/
public static Type typeFor(final Class<?> clazz) {
Type type = cache.get(clazz);
if (type == null) {
assert !clazz.isPrimitive() || clazz == void.class;
if (clazz.isArray()) {
type = new ArrayType(clazz);
} else {
type = new ObjectType(clazz);
}
cache.put(clazz, type);
}
return type;
}
@Override
public int compareTo(final Type o) {
return o.weight() - weight();
}
/**
* Common logic for implementing dup for all types
*
* @param method method visitor
* @param depth dup depth
*
* @return the type at the top of the stack afterwards
*/
@Override
public Type dup(final MethodVisitor method, final int depth) {
return Type.dup(method, this, depth);
}
/**
* Common logic for implementing swap for all types
*
* @param method method visitor
* @param other the type to swap with
*
* @return the type at the top of the stack afterwards, i.e. other
*/
@Override
public Type swap(final MethodVisitor method, final Type other) {
Type.swap(method, this, other);
return other;
}
/**
* Common logic for implementing pop for all types
*
* @param method method visitor
*
* @return the type that was popped
*/
@Override
public Type pop(final MethodVisitor method) {
Type.pop(method, this);
return this;
}
/**
* Superclass logic for pop for all types
*
* @param method method emitter
* @param type type to pop
*/
protected static void pop(final MethodVisitor method, final Type type) {
method.visitInsn(type.isCategory2() ? POP2 : POP);
}
private static Type dup(final MethodVisitor method, final Type type, final int depth) {
final boolean cat2 = type.isCategory2();
switch (depth) {
case 0:
method.visitInsn(cat2 ? DUP2 : DUP);
break;
case 1:
method.visitInsn(cat2 ? DUP2_X1 : DUP_X1);
break;
case 2:
method.visitInsn(cat2 ? DUP2_X2 : DUP_X2);
break;
default:
return null; //invalid depth
}
return type;
}
private static void swap(final MethodVisitor method, final Type above, final Type below) {
final MethodVisitor mv = method;
if (below.isCategory2()) {
if (above.isCategory2()) {
mv.visitInsn(DUP2_X2);
mv.visitInsn(POP2);
} else {
mv.visitInsn(DUP_X2);
mv.visitInsn(POP);
}
} else {
if (above.isCategory2()) {
mv.visitInsn(DUP2_X1);
mv.visitInsn(POP2);
} else {
mv.visitInsn(SWAP);
}
}
}
/**
* This is the boolean singleton, used for all boolean types
*/
public static final Type BOOLEAN = new BooleanType();
/**
* This is an integer type, i.e INT, INT32.
*/
public static final Type INT = new IntType();
/**
* This is the number singleton, used for all number types
*/
public static final Type NUMBER = new NumberType();
/**
* This is the long singleton, used for all long types
*/
public static final Type LONG = new LongType();
/**
* A string singleton
*/
public static final Type STRING = new ObjectType(String.class);
/**
* This is the object singleton, used for all object types
*/
public static final Type OBJECT = new ObjectType();
/**
* This is the singleton for integer arrays
*/
public static final ArrayType INT_ARRAY = new ArrayType(int[].class) {
@Override
public void astore(final MethodVisitor method) {
method.visitInsn(IASTORE);
}
@Override
public Type aload(final MethodVisitor method) {
method.visitInsn(IALOAD);
return INT;
}
@Override
public Type newarray(final MethodVisitor method) {
method.visitIntInsn(NEWARRAY, T_INT);
return this;
}
@Override
public Type getElementType() {
return INT;
}
};
/**
* This is the singleton for long arrays
*/
public static final ArrayType LONG_ARRAY = new ArrayType(long[].class) {
@Override
public void astore(final MethodVisitor method) {
method.visitInsn(LASTORE);
}
@Override
public Type aload(final MethodVisitor method) {
method.visitInsn(IALOAD);
return INT;
}
@Override
public Type newarray(final MethodVisitor method) {
method.visitIntInsn(NEWARRAY, T_INT);
return this;
}
@Override
public Type getElementType() {
return INT;
}
};
/**
* This is the singleton for numeric arrays
*/
public static final ArrayType NUMBER_ARRAY = new ArrayType(double[].class) {
@Override
public void astore(final MethodVisitor method) {
method.visitInsn(DASTORE);
}
@Override
public Type aload(final MethodVisitor method) {
method.visitInsn(DALOAD);
return NUMBER;
}
@Override
public Type newarray(final MethodVisitor method) {
method.visitIntInsn(NEWARRAY, T_DOUBLE);
return this;
}
@Override
public Type getElementType() {
return NUMBER;
}
};
/** Singleton for method handle arrays used for properties etc. */
public static final ArrayType METHODHANDLE_ARRAY = new ArrayType(new MethodHandle[0].getClass());
/** This is the singleton for string arrays */
public static final ArrayType STRING_ARRAY = new ArrayType(new String[0].getClass());
/** This is the singleton for object arrays */
public static final ArrayType OBJECT_ARRAY = new ArrayType(new Object[0].getClass());
/** This type, always an object type, just a toString override */
public static final Type THIS = new ObjectType() {
@Override
public String toString() {
return "this";
}
};
/** Scope type, always an object type, just a toString override */
public static final Type SCOPE = new ObjectType() {
@Override
public String toString() {
return "scope";
}
};
private static interface Unknown {
// EMPTY - used as a class that is absolutely not compatible with a type to represent "unknown"
}
/**
* This is the unknown type which is used as initial type for type
* inference. It has the minimum type width
*/
public static final Type UNKNOWN = new Type("<unknown>", Unknown.class, MIN_WEIGHT, 1) {
@Override
public String getDescriptor() {
return "<unknown>";
}
@Override
public Type load(final MethodVisitor method, final int slot) {
assert false : "unsupported operation";
return null;
}
@Override
public void store(final MethodVisitor method, final int slot) {
assert false : "unsupported operation";
}
@Override
public Type ldc(final MethodVisitor method, final Object c) {
assert false : "unsupported operation";
return null;
}
@Override
public Type loadUndefined(final MethodVisitor method) {
assert false : "unsupported operation";
return null;
}
@Override
public Type loadEmpty(final MethodVisitor method) {
assert false : "unsupported operation";
return null;
}
@Override
public Type convert(final MethodVisitor method, final Type to) {
assert false : "unsupported operation";
return null;
}
@Override
public void _return(final MethodVisitor method) {
assert false : "unsupported operation";
}
@Override
public Type add(final MethodVisitor method) {
assert false : "unsupported operation";
return null;
}
};
/** Mappings between java classes and their Type singletons */
private static final Map<Class<?>, Type> cache = Collections.synchronizedMap(new HashMap<Class<?>, Type>());
//TODO may need to be cleared, as all types are retained throughout code generation
static {
cache.put(BOOLEAN.getTypeClass(), BOOLEAN);
cache.put(INT.getTypeClass(), INT);
cache.put(LONG.getTypeClass(), LONG);
cache.put(NUMBER.getTypeClass(), NUMBER);
cache.put(STRING.getTypeClass(), STRING);
cache.put(OBJECT.getTypeClass(), OBJECT);
cache.put(OBJECT_ARRAY.getTypeClass(), OBJECT_ARRAY);
}
}

View File

@ -0,0 +1,166 @@
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.ir;
import static jdk.nashorn.internal.codegen.objects.ObjectClassGenerator.DEBUG_FIELDS;
import jdk.nashorn.internal.codegen.objects.ObjectClassGenerator;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.runtime.Source;
/**
* IR representation of a property access (period operator.)
*
*/
public class AccessNode extends BaseNode implements TypeOverride {
/** Property ident. */
private IdentNode property;
/** Does this node have a type override */
private boolean hasCallSiteType;
/**
* Constructor
*
* @param source source code
* @param token token
* @param finish finish
* @param base base node
* @param property property
*/
public AccessNode(final Source source, final long token, final int finish, final Node base, final IdentNode property) {
super(source, token, finish, base);
this.start = base.getStart();
this.property = property;
this.property.setIsPropertyName();
}
/**
* Copy constructor
*
* @param accessNode source node
*/
public AccessNode(final AccessNode accessNode) {
this(accessNode, new CopyState());
}
/**
* Internal copy constructor
*
* @param accessNode source node
* @param cs copy state
*/
protected AccessNode(final AccessNode accessNode, final CopyState cs) {
super(accessNode, cs);
this.property = (IdentNode)cs.existingOrCopy(accessNode.getProperty());
}
@Override
protected Node copy(final CopyState cs) {
return new AccessNode(this, cs);
}
@Override
public boolean equals(final Object other) {
if (!super.equals(other)) {
return false;
}
final AccessNode accessNode = (AccessNode)other;
return property.equals(accessNode.getProperty());
}
@Override
public int hashCode() {
return super.hashCode() ^ property.hashCode();
}
/**
* Assist in IR navigation.
* @param visitor IR navigating visitor.
*/
@Override
public Node accept(final NodeVisitor visitor) {
if (visitor.enter(this) != null) {
base = base.accept(visitor);
property = (IdentNode)property.accept(visitor);
return visitor.leave(this);
}
return this;
}
@Override
public void toString(final StringBuilder sb) {
final boolean needsParen = tokenType().needsParens(getBase().tokenType(), true);
if (hasCallSiteType) {
sb.append('{');
final String desc = getType().getDescriptor();
sb.append(desc.charAt(desc.length() - 1) == ';' ? "O" : getType().getDescriptor());
sb.append('}');
}
if (needsParen) {
sb.append('(');
}
base.toString(sb);
if (needsParen) {
sb.append(')');
}
sb.append('.');
sb.append(property.getName());
}
/**
* Get the property
*
* @return the property IdentNode
*/
public IdentNode getProperty() {
return property;
}
@Override
public void setType(final Type type) {
if (DEBUG_FIELDS && !Type.areEquivalent(getSymbol().getSymbolType(), type)) {
ObjectClassGenerator.LOG.info(getClass().getName() + " " + this + " => " + type + " instead of " + getType());
}
property.setType(type);
getSymbol().setTypeOverride(type); //always a temp so this is fine.
hasCallSiteType = true;
}
@Override
public boolean canHaveCallSiteType() {
return true; //carried by the symbol and always the same nodetype==symboltype
}
}

Some files were not shown because too many files have changed in this diff Show More