Merge
This commit is contained in:
commit
250d670627
@ -13,6 +13,8 @@ webrev.zip
|
||||
*.clazz
|
||||
*.log
|
||||
*.orig
|
||||
*.rej
|
||||
*~
|
||||
genfiles.properties
|
||||
hotspot.log
|
||||
.DS_Store*
|
||||
|
@ -1,266 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2010, 2013, 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"
|
@ -22,9 +22,16 @@
|
||||
# questions.
|
||||
#
|
||||
|
||||
#convert tabs to spaces
|
||||
find . -name "*.java" -exec sed -i "" 's/ / /g' {} \;
|
||||
|
||||
#remove trailing whitespace
|
||||
find . -name "*.java" -exec sed -i "" 's/[ ]*$//' \{} \;
|
||||
fix() {
|
||||
#convert tabs to spaces
|
||||
find . -name $1 -exec sed -i "" 's/ / /g' {} \;
|
||||
#remove trailing whitespace
|
||||
find . -name $1 -exec sed -i "" 's/[ ]*$//' \{} \;
|
||||
}
|
||||
|
||||
if [ ! -z $1 ]; then
|
||||
fix $1;
|
||||
else
|
||||
fix "*.java"
|
||||
fix "*.js"
|
||||
fi
|
||||
|
51
nashorn/bin/run_octane.sh
Normal file
51
nashorn/bin/run_octane.sh
Normal file
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License version 2 only, as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
# version 2 for more details (a copy is included in the LICENSE file that
|
||||
# accompanied this code).
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License version
|
||||
# 2 along with this work; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
|
||||
LOG="./octane_$(date|sed "s/ /_/g"|sed "s/:/_/g").log"
|
||||
|
||||
run_one() {
|
||||
sh ../bin/runopt.sh -scripting ../test/script/basic/run-octane.js -- $1 --verbose --iterations 25 | tee -a $LOG
|
||||
}
|
||||
|
||||
if [ -z $1 ]; then
|
||||
|
||||
run_one "box2d"
|
||||
run_one "code-load"
|
||||
run_one "crypto"
|
||||
run_one "deltablue"
|
||||
run_one "earley-boyer"
|
||||
run_one "gbemu"
|
||||
run_one "mandreel"
|
||||
run_one "navier-stokes"
|
||||
run_one "pdfjs"
|
||||
run_one "raytrace"
|
||||
run_one "regexp"
|
||||
run_one "richards"
|
||||
run_one "splay"
|
||||
run_one "typescript"
|
||||
run_one "zlib"
|
||||
|
||||
else
|
||||
run_one $1
|
||||
fi
|
25
nashorn/bin/rundiff.sh
Normal file
25
nashorn/bin/rundiff.sh
Normal file
@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
|
||||
# do two runs of a script, one optimistic and one pessimistic, expect identical outputs
|
||||
# if not, display and error message and a diff
|
||||
|
||||
which opendiff >/dev/null
|
||||
RES=$?
|
||||
if [ $RES = 0 ]; then
|
||||
DIFFTOOL=opendiff
|
||||
else
|
||||
DIFFTOOL=diff
|
||||
fi
|
||||
|
||||
OPTIMISTIC=out_optimistic
|
||||
PESSIMISTIC=out_pessimistic
|
||||
$JAVA_HOME/bin/java -ea -jar ../dist/nashorn.jar ${@} >$PESSIMISTIC
|
||||
$JAVA_HOME/bin/java -ea -Dnashorn.optimistic -jar ../dist/nashorn.jar ${@} >$OPTIMISTIC
|
||||
|
||||
if ! diff -q $PESSIMISTIC $OPTIMISTIC >/dev/null ; then
|
||||
echo "Failure! Results are different"
|
||||
echo ""
|
||||
$DIFFTOOL $PESSIMISTIC $OPTIMISTIC
|
||||
else
|
||||
echo "OK - Results are identical"
|
||||
fi
|
110
nashorn/bin/runopt.sh
Normal file
110
nashorn/bin/runopt.sh
Normal file
@ -0,0 +1,110 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License version 2 only, as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
# version 2 for more details (a copy is included in the LICENSE file that
|
||||
# accompanied this code).
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License version
|
||||
# 2 along with this work; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
|
||||
###########################################################################################
|
||||
# This is a helper script to evaluate nashorn with optimistic types
|
||||
# it produces a flight recording for every run, and uses the best
|
||||
# known flags for performance for the current configration
|
||||
###########################################################################################
|
||||
|
||||
# Flags to instrument lambdaform computation, caching, interpretation and compilation
|
||||
# Default compile threshold for lambdaforms is 30
|
||||
#FLAGS="-Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true"
|
||||
|
||||
|
||||
# Flags to run trusted tests from the Nashorn test suite
|
||||
#FLAGS="-Djava.security.manager -Djava.security.policy=../build/nashorn.policy -Dnashorn.debug"
|
||||
|
||||
|
||||
# Unique timestamped file name for JFR recordings. For JFR, we also have to
|
||||
# crank up the stack cutoff depth to 1024, because of ridiculously long lambda form
|
||||
# stack traces.
|
||||
#
|
||||
# It is also recommended that you go into $JAVA_HOME/jre/lib/jfr/default.jfc and
|
||||
# set the "method-sampling-interval" Normal and Maximum sample time as low as you
|
||||
# can go (10 ms on most platforms). The default is normally higher. The increased
|
||||
# sampling overhead is usually negligible for Nashorn runs, but the data is better
|
||||
|
||||
JFR_FILENAME="./nashorn_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||
|
||||
|
||||
# Directory where to look for nashorn.jar in a dist folder. The default is "..", assuming
|
||||
# that we run the script from the make dir
|
||||
DIR=..
|
||||
NASHORN_JAR=$DIR/dist/nashorn.jar
|
||||
|
||||
|
||||
# The built Nashorn jar is placed first in the bootclasspath to override the JDK
|
||||
# nashorn.jar in $JAVA_HOME/jre/lib/ext. Thus, we also need -esa, as assertions in
|
||||
# nashorn count as system assertions in this configuration
|
||||
|
||||
# Type profiling default level is 111, 222 adds some compile time, but is faster
|
||||
|
||||
$JAVA_HOME/bin/java \
|
||||
$FLAGS \
|
||||
-ea \
|
||||
-esa \
|
||||
-Xbootclasspath/p:$NASHORN_JAR \
|
||||
-Xms2G -Xmx2G \
|
||||
-XX:TypeProfileLevel=222 \
|
||||
-cp $CLASSPATH:../build/test/classes/ \
|
||||
jdk.nashorn.tools.Shell ${@}
|
||||
|
||||
# Below are flags that may come in handy, but aren't used for default runs
|
||||
|
||||
# Testing out new code optimizations using the generic hotspot "new code" parameter
|
||||
#-XX:+UnlockDiagnosticVMOptions \
|
||||
#-XX:+UseNewCode \
|
||||
|
||||
# Flight recorder
|
||||
#-XX:+UnlockCommercialFeatures \
|
||||
#-XX:+FlightRecorder \
|
||||
#-XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$JFR_FILENAME,stackdepth=1024 \
|
||||
|
||||
|
||||
# Type specialization and math intrinsic replacement should be enabled by default in 8u20 and nine,
|
||||
# keeping this flag around for experimental reasons. Replace + with - to switch it off
|
||||
#-XX:+UseTypeSpeculation \
|
||||
|
||||
|
||||
# Same with math intrinsics. They should be enabled by default in 8u20 and 9
|
||||
#-XX:+UseMathExactIntrinsics \
|
||||
|
||||
|
||||
# Add -Dnashorn.time to time the compilation phases.
|
||||
#-Dnashorn.time \
|
||||
|
||||
|
||||
# Add ShowHiddenFrames to get lambda form internals on the stack traces
|
||||
#-XX:+ShowHiddenFrames \
|
||||
|
||||
|
||||
# Add print optoassembly to get an asm dump. This requires 1) a debug build, not product,
|
||||
# That tired compilation is switched off, for C2 only output and that the number of
|
||||
# compiler threads is set to 1 for determinsm.
|
||||
#-XX:+PrintOptoAssembly -XX:-TieredCompilation -XX:CICompilerCount=1 \
|
||||
|
||||
# Tier compile threasholds. Default value is 10. (1-100 is useful for experiments)
|
||||
# -XX:IncreaseFirstTierCompileThresholdAt=XX
|
||||
|
28
nashorn/bin/runopt_noassert.sh
Normal file
28
nashorn/bin/runopt_noassert.sh
Normal file
@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
|
||||
#FLAGS="-Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true"
|
||||
|
||||
FILENAME="./optimistic_noassert_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||
|
||||
DIR=..
|
||||
NASHORN_JAR=$DIR/dist/nashorn.jar
|
||||
|
||||
$JAVA_HOME/bin/java \
|
||||
$FLAGS \
|
||||
-Xbootclasspath/p:$NASHORN_JAR \
|
||||
-Xms2G -Xmx2G \
|
||||
-XX:+UnlockCommercialFeatures \
|
||||
-XX:+FlightRecorder \
|
||||
-XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=1024 \
|
||||
-XX:TypeProfileLevel=222 \
|
||||
-XX:+UnlockExperimentalVMOptions \
|
||||
-XX:+UseTypeSpeculation \
|
||||
-XX:+UseMathExactIntrinsics \
|
||||
-XX:+UnlockDiagnosticVMOptions \
|
||||
-cp $CLASSPATH:../build/test/classes/ \
|
||||
jdk.nashorn.tools.Shell ${@}
|
||||
|
||||
#-XX:+ShowHiddenFrames \
|
||||
#-XX:+PrintOptoAssembly \
|
||||
#-XX:-TieredCompilation \
|
||||
#-XX:CICompilerCount=1 \
|
27
nashorn/bin/runopt_nojfr.sh
Normal file
27
nashorn/bin/runopt_nojfr.sh
Normal file
@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
#FLAGS="-Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true"
|
||||
|
||||
DIR=..
|
||||
NASHORN_JAR=$DIR/dist/nashorn.jar
|
||||
|
||||
$JAVA_HOME/bin/java \
|
||||
$FLAGS \
|
||||
-ea \
|
||||
-esa \
|
||||
-Xbootclasspath/p:$NASHORN_JAR \
|
||||
-Xms2G -Xmx2G \
|
||||
-XX:+UnlockCommercialFeatures \
|
||||
-XX:TypeProfileLevel=222 \
|
||||
-XX:+UnlockExperimentalVMOptions \
|
||||
-XX:+UseTypeSpeculation \
|
||||
-XX:+UseMathExactIntrinsics \
|
||||
-XX:+UnlockDiagnosticVMOptions \
|
||||
-XX:+UseNewCode \
|
||||
-cp $CLASSPATH:../build/test/classes/ \
|
||||
jdk.nashorn.tools.Shell ${@}
|
||||
|
||||
#-XX:+ShowHiddenFrames \
|
||||
#-XX:+PrintOptoAssembly \
|
||||
#-XX:-TieredCompilation \
|
||||
#-XX:CICompilerCount=1 \
|
@ -1,59 +0,0 @@
|
||||
rem
|
||||
rem Copyright (c) 2010, 2013, 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/box2d.js %OCTANE_ARGS%
|
||||
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/code-load.js %OCTANE_ARGS%
|
||||
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/crypto.js %OCTANE_ARGS%
|
||||
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/deltablue.js %OCTANE_ARGS%
|
||||
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/gbemu.js %OCTANE_ARGS%
|
||||
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/navier-stokes.js %OCTANE_ARGS%
|
||||
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/pdfjs.js %OCTANE_ARGS%
|
||||
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/raytrace.js %OCTANE_ARGS%
|
||||
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/regexp.js %OCTANE_ARGS%
|
||||
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/richards.js %OCTANE_ARGS%
|
||||
%CMD% test/script/basic/run-octane.js -- test/script/external/octane/splay.js %OCTANE_ARGS%
|
||||
endlocal
|
||||
goto :EOF
|
@ -1,58 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) 2010, 2013, 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="-Djava.ext.dirs=`dirname $0`/../dist:$JAVA_HOME/jre/lib/ext -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" "pdfjs.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="${JAVA7_HOME}/bin/java ${JVM_FLAGS} test/script/basic/run-octane.js -- test/script/external/octane/${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/${BENCHMARK} ${OCTANE_ARGS}"
|
||||
$CMD
|
||||
done
|
||||
else
|
||||
echo "no JAVA8_HOME set."
|
||||
fi
|
||||
|
||||
echo "Done"
|
@ -31,29 +31,29 @@ 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.ACCESSORPROPERTY_CREATE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_CREATE_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_TYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.ARRAYLIST_INIT_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.ARRAYLIST_TYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.CLINIT;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_EMPTY_LIST;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_TYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_ADD;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_ADD_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_TYPE;
|
||||
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.ACCESSORPROPERTY_CREATE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_CREATE_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_TYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.LIST_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.ARRAYLIST_TYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.ARRAYLIST_INIT_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_TYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_ADD;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_ADD_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_TYPE;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_EMPTY_LIST;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_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;
|
||||
|
@ -32,9 +32,9 @@ 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.OBJECT_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
|
||||
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;
|
||||
|
@ -319,7 +319,7 @@ public final class MemberInfo implements Cloneable {
|
||||
break;
|
||||
case FUNCTION: {
|
||||
final Type returnType = Type.getReturnType(javaDesc);
|
||||
if (!isValidJSType(returnType)) {
|
||||
if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) {
|
||||
error("return value of a @Function method should be a valid JS type, found " + returnType);
|
||||
}
|
||||
final Type[] argTypes = Type.getArgumentTypes(javaDesc);
|
||||
@ -351,7 +351,7 @@ public final class MemberInfo implements Cloneable {
|
||||
break;
|
||||
case SPECIALIZED_FUNCTION: {
|
||||
final Type returnType = Type.getReturnType(javaDesc);
|
||||
if (!isValidJSType(returnType)) {
|
||||
if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) {
|
||||
error("return value of a @SpecializedFunction method should be a valid JS type, found " + returnType);
|
||||
}
|
||||
final Type[] argTypes = Type.getArgumentTypes(javaDesc);
|
||||
@ -371,9 +371,8 @@ public final class MemberInfo implements Cloneable {
|
||||
error("first argument of a @Getter method should be of Object type, found: " + argTypes[0]);
|
||||
}
|
||||
|
||||
final Type returnType = Type.getReturnType(javaDesc);
|
||||
if (!isJavaLangObject(returnType)) {
|
||||
error("return type of a @Getter method should be Object, found: " + javaDesc);
|
||||
if (Type.getReturnType(javaDesc).equals(Type.VOID_TYPE)) {
|
||||
error("return type of getter should not be void");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -413,7 +413,8 @@ public class MethodGenerator extends MethodVisitor {
|
||||
super.visitMethodInsn(INVOKEVIRTUAL,
|
||||
"java/io/PrintStream",
|
||||
"println",
|
||||
"(Ljava/lang/String;)V", false);
|
||||
"(Ljava/lang/String;)V",
|
||||
false);
|
||||
}
|
||||
|
||||
// print the object on the top of the stack
|
||||
@ -426,6 +427,7 @@ public class MethodGenerator extends MethodVisitor {
|
||||
super.visitMethodInsn(INVOKEVIRTUAL,
|
||||
"java/io/PrintStream",
|
||||
"println",
|
||||
"(Ljava/lang/Object;)V", false);
|
||||
"(Ljava/lang/Object;)V",
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
@ -31,9 +31,9 @@ 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.OBJECT_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC;
|
||||
import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME;
|
||||
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;
|
||||
|
@ -26,9 +26,8 @@
|
||||
package jdk.nashorn.internal.tools.nasgen;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
|
@ -737,26 +737,6 @@ an implementation based on Joni, the regular expression engine used by
|
||||
the JRuby project. The default value for this flag is "joni"
|
||||
|
||||
|
||||
SYSTEM PROPERTY: -Dnashorn.time
|
||||
|
||||
This enables timers for various phases of script compilation. The timers
|
||||
will be dumped when the Nashorn process exits. We see a percentage value
|
||||
of how much time was spent not executing bytecode (i.e. compilation and
|
||||
internal tasks) at the end of the report.
|
||||
|
||||
Here is an example:
|
||||
|
||||
[JavaScript Parsing] 61 ms
|
||||
[Constant Folding] 11 ms
|
||||
[Control Flow Lowering] 26 ms
|
||||
[Type Attribution] 81 ms
|
||||
[Range Analysis] 0 ms
|
||||
[Code Splitting] 29 ms
|
||||
[Type Finalization] 19 ms
|
||||
[Bytecode Generation] 189 ms
|
||||
[Code Installation] 7 ms
|
||||
Total runtime: 508 ms (Non-runtime: 423 ms [83%])
|
||||
|
||||
===============
|
||||
2. The loggers.
|
||||
===============
|
||||
@ -887,6 +867,34 @@ etc. It will also show the internal representation of respective field
|
||||
(Object in the normal case, unless running with the dual field
|
||||
representation)
|
||||
|
||||
* time
|
||||
|
||||
This enables timers for various phases of script compilation. The timers
|
||||
will be dumped when the Nashorn process exits. We see a percentage value
|
||||
of how much time was spent not executing bytecode (i.e. compilation and
|
||||
internal tasks) at the end of the report.
|
||||
|
||||
A finer level than "info" will show individual compilation timings as they
|
||||
happen.
|
||||
|
||||
Here is an example:
|
||||
|
||||
[time] Accumulated complation phase Timings:
|
||||
[time]
|
||||
[time] 'JavaScript Parsing' 1076 ms
|
||||
[time] 'Constant Folding' 159 ms
|
||||
[time] 'Control Flow Lowering' 303 ms
|
||||
[time] 'Program Point Calculation' 282 ms
|
||||
[time] 'Builtin Replacement' 71 ms
|
||||
[time] 'Code Splitting' 670 ms
|
||||
[time] 'Symbol Assignment' 474 ms
|
||||
[time] 'Scope Depth Computation' 249 ms
|
||||
[time] 'Optimistic Type Assignment' 186 ms
|
||||
[time] 'Local Variable Type Calculation' 526 ms
|
||||
[time] 'Bytecode Generation' 5177 ms
|
||||
[time] 'Class Installation' 1854 ms
|
||||
[time]
|
||||
[time] Total runtime: 11994 ms (Non-runtime: 11027 ms [91%])
|
||||
|
||||
=======================
|
||||
3. Undocumented options
|
||||
@ -914,11 +922,10 @@ A short summary follows:
|
||||
|
||||
-cp, -classpath (-cp path. Specify where to find user class files.)
|
||||
|
||||
-co, --compile-only (Compile script without running. Exit after compilation)
|
||||
-co, --compile-only (Compile without running.)
|
||||
param: [true|false] default: false
|
||||
|
||||
-d, --dump-debug-dir (specify a destination directory to dump class files.
|
||||
This must be combined with the --compile-only option to work)
|
||||
-d, --dump-debug-dir (specify a destination directory to dump class files.)
|
||||
param: <path>
|
||||
|
||||
--debug-lines (Generate line number table in .class files.)
|
||||
@ -954,10 +961,6 @@ A short summary follows:
|
||||
-h, -help (Print help for command line flags.)
|
||||
param: [true|false] default: false
|
||||
|
||||
--lazy-compilation (EXPERIMENTAL: Use lazy code generation strategies - do not compile
|
||||
the entire script at once.)
|
||||
param: [true|false] default: false
|
||||
|
||||
--loader-per-compile (Create a new class loader per compile.)
|
||||
param: [true|false] default: true
|
||||
|
||||
@ -965,16 +968,16 @@ A short summary follows:
|
||||
param: <locale> default: en-US
|
||||
|
||||
--log (Enable logging of a given level for a given number of sub systems.
|
||||
[for example: --log=fields:finest,codegen:info])
|
||||
[for example: --log=fields:finest,codegen:info].)
|
||||
param: <module:level>,*
|
||||
|
||||
-nj, --no-java (No Java support)
|
||||
-nj, --no-java (Disable Java support.)
|
||||
param: [true|false] default: false
|
||||
|
||||
-nse, --no-syntax-extensions (No non-standard syntax extensions)
|
||||
-nse, --no-syntax-extensions (Disallow non-standard syntax extensions.)
|
||||
param: [true|false] default: false
|
||||
|
||||
-nta, --no-typed-arrays (No Typed arrays support)
|
||||
-nta, --no-typed-arrays (Disable typed arrays support.)
|
||||
param: [true|false] default: false
|
||||
|
||||
--parse-only (Parse without compiling.)
|
||||
@ -983,13 +986,15 @@ A short summary follows:
|
||||
--print-ast (Print abstract syntax tree.)
|
||||
param: [true|false] default: false
|
||||
|
||||
--print-code (Print bytecode.)
|
||||
param: [true|false] default: false
|
||||
-pc, --print-code (Print generated bytecode. If a directory is specified, nothing will
|
||||
be dumped to stderr. Also, in that case, .dot files will be generated
|
||||
for all functions or for the function with the specified name only.)
|
||||
param: [dir:<output-dir>,function:<name>]
|
||||
|
||||
--print-lower-ast (Print lowered abstract syntax tree.)
|
||||
param: [true|false] default: false
|
||||
|
||||
--print-lower-parse (Print the parse tree after lowering.)
|
||||
-plp, --print-lower-parse (Print the parse tree after lowering.)
|
||||
param: [true|false] default: false
|
||||
|
||||
--print-mem-usage (Print memory usage of IR after each compile stage.)
|
||||
@ -998,7 +1003,7 @@ A short summary follows:
|
||||
--print-no-newline (Print function will not print new line char.)
|
||||
param: [true|false] default: false
|
||||
|
||||
--print-parse (Print the parse tree.)
|
||||
-pp, --print-parse (Print the parse tree.)
|
||||
param: [true|false] default: false
|
||||
|
||||
--print-symbols (Print the symbol table.)
|
||||
@ -1007,21 +1012,13 @@ A short summary follows:
|
||||
-pcs, --profile-callsites (Dump callsite profile data.)
|
||||
param: [true|false] default: false
|
||||
|
||||
--range-analysis (EXPERIMENTAL: Do range analysis using known compile time types,
|
||||
and try to narrow number types)
|
||||
param: [true|false] default: false
|
||||
|
||||
-scripting (Enable scripting features.)
|
||||
param: [true|false] default: false
|
||||
|
||||
--specialize-calls (EXPERIMENTAL: Specialize all or a set of method according
|
||||
to callsite parameter types)
|
||||
param: [=function_1,...,function_n]
|
||||
|
||||
--stderr (Redirect stderr to a filename or to another tty, e.g. stdout)
|
||||
--stderr (Redirect stderr to a filename or to another tty, e.g. stdout.)
|
||||
param: <output console>
|
||||
|
||||
--stdout (Redirect stdout to a filename or to another tty, e.g. stderr)
|
||||
--stdout (Redirect stdout to a filename or to another tty, e.g. stderr.)
|
||||
param: <output console>
|
||||
|
||||
-strict (Run scripts in strict mode.)
|
||||
@ -1031,7 +1028,7 @@ A short summary follows:
|
||||
param: <timezone> default: Europe/Stockholm
|
||||
|
||||
-tcs, --trace-callsites (Enable callsite trace mode. Options are: miss [trace callsite misses]
|
||||
enterexit [trace callsite enter/exit], objects [print object properties])
|
||||
enterexit [trace callsite enter/exit], objects [print object properties].)
|
||||
param: [=[option,]*]
|
||||
|
||||
--verify-code (Verify byte code before running.)
|
||||
|
@ -1,384 +1,333 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (c) 2010, 2013, 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">
|
||||
<property name="octane-tests" value="box2d code-load crypto deltablue earley-boyer gbemu navier-stokes pdfjs raytrace regexp richards splay"/>
|
||||
</target>
|
||||
|
||||
<!-- ignore benchmarks where rhino crashes -->
|
||||
<target name="octane-init-rhino" depends="jar">
|
||||
<property name="octane-tests" value="box2d code-load crypto deltablue earley-boyer gbemu navier-stokes raytrace regexp richards splay"/>
|
||||
</target>
|
||||
<!--
|
||||
Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
|
||||
This code is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License version 2 only, as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
version 2 for more details (a copy is included in the LICENSE file that
|
||||
accompanied this code).
|
||||
|
||||
You should have received a copy of the GNU General Public License version
|
||||
2 along with this work; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
or visit www.oracle.com if you need additional information or have any
|
||||
questions.
|
||||
-->
|
||||
|
||||
|
||||
<project
|
||||
name="nashorn-benchmarks"
|
||||
default="all"
|
||||
basedir=".."
|
||||
xmlns:if="ant:if">
|
||||
|
||||
<!--
|
||||
Below are the octane benchmarks that should be run.
|
||||
The ones that are excluded, as Nashorn currently has
|
||||
some issues with them (functionality or performance)
|
||||
are commented out
|
||||
-->
|
||||
|
||||
<!-- box2d -->
|
||||
<target name="octane-box2d" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="box2d"/>
|
||||
</antcall>
|
||||
<target name="octane-box2d" depends="octane-box2d-nashorn"/>
|
||||
<target name="octane-box2d-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.box2d" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-box2d-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="box2d"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.box2d" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-box2d-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="box2d"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.box2d" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- code-load -->
|
||||
<target name="octane-code-load" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="code-load"/>
|
||||
</antcall>
|
||||
<!-- code-load -->
|
||||
<target name="octane-code-load" depends="octane-code-load-nashorn"/>
|
||||
<target name="octane-code-load-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.code-load" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-code-load-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="code-load"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.code-load" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-code-load-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="code-load"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.code-load" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- crypto -->
|
||||
<target name="octane-crypto" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="crypto"/>
|
||||
</antcall>
|
||||
<target name="octane-crypto" depends="octane-crypto-nashorn"/>
|
||||
<target name="octane-crypto-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.crypto" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-crypto-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="crypto"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.crypto" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-crypto-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="crypto"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.crypto" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- deltablue -->
|
||||
<target name="octane-deltablue" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="deltablue"/>
|
||||
</antcall>
|
||||
<target name="octane-deltablue" depends="octane-deltablue-nashorn"/>
|
||||
<target name="octane-deltablue-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.deltablue" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-deltablue-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="deltablue"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.deltablue" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-deltablue-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="deltablue"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.deltablue" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- earley-boyer -->
|
||||
<target name="octane-earley-boyer" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="earley-boyer"/>
|
||||
</antcall>
|
||||
<target name="octane-earley-boyer" depends="octane-earley-boyer-nashorn"/>
|
||||
<target name="octane-earley-boyer-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.earley-boyer" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-earley-boyer-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="earley-boyer"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.earley-boyer" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-earley-boyer-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="earley-boyer"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.earley-boyer" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- gbemu -->
|
||||
<target name="octane-gbemu" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="gbemu"/>
|
||||
</antcall>
|
||||
|
||||
<!-- gbemu -->
|
||||
<target name="octane-gbemu" depends="octane-gbemu-nashorn"/>
|
||||
<target name="octane-gbemu-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.gbemu" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-gbemu-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="gbemu"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.gbemu" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-gbemu-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="gbemu"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.gbemu" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- mandreel -->
|
||||
<target name="octane-mandreel" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="mandreel"/>
|
||||
</antcall>
|
||||
<!-- mandreel -->
|
||||
<target name="octane-mandreel" depends="octane-mandreel-nashorn"/>
|
||||
<target name="octane-mandreel-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.mandreel" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-mandreel-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="mandreel"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.mandreel" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-mandreel-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="mandreel"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.mandreel" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- navier-stokes -->
|
||||
<target name="octane-navier-stokes" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="navier-stokes"/>
|
||||
</antcall>
|
||||
<target name="octane-navier-stokes" depends="octane-navier-stokes-nashorn"/>
|
||||
<target name="octane-navier-stokes-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.navier-stokes" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-navier-stokes-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="navier-stokes"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.navier-stokes" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-navier-stokes-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="navier-stokes"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.navier-stokes" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- pdfjs -->
|
||||
<target name="octane-pdfjs" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="pdfjs"/>
|
||||
</antcall>
|
||||
<!-- pdfjs -->
|
||||
<target name="octane-pdfjs" depends="octane-pdfjs-nashorn"/>
|
||||
<target name="octane-pdfjs-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.pdfjs" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-pdfjs-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="pdfjs"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.pdfjs" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-pdfjs-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="pdfjs"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.pdfjs" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- raytrace -->
|
||||
<target name="octane-raytrace" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="raytrace"/>
|
||||
</antcall>
|
||||
<target name="octane-raytrace" depends="octane-raytrace-nashorn"/>
|
||||
<target name="octane-raytrace-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.raytrace" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-raytrace-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="raytrace"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.raytrace" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-raytrace-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="raytrace"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.raytrace" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- regexp -->
|
||||
<target name="octane-regexp" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="regexp"/>
|
||||
</antcall>
|
||||
<target name="octane-regexp" depends="octane-regexp-nashorn"/>
|
||||
<target name="octane-regexp-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.regexp" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-regexp-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="regexp"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.regexp" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-regexp-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="regexp"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.regexp" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- richards -->
|
||||
<target name="octane-richards" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="richards"/>
|
||||
</antcall>
|
||||
<target name="octane-richards" depends="octane-richards-nashorn"/>
|
||||
<target name="octane-richards-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.richards" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-richards-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="richards"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.richards" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-richards-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="richards"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.richards" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- splay -->
|
||||
<target name="octane-splay" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="splay"/>
|
||||
</antcall>
|
||||
<target name="octane-splay" depends="octane-splay-nashorn"/>
|
||||
<target name="octane-splay-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.splay" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-splay-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-tests" value="splay"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.splay" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-splay-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="splay"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.splay" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
<!-- splay -->
|
||||
<target name="octane-typescript" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="typescript"/>
|
||||
</antcall>
|
||||
<!-- typescript -->
|
||||
<target name="octane-typescript" depends="octane-typescript-nashorn"/>
|
||||
<target name="octane-typescript-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.typescript" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-typescript-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-typescript" value="typescript"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.typescript" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-typescript-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="typescript"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.typescript" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
<!-- zlib -->
|
||||
<target name="octane-zlib" depends="jar">
|
||||
<antcall target="run-octane">
|
||||
<param name="octane-tests" value="zlib"/>
|
||||
</antcall>
|
||||
<target name="octane-zlib" depends="octane-zlib-nashorn"/>
|
||||
<target name="octane-zlib-nashorn" depends="jar">
|
||||
<run-one cond="octane.benchmark.zlib" runtime="nashorn"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-zlib-v8" depends="jar">
|
||||
<antcall target="run-octane-v8">
|
||||
<param name="octane-typescript" value="zlib"/>
|
||||
</antcall>
|
||||
<run-one cond="octane.benchmark.zlib" runtime="v8"/>
|
||||
</target>
|
||||
|
||||
<target name="octane-zlib-rhino" depends="jar">
|
||||
<antcall target="run-octane-rhino">
|
||||
<param name="octane-tests" value="zlib"/>
|
||||
<run-one cond="octane.benchmark.zlib" runtime="rhino"/>
|
||||
</target>
|
||||
|
||||
<!--
|
||||
Benchmark runners for one or more benchmarks, single
|
||||
or multiple process
|
||||
-->
|
||||
|
||||
<target name="octane-process-separate" if="${octane-test-sys-prop.separate.process}">
|
||||
<echo message="Running each benchmark in separate processes, starting new JVMs for each."/>
|
||||
<script language="javascript"><![CDATA[
|
||||
var props = [];
|
||||
|
||||
for (var prop in project.getProperties()) {
|
||||
if (prop.startsWith("octane.benchmark.")) {
|
||||
props.push(prop);
|
||||
}
|
||||
}
|
||||
|
||||
//sort benchmark props in alphabetical order by name
|
||||
props.sort(function(a, b) {
|
||||
if (a < b) {
|
||||
return -1;
|
||||
} else if (a > b) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
var runtime = project.getProperty("runtime");
|
||||
|
||||
for (var i in props) {
|
||||
var task = project.createTask("run-one");
|
||||
// workaround for https://issues.apache.org/bugzilla/show_bug.cgi?id=53831, still not fixed
|
||||
if (task.getOwningTarget() == null) {
|
||||
task.setOwningTarget(self.getOwningTarget());
|
||||
}
|
||||
var prop = props[i];
|
||||
task.setDynamicAttribute("cond", prop);
|
||||
task.setDynamicAttribute("runtime", runtime);
|
||||
task.perform();
|
||||
}
|
||||
]]></script>
|
||||
</target>
|
||||
|
||||
<target name="octane-process-single" unless="${octane-test-sys-prop.separate.process}">
|
||||
<echo message="Running all benchmarks in the same process."/>
|
||||
<pathconvert property="octane.benchmarks" pathsep=" ">
|
||||
<propertyset>
|
||||
<propertyref prefix="octane.benchmark."/>
|
||||
</propertyset>
|
||||
</pathconvert>
|
||||
<antcall target="run-octane${runtime}">
|
||||
<param name="octane-tests" value="${octane.benchmarks}"/>
|
||||
</antcall>
|
||||
</target>
|
||||
|
||||
<!-- run octane benchmarks in a single process -->
|
||||
<target name="octane-single-process" depends="octane-init">
|
||||
<antcall target="run-octane"/>
|
||||
<!--
|
||||
run 'octane' in single or separate processes based on config
|
||||
This uses nashorn as the default runtime
|
||||
-->
|
||||
<target name="octane-nashorn" depends="jar">
|
||||
<property name="runtime" value="nashorn"/>
|
||||
<antcall target="octane-process-separate"/>
|
||||
<antcall target="octane-process-single"/>
|
||||
</target>
|
||||
|
||||
<!-- zlib excluded due to missing implementation of 'read' -->
|
||||
<target name="octane-separate-process" depends=
|
||||
"octane-box2d, octane-code-load, octane-crypto,
|
||||
octane-deltablue, octane-earley-boyer, octane-gbemu,
|
||||
octane-mandreel, octane-navier-stokes, octane-pdfjs,
|
||||
octane-raytrace, octane-regexp, octane-richards,
|
||||
octane-splay, octane-typescript"/>
|
||||
|
||||
<target name="--single-process" unless="${octane-test-sys-prop.separate.process}">
|
||||
<antcall target="octane-single-process"/>
|
||||
</target>
|
||||
<target name="--separate-process" if="${octane-test-sys-prop.separate.process}">
|
||||
<antcall target="octane-separate-process"/>
|
||||
</target>
|
||||
|
||||
<!-- run 'octane' in single or separate processes based on config -->
|
||||
<target name="octane" depends="init, --single-process, --separate-process"/>
|
||||
<!-- alias for 'octane' -->
|
||||
<target name="octane" depends="octane-nashorn"/>
|
||||
|
||||
<!-- run octane benchmarks using octane as runtime -->
|
||||
<target name="octane-v8" depends="octane-init">
|
||||
<antcall target="run-octane-v8"/>
|
||||
<target name="octane-v8" depends="jar">
|
||||
<property name="runtime" value="v8"/>
|
||||
<antcall target="octane-process-separate"/>
|
||||
<antcall target="octane-process-single"/>
|
||||
</target>
|
||||
|
||||
<!-- run octane benchmarks using Rhino as runtime -->
|
||||
<target name="octane-rhino" depends="octane-init-rhino">
|
||||
<antcall target="run-octane-rhino"/>
|
||||
<target name="octane-rhino" depends="jar">
|
||||
<property name="runtime" value="rhino"/>
|
||||
<antcall target="octane-process-separate"/>
|
||||
<antcall target="octane-process-single"/>
|
||||
</target>
|
||||
|
||||
<target name="run-octane">
|
||||
|
||||
<macrodef name="run-one">
|
||||
<attribute name="cond"/>
|
||||
<attribute name="runtime" default=""/>
|
||||
<sequential>
|
||||
<antcall target="run-octane-@{runtime}" if:set="@{cond}">
|
||||
<param name="octane-tests" value="${@{cond}}"/>
|
||||
</antcall>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<target name="run-octane-nashorn">
|
||||
<java classname="${nashorn.shell.tool}"
|
||||
classpath="${run.test.classpath}"
|
||||
fork="true"
|
||||
dir=".">
|
||||
<jvmarg line="${ext.class.path}"/>
|
||||
<jvmarg line="${run.test.jvmargs.octane} -Xms${run.test.xms} -Xmx${run.test.xmx}"/>
|
||||
<!-- pass on all properties prefixed with 'nashorn' to the runtime -->
|
||||
<syspropertyset>
|
||||
<propertyref prefix="nashorn."/>
|
||||
</syspropertyset>
|
||||
<arg value="${octane-test-sys-prop.test.js.framework}"/>
|
||||
<arg value="-scripting"/>
|
||||
<arg value="--"/>
|
||||
<arg value="${octane-tests}"/>
|
||||
<arg value="--runtime"/>
|
||||
<arg value="Nashorn"/>
|
||||
<arg value="nashorn"/>
|
||||
<arg value="--verbose"/>
|
||||
<arg value="--iterations 8"/>
|
||||
<arg value="--iterations ${octane.iterations}"/>
|
||||
</java>
|
||||
</target>
|
||||
|
||||
@ -386,11 +335,11 @@
|
||||
<exec executable="${v8.shell}">
|
||||
<arg value="${octane-test-sys-prop.test.js.framework}"/>
|
||||
<arg value="--"/>
|
||||
<arg value="${octane-tests}"/>
|
||||
<arg value="${octane-tests}"/>
|
||||
<arg value="--runtime"/>
|
||||
<arg value="v8"/>
|
||||
<arg value="--verbose"/>
|
||||
<arg value="--iterations 8"/>
|
||||
<arg value="--iterations ${octane.iterations}"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
@ -400,12 +349,14 @@
|
||||
fork="true"
|
||||
dir=".">
|
||||
<jvmarg line="${run.test.jvmargs.octane} -Xms${run.test.xms} -Xmx${run.test.xmx}"/>
|
||||
<arg value="-opt"/>
|
||||
<arg value="9"/>
|
||||
<arg value="${octane-test-sys-prop.test.js.framework}"/>
|
||||
<arg value="${octane-tests}"/>
|
||||
<arg value="--runtime"/>
|
||||
<arg value="Rhino"/>
|
||||
<arg value="rhino"/>
|
||||
<arg value="--verbose"/>
|
||||
<arg value="--iterations 8"/>
|
||||
<arg value="--iterations ${octane.iterations}"/>
|
||||
</java>
|
||||
</target>
|
||||
|
||||
@ -416,18 +367,22 @@
|
||||
<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}">
|
||||
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>
|
||||
|
||||
<!--- SUNSPIDER JOB BELOW -->
|
||||
|
||||
<!-- run sunspider with Nashorn -->
|
||||
<target name="sunspider" depends="sunspider-init">
|
||||
<target name="sunspider" depends="sunspider-nashorn"/>
|
||||
|
||||
<target name="sunspider-nashorn" depends="sunspider-init">
|
||||
<java classname="${nashorn.shell.tool}"
|
||||
classpath="${run.test.classpath}"
|
||||
fork="true"
|
||||
@ -439,6 +394,9 @@
|
||||
<arg value="${sunspider-test-sys-prop.test.js.framework}"/>
|
||||
<arg value="--"/>
|
||||
<arg value="${sunspider-tests}/"/>
|
||||
<arg value="--verbose"/>
|
||||
<arg value="--times"/>
|
||||
<arg value="${sunspider.iterations}"/>
|
||||
</java>
|
||||
</target>
|
||||
|
||||
@ -448,6 +406,9 @@
|
||||
<arg value="${sunspider-test-sys-prop.test.js.framework}"/>
|
||||
<arg value="--"/>
|
||||
<arg value="${sunspider-tests}/"/>
|
||||
<arg value="--verbose"/>
|
||||
<arg value="--times"/>
|
||||
<arg value="${sunspider.iterations}"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
@ -458,8 +419,13 @@
|
||||
fork="true"
|
||||
dir=".">
|
||||
<jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx}"/>
|
||||
<arg value="-opt"/>
|
||||
<arg value="9"/>
|
||||
<arg value="${sunspider-test-sys-prop.test.js.framework}"/>
|
||||
<arg value="${sunspider-tests}/"/>
|
||||
<arg value="--verbose"/>
|
||||
<arg value="--times"/>
|
||||
<arg value="${sunspider.iterations}"/>
|
||||
</java>
|
||||
</target>
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
@ -21,12 +22,11 @@
|
||||
or visit www.oracle.com if you need additional information or have any
|
||||
questions.
|
||||
-->
|
||||
|
||||
<project name="nashorn" default="test" basedir="..">
|
||||
<import file="build-nasgen.xml"/>
|
||||
<import file="build-benchmark.xml"/>
|
||||
<import file="code_coverage.xml"/>
|
||||
|
||||
|
||||
<target name="init-conditions">
|
||||
<!-- loading locally defined resources and properties. NB they owerwrite default ones defined later -->
|
||||
<property file="${user.home}/.nashorn.project.local.properties"/>
|
||||
@ -51,7 +51,7 @@
|
||||
<available property="testng.available" file="${file.reference.testng.jar}"/>
|
||||
<!-- check if Jemmy ang testng.jar are avaiable -->
|
||||
<condition property="jemmy.jfx.testng.available" value="true">
|
||||
<and>
|
||||
<and>
|
||||
<available file="${file.reference.jemmyfx.jar}"/>
|
||||
<available file="${file.reference.jemmycore.jar}"/>
|
||||
<available file="${file.reference.jemmyawtinput.jar}"/>
|
||||
@ -364,6 +364,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
|
||||
</target>
|
||||
|
||||
<target name="test" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
|
||||
<delete dir="${build.dir}/nashorn_code_cache"/>
|
||||
<fileset id="test.classes" dir="${build.test.classes.dir}">
|
||||
<include name="**/api/javaaccess/*Test.class"/>
|
||||
<include name="**/api/scripting/*Test.class"/>
|
||||
@ -395,6 +396,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
|
||||
<pathelement path="${run.test.classpath}"/>
|
||||
</classpath>
|
||||
</testng>
|
||||
|
||||
<testng outputdir="${build.nosecurity.test.results.dir}" classfilesetref="test.nosecurity.classes"
|
||||
verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
|
||||
<jvmarg line="${ext.class.path}"/>
|
||||
@ -412,18 +414,6 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
|
||||
</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} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs}"/>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="test-sys-prop."/>
|
||||
<mapper type="glob" from="test-sys-prop.*" to="*"/>
|
||||
</syspropertyset>
|
||||
</java>
|
||||
</target>
|
||||
|
||||
<target name="check-jemmy.jfx.testng" unless="jemmy.jfx.testng.available">
|
||||
<echo message="WARNING: Jemmy or JavaFX or TestNG not available, will not run tests. Please copy testng.jar, JemmyCore.jar, JemmyFX.jar, JemmyAWTInput.jar under test${file.separator}lib directory. And make sure you have jfxrt.jar in ${java.home}${file.separator}lib${file.separator}ext dir."/>
|
||||
</target>
|
||||
@ -432,15 +422,15 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
|
||||
<fileset id="test.classes" dir="${build.test.classes.dir}">
|
||||
<include name="**/framework/*Test.class"/>
|
||||
</fileset>
|
||||
|
||||
|
||||
<copy file="${file.reference.jfxrt.jar}" todir="dist"/>
|
||||
|
||||
|
||||
<condition property="jfx.prism.order" value="-Dprism.order=j2d" else=" ">
|
||||
<not>
|
||||
<not>
|
||||
<os family="mac"/>
|
||||
</not>
|
||||
</condition>
|
||||
|
||||
</condition>
|
||||
|
||||
<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}"/>
|
||||
@ -455,7 +445,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
|
||||
</classpath>
|
||||
</testng>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="testmarkdown" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
|
||||
<fileset id="test.classes" dir="${build.test.classes.dir}">
|
||||
<include name="**/framework/*Test.class"/>
|
||||
@ -474,7 +464,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
|
||||
</classpath>
|
||||
</testng>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="test262" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
|
||||
<fileset id="test.classes" dir="${build.test.classes.dir}">
|
||||
<include name="**/framework/*Test.class"/>
|
||||
@ -514,6 +504,26 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
|
||||
</java>
|
||||
</target>
|
||||
|
||||
<target name="testparallel" depends="test-parallel"/>
|
||||
|
||||
<target name="test-parallel" depends="jar, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
|
||||
<!-- use just build.test.classes.dir to avoid referring to TestNG -->
|
||||
<java classname="${parallel.test.runner}" dir="${basedir}"
|
||||
failonerror="true"
|
||||
fork="true">
|
||||
<jvmarg line="${ext.class.path}"/>
|
||||
<jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs}"/>
|
||||
<classpath>
|
||||
<pathelement path="${run.test.classpath}"/>
|
||||
<pathelement path="${build.test.classes.dir}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="test-sys-prop."/>
|
||||
<mapper type="glob" from="test-sys-prop.*" to="*"/>
|
||||
</syspropertyset>
|
||||
</java>
|
||||
</target>
|
||||
|
||||
<target name="all" depends="test, docs"
|
||||
description="Build, test and generate docs for nashorn"/>
|
||||
|
||||
@ -621,7 +631,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
|
||||
<mkdir dir="${test.external.dir}/yui"/>
|
||||
<get src="http://yui.yahooapis.com/3.5.1/build/yui/yui.js" dest="${test.external.dir}/yui" skipexisting="true" ignoreerrors="true"/>
|
||||
<get src="http://yui.yahooapis.com/3.5.1/build/yui/yui-min.js" dest="${test.external.dir}/yui" skipexisting="true" ignoreerrors="true"/>
|
||||
|
||||
|
||||
<!-- showdown -->
|
||||
<mkdir dir="${test.external.dir}/showdown"/>
|
||||
<get src="https://raw.github.com/coreyti/showdown/master/src/showdown.js" dest="${test.external.dir}/showdown" skipexisting="true" ignoreerrors="true"/>
|
||||
@ -642,4 +652,6 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
|
||||
|
||||
<target name="alltests" depends="exit-if-no-testng, externals, update-externals, test, test262parallel, perf"/>
|
||||
|
||||
<import file="build-benchmark.xml"/>
|
||||
|
||||
</project>
|
||||
|
@ -31,9 +31,10 @@
|
||||
<classpath path="${run.test.classpath}"/>
|
||||
</nbjpdastart>
|
||||
<java classname="jdk.nashorn.tools.Shell" classpath="${run.test.classpath}" dir="samples" fork="true">
|
||||
<jvmarg line="-Dnashorn.optimistic"/>
|
||||
<jvmarg line="${ext.class.path}"/>
|
||||
<jvmarg line="${run.test.jvmargs}"/>
|
||||
<arg value="test.js"/>
|
||||
<arg value="../make/str.js"/>
|
||||
<jvmarg value="-Xdebug"/>
|
||||
<jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
|
||||
</java>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -53,6 +53,7 @@ parallel.test.runner=jdk.nashorn.internal.test.framework.ParallelTestRunner
|
||||
|
||||
# test classes directory
|
||||
build.test.classes.dir=${build.dir}/test/classes
|
||||
|
||||
# nashorn test jar - internal tests jar and api tests jar
|
||||
nashorn.internal.tests.jar=${build.dir}/nashorn-internal-tests.jar
|
||||
nashorn.api.tests.jar=${build.dir}/nashorn-api-tests.jar
|
||||
@ -60,6 +61,7 @@ nashorn.api.tests.jar=${build.dir}/nashorn-api-tests.jar
|
||||
# test results directory
|
||||
build.test.results.dir=${build.dir}/test/reports
|
||||
build.nosecurity.test.results.dir=${build.dir}/test/nosecurity/reports
|
||||
build.nooptimistic.test.results.dir=${build.dir}/test/nooptimistic/reports
|
||||
|
||||
# This directory is removed when the project is cleaned:
|
||||
dist.dir=dist
|
||||
@ -167,21 +169,14 @@ test-sys-prop.test.js.unchecked.dir=${test262.dir}
|
||||
# test root for octane
|
||||
octane-test-sys-prop.test.js.roots=${test.external.dir}/octane/
|
||||
|
||||
# run octane benchmars in separate processes?
|
||||
# run octane benchmars in separate processes? (recommended)
|
||||
octane-test-sys-prop.separate.process=true
|
||||
|
||||
# framework root for octane
|
||||
octane-test-sys-prop.test.js.framework=${test.basic.dir}/run-octane.js
|
||||
|
||||
# list of tests to be excluded
|
||||
# mandreel excluded due to OOM
|
||||
octane-test-sys-prop.test.js.exclude.list=\
|
||||
base.js \
|
||||
run.js \
|
||||
mandreel.js
|
||||
|
||||
# test root for sunspider
|
||||
sunspider-test-sys-prop.test.js.roots=${test.external.dir}/sunspider/tests/sunspider-1.0/
|
||||
sunspider-test-sys-prop.test.js.roots=${test.external.dir}/sunspider/tests/sunspider-1.0.2/
|
||||
|
||||
# framework root for sunspider
|
||||
sunspider-test-sys-prop.test.js.framework=${test.basic.dir}/runsunspider.js
|
||||
@ -197,6 +192,7 @@ 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
|
||||
|
||||
@ -268,48 +264,113 @@ test.src.dir=test/src
|
||||
run.test.xmx=2G
|
||||
run.test.xms=2G
|
||||
|
||||
# uncomment this jfr.args to enable light recordings. the stack needs to be cranked up to 1024 frames,
|
||||
# or everything will as of the now drown in lambda forms and be cut off.
|
||||
#
|
||||
#jfr.args=-XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath="test_suite.jfr",stackdepth=1024 \
|
||||
|
||||
jfr.args=
|
||||
|
||||
run.test.user.language=tr
|
||||
run.test.user.country=TR
|
||||
|
||||
run.test.jvmargs.common=-server -XX:+TieredCompilation -Dfile.encoding=UTF-8 -Duser.language=${run.test.user.language} -Duser.country=${run.test.user.country} -XX:+HeapDumpOnOutOfMemoryError
|
||||
|
||||
#-XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
|
||||
# -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintNMethods
|
||||
run.test.jvmargs.common=\
|
||||
-server \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
-Duser.language=${run.test.user.language} \
|
||||
-Duser.country=${run.test.user.country} \
|
||||
${jfr.args} \
|
||||
-XX:+HeapDumpOnOutOfMemoryError
|
||||
|
||||
# turn on assertions for tests
|
||||
run.test.jvmargs.main=${run.test.jvmargs.common} -ea
|
||||
run.test.jvmargs.main=${run.test.jvmargs.common} -ea -Dnashorn.lazy
|
||||
|
||||
#-XX:-UseCompressedKlassPointers -XX:+PrintHeapAtGC -XX:ClassMetaspaceSize=300M
|
||||
run.test.jvmargs.octane.main=${run.test.jvmargs.common}
|
||||
# extra jvmargs that might be useful for debugging
|
||||
#
|
||||
# -XX:+UnlockDiagnosticVMOptions
|
||||
#
|
||||
# turn off compressed class pointers in metaspace
|
||||
# -XX:-UseCompressedKlassPointers
|
||||
#
|
||||
# dump the heap after every GC
|
||||
# -XX:+PrintHeapAtGC
|
||||
#
|
||||
# manually set a metaspace size for class data
|
||||
# -XX:ClassMetaspaceSize=300M
|
||||
#
|
||||
# print out methods compiled
|
||||
# -XX:+PrintCompilation
|
||||
#
|
||||
# print all compiled nmethods with oopmaps and lots of other info
|
||||
# -XX:+PrintNMethods
|
||||
|
||||
# Use best known performance options for octane
|
||||
run.test.jvmargs.octane.main=${run.test.jvmargs.common} -Dnashorn.lazy -XX:+UnlockDiagnosticVMOptions -XX:+UseNewCode -XX:TypeProfileLevel=222
|
||||
|
||||
# Security manager args - make sure that we run with the nashorn.policy that the build creates
|
||||
run.test.jvmsecurityargs=-Xverify:all -Djava.security.manager -Djava.security.policy=${basedir}/build/nashorn.policy
|
||||
|
||||
# VM options for script tests with @fork option
|
||||
test-sys-prop.test.fork.jvm.options=${run.test.jvmargs.main} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -cp ${run.test.classpath}
|
||||
|
||||
# path of rhino.jar for benchmarks
|
||||
rhino.jar=
|
||||
rhino.dir=
|
||||
rhino.jar=${rhino.dir}/js.jar
|
||||
|
||||
v8.shell=d8
|
||||
|
||||
# How many iterations should 'ant octane' run for each
|
||||
# benchmark
|
||||
octane.iterations=25
|
||||
|
||||
# List of octane tests to run, as properties prefixed with
|
||||
# "octane.benchmark." mapping to the benchmark name in
|
||||
# the test harness
|
||||
#
|
||||
# Octane tests that are disabled should have their entire line
|
||||
# commented out Tests may be disabled for functionality reasons when
|
||||
# they have bugs or when the runtime doesn't handle them (yet)
|
||||
octane.benchmark.box2d=box2d
|
||||
#octane.benchmark.code-load=code-load
|
||||
octane.benchmark.crypto=crypto
|
||||
octane.benchmark.deltablue=deltablue
|
||||
octane.benchmark.earley-boyer=earley-boyer
|
||||
octane.benchmark.gbemu=gbemu
|
||||
octane.benchmark.navier-stokes=navier-stokes
|
||||
octane.benchmark.mandreel=mandreel
|
||||
octane.benchmark.pdfjs=pdfjs
|
||||
octane.benchmark.raytrace=raytrace
|
||||
octane.benchmark.regexp=regexp
|
||||
octane.benchmark.richards=richards
|
||||
octane.benchmark.splay=splay
|
||||
#octane.benchmark.typescript=typescript
|
||||
#octane.benchmark.zlib=zlib
|
||||
|
||||
#path to rhino jar file
|
||||
octaneperf-sys-prop.rhino.jar=${rhino.jar}
|
||||
|
||||
#timeout for performance tests in minutes
|
||||
octaneperf-sys-prop.timeout.value=10
|
||||
|
||||
################
|
||||
# codecoverage #
|
||||
################
|
||||
#enable/disable code coverage; please redifine in the ${user.home}/.nashorn.project.local.properties
|
||||
#how many iterations to run sunspider after warmup
|
||||
sunspider.iterations=3000
|
||||
|
||||
#################
|
||||
# code coverage #
|
||||
#################
|
||||
|
||||
#enable/disable code coverage; please redifine in the ${user.home}/.nashorn.project.local.properties
|
||||
make.code.coverage=false
|
||||
#type of codecoverage; one of static or dynamic. Now only dynamic is supported
|
||||
|
||||
#type of codecoverage; one of static or dynamic. Now only dynamic is supported
|
||||
jcov=dynamic
|
||||
#naming of CC results
|
||||
#NB directory specified in the cc.dir will be cleaned up!!!
|
||||
|
||||
#naming of CC results
|
||||
#NB directory specified in the cc.dir will be cleaned up!!!
|
||||
cc.dir=${basedir}/../Codecoverage_Nashorn
|
||||
cc.result.file.name=CC_${jcov}_nashorn.xml
|
||||
#dynamic CC parameters; please redefine in the ${user.home}/.nashorn.project.local.properties
|
||||
|
||||
#dynamic CC parameters; please redefine in the ${user.home}/.nashorn.project.local.properties
|
||||
jcov2.lib.dir=${basedir}/../jcov2/lib
|
||||
jcov.jar=${jcov2.lib.dir}/jcov.jar
|
||||
cc.include=jdk\.nashorn\.*
|
||||
|
@ -103,8 +103,27 @@ import jdk.internal.dynalink.support.Lookup;
|
||||
* handle is always at the start of the chain.
|
||||
*/
|
||||
public class ChainedCallSite extends AbstractRelinkableCallSite {
|
||||
private static final MethodHandle PRUNE = Lookup.findOwnSpecial(MethodHandles.lookup(), "prune", MethodHandle.class,
|
||||
MethodHandle.class);
|
||||
private static final MethodHandle PRUNE_CATCHES =
|
||||
MethodHandles.insertArguments(
|
||||
Lookup.findOwnSpecial(
|
||||
MethodHandles.lookup(),
|
||||
"prune",
|
||||
MethodHandle.class,
|
||||
MethodHandle.class,
|
||||
boolean.class),
|
||||
2,
|
||||
true);
|
||||
|
||||
private static final MethodHandle PRUNE_SWITCHPOINTS =
|
||||
MethodHandles.insertArguments(
|
||||
Lookup.findOwnSpecial(
|
||||
MethodHandles.lookup(),
|
||||
"prune",
|
||||
MethodHandle.class,
|
||||
MethodHandle.class,
|
||||
boolean.class),
|
||||
2,
|
||||
false);
|
||||
|
||||
private final AtomicReference<LinkedList<GuardedInvocation>> invocations = new AtomicReference<>();
|
||||
|
||||
@ -112,7 +131,7 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
|
||||
* Creates a new chained call site.
|
||||
* @param descriptor the descriptor for the call site.
|
||||
*/
|
||||
public ChainedCallSite(CallSiteDescriptor descriptor) {
|
||||
public ChainedCallSite(final CallSiteDescriptor descriptor) {
|
||||
super(descriptor);
|
||||
}
|
||||
|
||||
@ -126,24 +145,26 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void relink(GuardedInvocation guardedInvocation, MethodHandle fallback) {
|
||||
relinkInternal(guardedInvocation, fallback, false);
|
||||
public void relink(final GuardedInvocation guardedInvocation, final MethodHandle fallback) {
|
||||
relinkInternal(guardedInvocation, fallback, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAndRelink(GuardedInvocation guardedInvocation, MethodHandle fallback) {
|
||||
relinkInternal(guardedInvocation, fallback, true);
|
||||
public void resetAndRelink(final GuardedInvocation guardedInvocation, final MethodHandle fallback) {
|
||||
relinkInternal(guardedInvocation, fallback, true, false);
|
||||
}
|
||||
|
||||
private MethodHandle relinkInternal(GuardedInvocation invocation, MethodHandle relink, boolean reset) {
|
||||
private MethodHandle relinkInternal(final GuardedInvocation invocation, final MethodHandle relink, final boolean reset, final boolean removeCatches) {
|
||||
final LinkedList<GuardedInvocation> currentInvocations = invocations.get();
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
final LinkedList<GuardedInvocation> newInvocations =
|
||||
currentInvocations == null || reset ? new LinkedList<>() : (LinkedList)currentInvocations.clone();
|
||||
|
||||
// First, prune the chain of invalidated switchpoints.
|
||||
for(Iterator<GuardedInvocation> it = newInvocations.iterator(); it.hasNext();) {
|
||||
if(it.next().hasBeenInvalidated()) {
|
||||
// First, prune the chain of invalidated switchpoints, we always do this
|
||||
// We also remove any catches if the remove catches flag is set
|
||||
for(final Iterator<GuardedInvocation> it = newInvocations.iterator(); it.hasNext();) {
|
||||
final GuardedInvocation inv = it.next();
|
||||
if(inv.hasBeenInvalidated() || (removeCatches && inv.getException() != null)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
@ -160,12 +181,13 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
|
||||
|
||||
// prune-and-invoke is used as the fallback for invalidated switchpoints. If a switchpoint gets invalidated, we
|
||||
// rebuild the chain and get rid of all invalidated switchpoints instead of letting them linger.
|
||||
final MethodHandle pruneAndInvoke = makePruneAndInvokeMethod(relink);
|
||||
final MethodHandle pruneAndInvokeSwitchPoints = makePruneAndInvokeMethod(relink, getPruneSwitchpoints());
|
||||
final MethodHandle pruneAndInvokeCatches = makePruneAndInvokeMethod(relink, getPruneCatches());
|
||||
|
||||
// Fold the new chain
|
||||
MethodHandle target = relink;
|
||||
for(GuardedInvocation inv: newInvocations) {
|
||||
target = inv.compose(pruneAndInvoke, target);
|
||||
for(final GuardedInvocation inv: newInvocations) {
|
||||
target = inv.compose(target, pruneAndInvokeSwitchPoints, pruneAndInvokeCatches);
|
||||
}
|
||||
|
||||
// If nobody else updated the call site while we were rebuilding the chain, set the target to our chain. In case
|
||||
@ -177,15 +199,31 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the switchpoint pruning function for a chained call site
|
||||
* @return function that removes invalidated switchpoints tied to callsite guard chain and relinks
|
||||
*/
|
||||
protected MethodHandle getPruneSwitchpoints() {
|
||||
return PRUNE_SWITCHPOINTS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the catch pruning function for a chained call site
|
||||
* @return function that removes all catches tied to callsite guard chain and relinks
|
||||
*/
|
||||
protected MethodHandle getPruneCatches() {
|
||||
return PRUNE_CATCHES;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a method that rebuilds our call chain, pruning it of any invalidated switchpoints, and then invokes that
|
||||
* chain.
|
||||
* @param relink the ultimate fallback for the chain (the {@code DynamicLinker}'s relink).
|
||||
* @return a method handle for prune-and-invoke
|
||||
*/
|
||||
private MethodHandle makePruneAndInvokeMethod(MethodHandle relink) {
|
||||
private MethodHandle makePruneAndInvokeMethod(final MethodHandle relink, final MethodHandle prune) {
|
||||
// Bind prune to (this, relink)
|
||||
final MethodHandle boundPrune = MethodHandles.insertArguments(PRUNE, 0, this, relink);
|
||||
final MethodHandle boundPrune = MethodHandles.insertArguments(prune, 0, this, relink);
|
||||
// Make it ignore all incoming arguments
|
||||
final MethodHandle ignoreArgsPrune = MethodHandles.dropArguments(boundPrune, 0, type().parameterList());
|
||||
// Invoke prune, then invoke the call site target with original arguments
|
||||
@ -193,7 +231,7 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private MethodHandle prune(MethodHandle relink) {
|
||||
return relinkInternal(null, relink, false);
|
||||
private MethodHandle prune(final MethodHandle relink, final boolean catches) {
|
||||
return relinkInternal(null, relink, false, catches);
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ public class DefaultBootstrapper {
|
||||
* @param type the method signature at the call site
|
||||
* @return a new {@link MonomorphicCallSite} linked with the default dynamic linker.
|
||||
*/
|
||||
public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) {
|
||||
public static CallSite bootstrap(final MethodHandles.Lookup caller, final String name, final MethodType type) {
|
||||
return bootstrapInternal(caller, name, type);
|
||||
}
|
||||
|
||||
@ -133,11 +133,11 @@ public class DefaultBootstrapper {
|
||||
* @param type the method signature at the call site
|
||||
* @return a new {@link MonomorphicCallSite} linked with the default dynamic linker.
|
||||
*/
|
||||
public static CallSite publicBootstrap(MethodHandles.Lookup caller, String name, MethodType type) {
|
||||
public static CallSite publicBootstrap(final MethodHandles.Lookup caller, final String name, final MethodType type) {
|
||||
return bootstrapInternal(MethodHandles.publicLookup(), name, type);
|
||||
}
|
||||
|
||||
private static CallSite bootstrapInternal(MethodHandles.Lookup caller, String name, MethodType type) {
|
||||
private static CallSite bootstrapInternal(final MethodHandles.Lookup caller, final String name, final MethodType type) {
|
||||
return dynamicLinker.link(new MonomorphicCallSite(CallSiteDescriptorFactory.create(caller, name, type)));
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +140,6 @@ import jdk.internal.dynalink.support.RuntimeContextLinkRequestImpl;
|
||||
* @author Attila Szegedi
|
||||
*/
|
||||
public class DynamicLinker {
|
||||
|
||||
private static final String CLASS_NAME = DynamicLinker.class.getName();
|
||||
private static final String RELINK_METHOD_NAME = "relink";
|
||||
|
||||
@ -148,6 +147,7 @@ public class DynamicLinker {
|
||||
private static final String INITIAL_LINK_METHOD_NAME = "linkCallSite";
|
||||
|
||||
private final LinkerServices linkerServices;
|
||||
private final GuardedInvocationFilter prelinkFilter;
|
||||
private final int runtimeContextArgCount;
|
||||
private final boolean syncOnRelink;
|
||||
private final int unstableRelinkThreshold;
|
||||
@ -156,18 +156,20 @@ public class DynamicLinker {
|
||||
* Creates a new dynamic linker.
|
||||
*
|
||||
* @param linkerServices the linkerServices used by the linker, created by the factory.
|
||||
* @param prelinkFilter see {@link DynamicLinkerFactory#setPrelinkFilter(GuardedInvocationFilter)}
|
||||
* @param runtimeContextArgCount see {@link DynamicLinkerFactory#setRuntimeContextArgCount(int)}
|
||||
*/
|
||||
DynamicLinker(LinkerServices linkerServices, int runtimeContextArgCount, boolean syncOnRelink,
|
||||
int unstableRelinkThreshold) {
|
||||
DynamicLinker(final LinkerServices linkerServices, final GuardedInvocationFilter prelinkFilter, final int runtimeContextArgCount,
|
||||
final boolean syncOnRelink, final int unstableRelinkThreshold) {
|
||||
if(runtimeContextArgCount < 0) {
|
||||
throw new IllegalArgumentException("runtimeContextArgCount < 0");
|
||||
}
|
||||
if(unstableRelinkThreshold < 0) {
|
||||
throw new IllegalArgumentException("unstableRelinkThreshold < 0");
|
||||
}
|
||||
this.runtimeContextArgCount = runtimeContextArgCount;
|
||||
this.linkerServices = linkerServices;
|
||||
this.prelinkFilter = prelinkFilter;
|
||||
this.runtimeContextArgCount = runtimeContextArgCount;
|
||||
this.syncOnRelink = syncOnRelink;
|
||||
this.unstableRelinkThreshold = unstableRelinkThreshold;
|
||||
}
|
||||
@ -199,7 +201,7 @@ public class DynamicLinker {
|
||||
private static final MethodHandle RELINK = Lookup.findOwnSpecial(MethodHandles.lookup(), RELINK_METHOD_NAME,
|
||||
MethodHandle.class, RelinkableCallSite.class, int.class, Object[].class);
|
||||
|
||||
private MethodHandle createRelinkAndInvokeMethod(final RelinkableCallSite callSite, int relinkCount) {
|
||||
private MethodHandle createRelinkAndInvokeMethod(final RelinkableCallSite callSite, final int relinkCount) {
|
||||
// Make a bound MH of invoke() for this linker and call site
|
||||
final MethodHandle boundRelinker = MethodHandles.insertArguments(RELINK, 0, this, callSite, Integer.valueOf(
|
||||
relinkCount));
|
||||
@ -219,16 +221,15 @@ public class DynamicLinker {
|
||||
* @throws Exception rethrows any exception thrown by the linkers
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private MethodHandle relink(RelinkableCallSite callSite, int relinkCount, Object... arguments) throws Exception {
|
||||
private MethodHandle relink(final RelinkableCallSite callSite, final int relinkCount, final Object... arguments) throws Exception {
|
||||
final CallSiteDescriptor callSiteDescriptor = callSite.getDescriptor();
|
||||
final boolean unstableDetectionEnabled = unstableRelinkThreshold > 0;
|
||||
final boolean callSiteUnstable = unstableDetectionEnabled && relinkCount >= unstableRelinkThreshold;
|
||||
final LinkRequest linkRequest =
|
||||
runtimeContextArgCount == 0 ? new LinkRequestImpl(callSiteDescriptor, callSiteUnstable, arguments)
|
||||
: new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSiteUnstable, arguments,
|
||||
runtimeContextArgCount);
|
||||
runtimeContextArgCount == 0 ?
|
||||
new LinkRequestImpl(callSiteDescriptor, callSite, relinkCount, callSiteUnstable, arguments) :
|
||||
new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSite, relinkCount, callSiteUnstable, arguments, runtimeContextArgCount);
|
||||
|
||||
// Find a suitable method handle with a guard
|
||||
GuardedInvocation guardedInvocation = linkerServices.getGuardedInvocation(linkRequest);
|
||||
|
||||
// None found - throw an exception
|
||||
@ -248,6 +249,11 @@ public class DynamicLinker {
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we filter the invocation before linking it into the call site. This is typically used to match the
|
||||
// return type of the invocation to the call site.
|
||||
guardedInvocation = prelinkFilter.filter(guardedInvocation, linkRequest, linkerServices);
|
||||
guardedInvocation.getClass(); // null pointer check
|
||||
|
||||
int newRelinkCount = relinkCount;
|
||||
// Note that the short-circuited "&&" evaluation below ensures we'll increment the relinkCount until
|
||||
// threshold + 1 but not beyond that. Threshold + 1 is treated as a special value to signal that resetAndRelink
|
||||
|
@ -102,14 +102,15 @@ import jdk.internal.dynalink.support.BottomGuardingDynamicLinker;
|
||||
import jdk.internal.dynalink.support.ClassLoaderGetterContextProvider;
|
||||
import jdk.internal.dynalink.support.CompositeGuardingDynamicLinker;
|
||||
import jdk.internal.dynalink.support.CompositeTypeBasedGuardingDynamicLinker;
|
||||
import jdk.internal.dynalink.support.DefaultPrelinkFilter;
|
||||
import jdk.internal.dynalink.support.LinkerServicesImpl;
|
||||
import jdk.internal.dynalink.support.TypeConverterFactory;
|
||||
|
||||
/**
|
||||
* A factory class for creating {@link DynamicLinker}s. The most usual dynamic linker is a linker that is a composition
|
||||
* of all {@link GuardingDynamicLinker}s known and pre-created by the caller as well as any
|
||||
* {@link AutoDiscovery automatically discovered} guarding linkers and the standard fallback {@link BeansLinker}. See
|
||||
* {@link DynamicLinker} documentation for tips on how to use this class.
|
||||
* {@link AutoDiscovery automatically discovered} guarding linkers and the standard fallback {@link BeansLinker} and a
|
||||
* {@link DefaultPrelinkFilter}. See {@link DynamicLinker} documentation for tips on how to use this class.
|
||||
*
|
||||
* @author Attila Szegedi
|
||||
*/
|
||||
@ -128,6 +129,7 @@ public class DynamicLinkerFactory {
|
||||
private int runtimeContextArgCount = 0;
|
||||
private boolean syncOnRelink = false;
|
||||
private int unstableRelinkThreshold = DEFAULT_UNSTABLE_RELINK_THRESHOLD;
|
||||
private GuardedInvocationFilter prelinkFilter;
|
||||
|
||||
/**
|
||||
* Sets the class loader for automatic discovery of available linkers. If not set explicitly, then the thread
|
||||
@ -135,7 +137,7 @@ public class DynamicLinkerFactory {
|
||||
*
|
||||
* @param classLoader the class loader used for the autodiscovery of available linkers.
|
||||
*/
|
||||
public void setClassLoader(ClassLoader classLoader) {
|
||||
public void setClassLoader(final ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
classLoaderExplicitlySet = true;
|
||||
}
|
||||
@ -149,7 +151,7 @@ public class DynamicLinkerFactory {
|
||||
* @param prioritizedLinkers the list of prioritized linkers. Null can be passed to indicate no prioritized linkers
|
||||
* (this is also the default value).
|
||||
*/
|
||||
public void setPrioritizedLinkers(List<? extends GuardingDynamicLinker> prioritizedLinkers) {
|
||||
public void setPrioritizedLinkers(final List<? extends GuardingDynamicLinker> prioritizedLinkers) {
|
||||
this.prioritizedLinkers =
|
||||
prioritizedLinkers == null ? null : new ArrayList<>(prioritizedLinkers);
|
||||
}
|
||||
@ -162,7 +164,7 @@ public class DynamicLinkerFactory {
|
||||
*
|
||||
* @param prioritizedLinkers a list of prioritized linkers.
|
||||
*/
|
||||
public void setPrioritizedLinkers(GuardingDynamicLinker... prioritizedLinkers) {
|
||||
public void setPrioritizedLinkers(final GuardingDynamicLinker... prioritizedLinkers) {
|
||||
setPrioritizedLinkers(Arrays.asList(prioritizedLinkers));
|
||||
}
|
||||
|
||||
@ -173,7 +175,7 @@ public class DynamicLinkerFactory {
|
||||
* @param prioritizedLinker the single prioritized linker. Must not be null.
|
||||
* @throws IllegalArgumentException if null is passed.
|
||||
*/
|
||||
public void setPrioritizedLinker(GuardingDynamicLinker prioritizedLinker) {
|
||||
public void setPrioritizedLinker(final GuardingDynamicLinker prioritizedLinker) {
|
||||
if(prioritizedLinker == null) {
|
||||
throw new IllegalArgumentException("prioritizedLinker == null");
|
||||
}
|
||||
@ -188,7 +190,7 @@ public class DynamicLinkerFactory {
|
||||
* @param fallbackLinkers the list of fallback linkers. Can be empty to indicate the caller wishes to set no
|
||||
* fallback linkers.
|
||||
*/
|
||||
public void setFallbackLinkers(List<? extends GuardingDynamicLinker> fallbackLinkers) {
|
||||
public void setFallbackLinkers(final List<? extends GuardingDynamicLinker> fallbackLinkers) {
|
||||
this.fallbackLinkers = fallbackLinkers == null ? null : new ArrayList<>(fallbackLinkers);
|
||||
}
|
||||
|
||||
@ -200,7 +202,7 @@ public class DynamicLinkerFactory {
|
||||
* @param fallbackLinkers the list of fallback linkers. Can be empty to indicate the caller wishes to set no
|
||||
* fallback linkers. If it is left as null, the standard fallback {@link BeansLinker} will be used.
|
||||
*/
|
||||
public void setFallbackLinkers(GuardingDynamicLinker... fallbackLinkers) {
|
||||
public void setFallbackLinkers(final GuardingDynamicLinker... fallbackLinkers) {
|
||||
setFallbackLinkers(Arrays.asList(fallbackLinkers));
|
||||
}
|
||||
|
||||
@ -214,7 +216,7 @@ public class DynamicLinkerFactory {
|
||||
*
|
||||
* @param runtimeContextArgCount the number of language runtime context arguments in call sites.
|
||||
*/
|
||||
public void setRuntimeContextArgCount(int runtimeContextArgCount) {
|
||||
public void setRuntimeContextArgCount(final int runtimeContextArgCount) {
|
||||
if(runtimeContextArgCount < 0) {
|
||||
throw new IllegalArgumentException("runtimeContextArgCount < 0");
|
||||
}
|
||||
@ -227,7 +229,7 @@ public class DynamicLinkerFactory {
|
||||
* multithreaded execution of dynamically linked code.
|
||||
* @param syncOnRelink true for invoking sync on relink, false otherwise.
|
||||
*/
|
||||
public void setSyncOnRelink(boolean syncOnRelink) {
|
||||
public void setSyncOnRelink(final boolean syncOnRelink) {
|
||||
this.syncOnRelink = syncOnRelink;
|
||||
}
|
||||
|
||||
@ -238,7 +240,7 @@ public class DynamicLinkerFactory {
|
||||
* call sites will never be considered unstable.
|
||||
* @see LinkRequest#isCallSiteUnstable()
|
||||
*/
|
||||
public void setUnstableRelinkThreshold(int unstableRelinkThreshold) {
|
||||
public void setUnstableRelinkThreshold(final int unstableRelinkThreshold) {
|
||||
if(unstableRelinkThreshold < 0) {
|
||||
throw new IllegalArgumentException("unstableRelinkThreshold < 0");
|
||||
}
|
||||
@ -246,7 +248,19 @@ public class DynamicLinkerFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers.
|
||||
* Set the pre-link filter. This is a {@link GuardedInvocationFilter} that will get the final chance to modify the
|
||||
* guarded invocation after it has been created by a component linker and before the dynamic linker links it into
|
||||
* the call site. It is normally used to adapt the return value type of the invocation to the type of the call site.
|
||||
* When not set explicitly, {@link DefaultPrelinkFilter} will be used.
|
||||
* @param prelinkFilter the pre-link filter for the dynamic linker.
|
||||
*/
|
||||
public void setPrelinkFilter(final GuardedInvocationFilter prelinkFilter) {
|
||||
this.prelinkFilter = prelinkFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new dynamic linker consisting of all the prioritized, autodiscovered, and fallback linkers as well as
|
||||
* the pre-link filter.
|
||||
*
|
||||
* @return the new dynamic Linker
|
||||
*/
|
||||
@ -275,7 +289,7 @@ public class DynamicLinkerFactory {
|
||||
// ... prioritized linkers, ...
|
||||
linkers.addAll(prioritizedLinkers);
|
||||
// ... filtered discovered linkers, ...
|
||||
for(GuardingDynamicLinker linker: discovered) {
|
||||
for(final GuardingDynamicLinker linker: discovered) {
|
||||
if(!knownLinkerClasses.contains(linker.getClass())) {
|
||||
linkers.add(linker);
|
||||
}
|
||||
@ -300,14 +314,18 @@ public class DynamicLinkerFactory {
|
||||
}
|
||||
|
||||
final List<GuardingTypeConverterFactory> typeConverters = new LinkedList<>();
|
||||
for(GuardingDynamicLinker linker: linkers) {
|
||||
for(final GuardingDynamicLinker linker: linkers) {
|
||||
if(linker instanceof GuardingTypeConverterFactory) {
|
||||
typeConverters.add((GuardingTypeConverterFactory)linker);
|
||||
}
|
||||
}
|
||||
|
||||
if(prelinkFilter == null) {
|
||||
prelinkFilter = new DefaultPrelinkFilter();
|
||||
}
|
||||
|
||||
return new DynamicLinker(new LinkerServicesImpl(new TypeConverterFactory(typeConverters), composite),
|
||||
runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
|
||||
prelinkFilter, runtimeContextArgCount, syncOnRelink, unstableRelinkThreshold);
|
||||
}
|
||||
|
||||
private static ClassLoader getThreadContextClassLoader() {
|
||||
@ -319,9 +337,9 @@ public class DynamicLinkerFactory {
|
||||
}, ClassLoaderGetterContextProvider.GET_CLASS_LOADER_CONTEXT);
|
||||
}
|
||||
|
||||
private static void addClasses(Set<Class<? extends GuardingDynamicLinker>> knownLinkerClasses,
|
||||
List<? extends GuardingDynamicLinker> linkers) {
|
||||
for(GuardingDynamicLinker linker: linkers) {
|
||||
private static void addClasses(final Set<Class<? extends GuardingDynamicLinker>> knownLinkerClasses,
|
||||
final List<? extends GuardingDynamicLinker> linkers) {
|
||||
for(final GuardingDynamicLinker linker: linkers) {
|
||||
knownLinkerClasses.add(linker.getClass());
|
||||
}
|
||||
}
|
||||
|
105
nashorn/src/jdk/internal/dynalink/GuardedInvocationFilter.java
Normal file
105
nashorn/src/jdk/internal/dynalink/GuardedInvocationFilter.java
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, 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 file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file, and Oracle licenses the original version of this file under the BSD
|
||||
* license:
|
||||
*/
|
||||
/*
|
||||
Copyright 2009-2013 Attila Szegedi
|
||||
|
||||
Licensed under both the Apache License, Version 2.0 (the "Apache License")
|
||||
and the BSD License (the "BSD License"), with licensee being free to
|
||||
choose either of the two at their discretion.
|
||||
|
||||
You may not use this file except in compliance with either the Apache
|
||||
License or the BSD License.
|
||||
|
||||
If you choose to use this file in compliance with the Apache License, the
|
||||
following notice applies to you:
|
||||
|
||||
You may obtain a copy of the Apache License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied. See the License for the specific language governing
|
||||
permissions and limitations under the License.
|
||||
|
||||
If you choose to use this file in compliance with the BSD License, the
|
||||
following notice applies to you:
|
||||
|
||||
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 the copyright holder nor the names of
|
||||
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 COPYRIGHT HOLDER
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package jdk.internal.dynalink;
|
||||
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.internal.dynalink.linker.LinkerServices;
|
||||
|
||||
/**
|
||||
* Interface for objects that are used to transform one guarded invocation into another one. Typical usage is for
|
||||
* implementing {@link DynamicLinkerFactory#setPrelinkFilter(GuardedInvocationFilter) pre-link filters}.
|
||||
*/
|
||||
public interface GuardedInvocationFilter {
|
||||
/**
|
||||
* Given a guarded invocation, return a potentially different guarded invocation.
|
||||
* @param inv the original guarded invocation. Null is never passed.
|
||||
* @param linkRequest the link request for which the invocation was generated (usually by some linker).
|
||||
* @param linkerServices the linker services that can be used during creation of a new invocation.
|
||||
* @return either the passed guarded invocation or a different one, with the difference usually determined based on
|
||||
* information in the link request and the differing invocation created with the assistance of the linker services.
|
||||
* Whether or not {@code null} is an accepted return value is dependent on the user of the filter.
|
||||
*/
|
||||
public GuardedInvocation filter(GuardedInvocation inv, LinkRequest linkRequest, LinkerServices linkerServices);
|
||||
}
|
@ -99,17 +99,17 @@ public class MonomorphicCallSite extends AbstractRelinkableCallSite {
|
||||
* Creates a new call site with monomorphic inline caching strategy.
|
||||
* @param descriptor the descriptor for this call site
|
||||
*/
|
||||
public MonomorphicCallSite(CallSiteDescriptor descriptor) {
|
||||
public MonomorphicCallSite(final CallSiteDescriptor descriptor) {
|
||||
super(descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void relink(GuardedInvocation guardedInvocation, MethodHandle relink) {
|
||||
public void relink(final GuardedInvocation guardedInvocation, final MethodHandle relink) {
|
||||
setTarget(guardedInvocation.compose(relink));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAndRelink(GuardedInvocation guardedInvocation, MethodHandle relink) {
|
||||
public void resetAndRelink(final GuardedInvocation guardedInvocation, final MethodHandle relink) {
|
||||
relink(guardedInvocation, relink);
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ public class NoSuchDynamicMethodException extends RuntimeException {
|
||||
* Creates a new NoSuchDynamicMethodException
|
||||
* @param message the message of the exception.
|
||||
*/
|
||||
public NoSuchDynamicMethodException(String message) {
|
||||
public NoSuchDynamicMethodException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,6 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.beans.GuardedInvocationComponent.ValidationType;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
@ -107,6 +106,7 @@ import jdk.internal.dynalink.linker.LinkerServices;
|
||||
import jdk.internal.dynalink.support.CallSiteDescriptorFactory;
|
||||
import jdk.internal.dynalink.support.Guards;
|
||||
import jdk.internal.dynalink.support.Lookup;
|
||||
import jdk.internal.dynalink.support.TypeUtilities;
|
||||
|
||||
/**
|
||||
* A base class for both {@link StaticClassLinker} and {@link BeanLinker}. Deals with common aspects of property
|
||||
@ -123,18 +123,18 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
private final Map<String, DynamicMethod> propertySetters = new HashMap<>();
|
||||
private final Map<String, DynamicMethod> methods = new HashMap<>();
|
||||
|
||||
AbstractJavaLinker(Class<?> clazz, MethodHandle classGuard) {
|
||||
AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard) {
|
||||
this(clazz, classGuard, classGuard);
|
||||
}
|
||||
|
||||
AbstractJavaLinker(Class<?> clazz, MethodHandle classGuard, MethodHandle assignableGuard) {
|
||||
AbstractJavaLinker(final Class<?> clazz, final MethodHandle classGuard, final MethodHandle assignableGuard) {
|
||||
this.clazz = clazz;
|
||||
this.classGuard = classGuard;
|
||||
this.assignableGuard = assignableGuard;
|
||||
|
||||
final FacetIntrospector introspector = createFacetIntrospector();
|
||||
// Add methods and properties
|
||||
for(Method method: introspector.getMethods()) {
|
||||
for(final Method method: introspector.getMethods()) {
|
||||
final String name = method.getName();
|
||||
// Add method
|
||||
addMember(name, method, methods);
|
||||
@ -153,7 +153,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
|
||||
// Add field getter/setters as property getters/setters.
|
||||
for(Field field: introspector.getFields()) {
|
||||
for(final Field field: introspector.getFields()) {
|
||||
final String name = field.getName();
|
||||
// Only add a property getter when one is not defined already as a getXxx()/isXxx() method.
|
||||
if(!propertyGetters.containsKey(name)) {
|
||||
@ -166,7 +166,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
|
||||
// Add inner classes, but only those for which we don't hide a property with it
|
||||
for(Map.Entry<String, MethodHandle> innerClassSpec: introspector.getInnerClassGetters().entrySet()) {
|
||||
for(final Map.Entry<String, MethodHandle> innerClassSpec: introspector.getInnerClassGetters().entrySet()) {
|
||||
final String name = innerClassSpec.getKey();
|
||||
if(!propertyGetters.containsKey(name)) {
|
||||
setPropertyGetter(name, innerClassSpec.getValue(), ValidationType.EXACT_CLASS);
|
||||
@ -174,7 +174,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
}
|
||||
|
||||
private static String decapitalize(String str) {
|
||||
private static String decapitalize(final String str) {
|
||||
assert str != null;
|
||||
if(str.isEmpty()) {
|
||||
return str;
|
||||
@ -209,7 +209,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
return getUnmodifiableKeys(methods);
|
||||
}
|
||||
|
||||
private static Collection<String> getUnmodifiableKeys(Map<String, ?> m) {
|
||||
private static Collection<String> getUnmodifiableKeys(final Map<String, ?> m) {
|
||||
return Collections.unmodifiableCollection(m.keySet());
|
||||
}
|
||||
|
||||
@ -222,7 +222,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
* @param handle the method handle that implements the property getter
|
||||
* @param validationType the validation type for the property
|
||||
*/
|
||||
private void setPropertyGetter(String name, SingleDynamicMethod handle, ValidationType validationType) {
|
||||
private void setPropertyGetter(final String name, final SingleDynamicMethod handle, final ValidationType validationType) {
|
||||
propertyGetters.put(name, new AnnotatedDynamicMethod(handle, validationType));
|
||||
}
|
||||
|
||||
@ -232,7 +232,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
* @param prefixLen the getter prefix in the method name; should be 3 for getter names starting with "get" and 2 for
|
||||
* names starting with "is".
|
||||
*/
|
||||
private void setPropertyGetter(Method getter, int prefixLen) {
|
||||
private void setPropertyGetter(final Method getter, final int prefixLen) {
|
||||
setPropertyGetter(decapitalize(getter.getName().substring(prefixLen)), createDynamicMethod(
|
||||
getMostGenericGetter(getter)), ValidationType.INSTANCE_OF);
|
||||
}
|
||||
@ -246,15 +246,15 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
* @param handle the method handle that implements the property getter
|
||||
* @param validationType the validation type for the property
|
||||
*/
|
||||
void setPropertyGetter(String name, MethodHandle handle, ValidationType validationType) {
|
||||
void setPropertyGetter(final String name, final MethodHandle handle, final ValidationType validationType) {
|
||||
setPropertyGetter(name, new SimpleDynamicMethod(handle, clazz, name), validationType);
|
||||
}
|
||||
|
||||
private void addMember(String name, AccessibleObject ao, Map<String, DynamicMethod> methodMap) {
|
||||
private void addMember(final String name, final AccessibleObject ao, final Map<String, DynamicMethod> methodMap) {
|
||||
addMember(name, createDynamicMethod(ao), methodMap);
|
||||
}
|
||||
|
||||
private void addMember(String name, SingleDynamicMethod method, Map<String, DynamicMethod> methodMap) {
|
||||
private void addMember(final String name, final SingleDynamicMethod method, final Map<String, DynamicMethod> methodMap) {
|
||||
final DynamicMethod existingMethod = methodMap.get(name);
|
||||
final DynamicMethod newMethod = mergeMethods(method, existingMethod, clazz, name);
|
||||
if(newMethod != existingMethod) {
|
||||
@ -270,9 +270,9 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
* @param name the common name of the reflective members.
|
||||
* @return a dynamic method representing all the specified reflective members.
|
||||
*/
|
||||
static DynamicMethod createDynamicMethod(Iterable<? extends AccessibleObject> members, Class<?> clazz, String name) {
|
||||
static DynamicMethod createDynamicMethod(final Iterable<? extends AccessibleObject> members, final Class<?> clazz, final String name) {
|
||||
DynamicMethod dynMethod = null;
|
||||
for(AccessibleObject method: members) {
|
||||
for(final AccessibleObject method: members) {
|
||||
dynMethod = mergeMethods(createDynamicMethod(method), dynMethod, clazz, name);
|
||||
}
|
||||
return dynMethod;
|
||||
@ -285,7 +285,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
* @param m the reflective member
|
||||
* @return the single dynamic method representing the reflective member
|
||||
*/
|
||||
private static SingleDynamicMethod createDynamicMethod(AccessibleObject m) {
|
||||
private static SingleDynamicMethod createDynamicMethod(final AccessibleObject m) {
|
||||
if(CallerSensitiveDetector.isCallerSensitive(m)) {
|
||||
return new CallerSensitiveDynamicMethod(m);
|
||||
}
|
||||
@ -301,7 +301,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
* @param m the method or constructor
|
||||
* @return the method handle
|
||||
*/
|
||||
private static MethodHandle unreflectSafely(AccessibleObject m) {
|
||||
private static MethodHandle unreflectSafely(final AccessibleObject m) {
|
||||
if(m instanceof Method) {
|
||||
final Method reflMethod = (Method)m;
|
||||
final MethodHandle handle = Lookup.PUBLIC.unreflect(reflMethod);
|
||||
@ -313,7 +313,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
return StaticClassIntrospector.editConstructorMethodHandle(Lookup.PUBLIC.unreflectConstructor((Constructor<?>)m));
|
||||
}
|
||||
|
||||
private static DynamicMethod mergeMethods(SingleDynamicMethod method, DynamicMethod existing, Class<?> clazz, String name) {
|
||||
private static DynamicMethod mergeMethods(final SingleDynamicMethod method, final DynamicMethod existing, final Class<?> clazz, final String name) {
|
||||
if(existing == null) {
|
||||
return method;
|
||||
} else if(existing.contains(method)) {
|
||||
@ -331,7 +331,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation getGuardedInvocation(LinkRequest request, final LinkerServices linkerServices)
|
||||
public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)
|
||||
throws Exception {
|
||||
final LinkRequest ncrequest = request.withoutRuntimeContext();
|
||||
// BeansLinker already checked that the name is at least 2 elements long and the first element is "dyn".
|
||||
@ -353,8 +353,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected GuardedInvocationComponent getGuardedInvocationComponent(CallSiteDescriptor callSiteDescriptor,
|
||||
LinkerServices linkerServices, List<String> operations) throws Exception {
|
||||
protected GuardedInvocationComponent getGuardedInvocationComponent(final CallSiteDescriptor callSiteDescriptor,
|
||||
final LinkerServices linkerServices, final List<String> operations) throws Exception {
|
||||
if(operations.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@ -374,27 +374,27 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
return null;
|
||||
}
|
||||
|
||||
static final <T> List<T> pop(List<T> l) {
|
||||
static final <T> List<T> pop(final List<T> l) {
|
||||
return l.subList(1, l.size());
|
||||
}
|
||||
|
||||
MethodHandle getClassGuard(CallSiteDescriptor desc) {
|
||||
MethodHandle getClassGuard(final CallSiteDescriptor desc) {
|
||||
return getClassGuard(desc.getMethodType());
|
||||
}
|
||||
|
||||
MethodHandle getClassGuard(MethodType type) {
|
||||
MethodHandle getClassGuard(final MethodType type) {
|
||||
return Guards.asType(classGuard, type);
|
||||
}
|
||||
|
||||
GuardedInvocationComponent getClassGuardedInvocationComponent(MethodHandle invocation, MethodType type) {
|
||||
GuardedInvocationComponent getClassGuardedInvocationComponent(final MethodHandle invocation, final MethodType type) {
|
||||
return new GuardedInvocationComponent(invocation, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
|
||||
}
|
||||
|
||||
private MethodHandle getAssignableGuard(MethodType type) {
|
||||
private MethodHandle getAssignableGuard(final MethodType type) {
|
||||
return Guards.asType(assignableGuard, type);
|
||||
}
|
||||
|
||||
private GuardedInvocation getCallPropWithThis(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) {
|
||||
private GuardedInvocation getCallPropWithThis(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
|
||||
switch(callSiteDescriptor.getNameTokenCount()) {
|
||||
case 3: {
|
||||
return createGuardedDynamicMethodInvocation(callSiteDescriptor, linkerServices,
|
||||
@ -406,25 +406,25 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
}
|
||||
|
||||
private GuardedInvocation createGuardedDynamicMethodInvocation(CallSiteDescriptor callSiteDescriptor,
|
||||
LinkerServices linkerServices, String methodName, Map<String, DynamicMethod> methodMap){
|
||||
private GuardedInvocation createGuardedDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
|
||||
final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap){
|
||||
final MethodHandle inv = getDynamicMethodInvocation(callSiteDescriptor, linkerServices, methodName, methodMap);
|
||||
return inv == null ? null : new GuardedInvocation(inv, getClassGuard(callSiteDescriptor.getMethodType()));
|
||||
}
|
||||
|
||||
private static MethodHandle getDynamicMethodInvocation(CallSiteDescriptor callSiteDescriptor,
|
||||
LinkerServices linkerServices, String methodName, Map<String, DynamicMethod> methodMap) {
|
||||
private static MethodHandle getDynamicMethodInvocation(final CallSiteDescriptor callSiteDescriptor,
|
||||
final LinkerServices linkerServices, final String methodName, final Map<String, DynamicMethod> methodMap) {
|
||||
final DynamicMethod dynaMethod = getDynamicMethod(methodName, methodMap);
|
||||
return dynaMethod != null ? dynaMethod.getInvocation(callSiteDescriptor, linkerServices) : null;
|
||||
}
|
||||
|
||||
private static DynamicMethod getDynamicMethod(String methodName, Map<String, DynamicMethod> methodMap) {
|
||||
private static DynamicMethod getDynamicMethod(final String methodName, final Map<String, DynamicMethod> methodMap) {
|
||||
final DynamicMethod dynaMethod = methodMap.get(methodName);
|
||||
return dynaMethod != null ? dynaMethod : getExplicitSignatureDynamicMethod(methodName, methodMap);
|
||||
}
|
||||
|
||||
private static SingleDynamicMethod getExplicitSignatureDynamicMethod(String methodName,
|
||||
Map<String, DynamicMethod> methodsMap) {
|
||||
private static SingleDynamicMethod getExplicitSignatureDynamicMethod(final String methodName,
|
||||
final Map<String, DynamicMethod> methodsMap) {
|
||||
// What's below is meant to support the "name(type, type, ...)" syntax that programmers can use in a method name
|
||||
// to manually pin down an exact overloaded variant. This is not usually required, as the overloaded method
|
||||
// resolution works correctly in almost every situation. However, in presence of many language-specific
|
||||
@ -457,14 +457,18 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
private static final MethodHandle CONSTANT_NULL_DROP_METHOD_HANDLE = MethodHandles.dropArguments(
|
||||
MethodHandles.constant(Object.class, null), 0, MethodHandle.class);
|
||||
|
||||
private GuardedInvocationComponent getPropertySetter(CallSiteDescriptor callSiteDescriptor,
|
||||
LinkerServices linkerServices, List<String> operations) throws Exception {
|
||||
final MethodType type = callSiteDescriptor.getMethodType();
|
||||
private GuardedInvocationComponent getPropertySetter(final CallSiteDescriptor callSiteDescriptor,
|
||||
final LinkerServices linkerServices, final List<String> operations) throws Exception {
|
||||
switch(callSiteDescriptor.getNameTokenCount()) {
|
||||
case 2: {
|
||||
// Must have three arguments: target object, property name, and property value.
|
||||
assertParameterCount(callSiteDescriptor, 3);
|
||||
|
||||
// We want setters that conform to "Object(O, V)". Note, we aren't doing "R(O, V)" as it might not be
|
||||
// valid for us to convert return values proactively. Also, since we don't know what setters will be
|
||||
// invoked, we'll conservatively presume Object return type.
|
||||
final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
|
||||
|
||||
// What's below is basically:
|
||||
// foldArguments(guardWithTest(isNotNull, invoke, null|nextComponent.invocation),
|
||||
// get_setter_handle(type, linkerServices))
|
||||
@ -473,8 +477,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
// component's invocation.
|
||||
|
||||
// Call site type is "ret_type(object_type,property_name_type,property_value_type)", which we'll
|
||||
// abbreviate to R(O, N, V) going forward.
|
||||
// We want setters that conform to "R(O, V)"
|
||||
// abbreviate to R(O, N, V) going forward, although we don't really use R here (see above about using
|
||||
// Object return type).
|
||||
final MethodType setterType = type.dropParameterTypes(1, 2);
|
||||
// Bind property setter handle to the expected setter type and linker services. Type is
|
||||
// MethodHandle(Object, String, Object)
|
||||
@ -495,11 +499,11 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
|
||||
final MethodHandle fallbackFolded;
|
||||
if(nextComponent == null) {
|
||||
// Object(MethodHandle)->R(MethodHandle, O, N, V); returns constant null
|
||||
// Object(MethodHandle)->Object(MethodHandle, O, N, V); returns constant null
|
||||
fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_METHOD_HANDLE, 1,
|
||||
type.parameterList()).asType(type.insertParameterTypes(0, MethodHandle.class));
|
||||
} else {
|
||||
// R(O, N, V)->R(MethodHandle, O, N, V); adapts the next component's invocation to drop the
|
||||
// Object(O, N, V)->Object(MethodHandle, O, N, V); adapts the next component's invocation to drop the
|
||||
// extra argument resulting from fold
|
||||
fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
|
||||
0, MethodHandle.class);
|
||||
@ -543,11 +547,14 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
"getTarget", MethodType.methodType(MethodHandle.class, MethodHandles.Lookup.class));
|
||||
private static final MethodHandle GETTER_INVOKER = MethodHandles.invoker(MethodType.methodType(Object.class, Object.class));
|
||||
|
||||
private GuardedInvocationComponent getPropertyGetter(CallSiteDescriptor callSiteDescriptor,
|
||||
LinkerServices linkerServices, List<String> ops) throws Exception {
|
||||
final MethodType type = callSiteDescriptor.getMethodType();
|
||||
private GuardedInvocationComponent getPropertyGetter(final CallSiteDescriptor callSiteDescriptor,
|
||||
final LinkerServices linkerServices, final List<String> ops) throws Exception {
|
||||
switch(callSiteDescriptor.getNameTokenCount()) {
|
||||
case 2: {
|
||||
// Since we can't know what kind of a getter we'll get back on different invocations, we'll just
|
||||
// conservatively presume Object. Note we can't just coerce to a narrower call site type as the linking
|
||||
// runtime might not allow coercing at that call site.
|
||||
final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
|
||||
// Must have exactly two arguments: receiver and name
|
||||
assertParameterCount(callSiteDescriptor, 2);
|
||||
|
||||
@ -563,11 +570,11 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
GET_ANNOTATED_METHOD, 1, callSiteDescriptor.getLookup());
|
||||
final MethodHandle callSiteBoundInvoker = MethodHandles.filterArguments(GETTER_INVOKER, 0,
|
||||
callSiteBoundMethodGetter);
|
||||
// Object(AnnotatedDynamicMethod, Object)->R(AnnotatedDynamicMethod, T0)
|
||||
// Object(AnnotatedDynamicMethod, Object)->Object(AnnotatedDynamicMethod, T0)
|
||||
final MethodHandle invokeHandleTyped = linkerServices.asType(callSiteBoundInvoker,
|
||||
MethodType.methodType(type.returnType(), AnnotatedDynamicMethod.class, type.parameterType(0)));
|
||||
// Since it's in the target of a fold, drop the unnecessary second argument
|
||||
// R(AnnotatedDynamicMethod, T0)->R(AnnotatedDynamicMethod, T0, T1)
|
||||
// Object(AnnotatedDynamicMethod, T0)->Object(AnnotatedDynamicMethod, T0, T1)
|
||||
final MethodHandle invokeHandleFolded = MethodHandles.dropArguments(invokeHandleTyped, 2,
|
||||
type.parameterType(1));
|
||||
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
|
||||
@ -575,17 +582,19 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
|
||||
final MethodHandle fallbackFolded;
|
||||
if(nextComponent == null) {
|
||||
// Object(AnnotatedDynamicMethod)->R(AnnotatedDynamicMethod, T0, T1); returns constant null
|
||||
// Object(AnnotatedDynamicMethod)->Object(AnnotatedDynamicMethod, T0, T1); returns constant null
|
||||
fallbackFolded = MethodHandles.dropArguments(CONSTANT_NULL_DROP_ANNOTATED_METHOD, 1,
|
||||
type.parameterList()).asType(type.insertParameterTypes(0, AnnotatedDynamicMethod.class));
|
||||
} else {
|
||||
// R(T0, T1)->R(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to drop the
|
||||
// extra argument resulting from fold
|
||||
fallbackFolded = MethodHandles.dropArguments(nextComponent.getGuardedInvocation().getInvocation(),
|
||||
0, AnnotatedDynamicMethod.class);
|
||||
// Object(T0, T1)->Object(AnnotatedDynamicMethod, T0, T1); adapts the next component's invocation to
|
||||
// drop the extra argument resulting from fold and to change its return type to Object.
|
||||
final MethodHandle nextInvocation = nextComponent.getGuardedInvocation().getInvocation();
|
||||
final MethodType nextType = nextInvocation.type();
|
||||
fallbackFolded = MethodHandles.dropArguments(nextInvocation.asType(
|
||||
nextType.changeReturnType(Object.class)), 0, AnnotatedDynamicMethod.class);
|
||||
}
|
||||
|
||||
// fold(R(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
|
||||
// fold(Object(AnnotatedDynamicMethod, T0, T1), AnnotatedDynamicMethod(T0, T1))
|
||||
final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
|
||||
IS_ANNOTATED_METHOD_NOT_NULL, invokeHandleFolded, fallbackFolded), typedGetter);
|
||||
if(nextComponent == null) {
|
||||
@ -612,8 +621,8 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
// value is null.
|
||||
final ValidationType validationType = annGetter.validationType;
|
||||
// TODO: we aren't using the type that declares the most generic getter here!
|
||||
return new GuardedInvocationComponent(linkerServices.asType(getter, type), getGuard(validationType,
|
||||
type), clazz, validationType);
|
||||
return new GuardedInvocationComponent(getter, getGuard(validationType,
|
||||
callSiteDescriptor.getMethodType()), clazz, validationType);
|
||||
}
|
||||
default: {
|
||||
// Can't do anything with more than 3 name components
|
||||
@ -622,7 +631,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
}
|
||||
|
||||
private MethodHandle getGuard(ValidationType validationType, MethodType methodType) {
|
||||
private MethodHandle getGuard(final ValidationType validationType, final MethodType methodType) {
|
||||
switch(validationType) {
|
||||
case EXACT_CLASS: {
|
||||
return getClassGuard(methodType);
|
||||
@ -642,21 +651,25 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
}
|
||||
|
||||
private static final MethodHandle IS_DYNAMIC_METHOD_NOT_NULL = Guards.asType(Guards.isNotNull(),
|
||||
MethodType.methodType(boolean.class, DynamicMethod.class));
|
||||
private static final MethodHandle DYNAMIC_METHOD_IDENTITY = MethodHandles.identity(DynamicMethod.class);
|
||||
private static final MethodHandle IS_DYNAMIC_METHOD = Guards.isInstance(DynamicMethod.class,
|
||||
MethodType.methodType(boolean.class, Object.class));
|
||||
private static final MethodHandle OBJECT_IDENTITY = MethodHandles.identity(Object.class);
|
||||
|
||||
private GuardedInvocationComponent getMethodGetter(CallSiteDescriptor callSiteDescriptor,
|
||||
LinkerServices linkerServices, List<String> ops) throws Exception {
|
||||
final MethodType type = callSiteDescriptor.getMethodType();
|
||||
private GuardedInvocationComponent getMethodGetter(final CallSiteDescriptor callSiteDescriptor,
|
||||
final LinkerServices linkerServices, final List<String> ops) throws Exception {
|
||||
// The created method handle will always return a DynamicMethod (or null), but since we don't want that type to
|
||||
// be visible outside of this linker, declare it to return Object.
|
||||
final MethodType type = callSiteDescriptor.getMethodType().changeReturnType(Object.class);
|
||||
switch(callSiteDescriptor.getNameTokenCount()) {
|
||||
case 2: {
|
||||
// Must have exactly two arguments: receiver and name
|
||||
assertParameterCount(callSiteDescriptor, 2);
|
||||
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
|
||||
linkerServices, ops);
|
||||
if(nextComponent == null) {
|
||||
// No next component operation; just return a component for this operation.
|
||||
if(nextComponent == null || !TypeUtilities.areAssignable(DynamicMethod.class,
|
||||
nextComponent.getGuardedInvocation().getInvocation().type().returnType())) {
|
||||
// No next component operation, or it can never produce a dynamic method; just return a component
|
||||
// for this operation.
|
||||
return getClassGuardedInvocationComponent(linkerServices.asType(getDynamicMethod, type), type);
|
||||
}
|
||||
|
||||
@ -665,21 +678,20 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
// bunch of method signature adjustments. Basically, execute method getter; if it returns a non-null
|
||||
// DynamicMethod, use identity to return it, otherwise delegate to nextComponent's invocation.
|
||||
|
||||
final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type.changeReturnType(
|
||||
DynamicMethod.class));
|
||||
final MethodHandle typedGetter = linkerServices.asType(getDynamicMethod, type);
|
||||
// Since it is part of the foldArgument() target, it will have extra args that we need to drop.
|
||||
final MethodHandle returnMethodHandle = linkerServices.asType(MethodHandles.dropArguments(
|
||||
DYNAMIC_METHOD_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0,
|
||||
DynamicMethod.class));
|
||||
OBJECT_IDENTITY, 1, type.parameterList()), type.insertParameterTypes(0, Object.class));
|
||||
final MethodHandle nextComponentInvocation = nextComponent.getGuardedInvocation().getInvocation();
|
||||
// The assumption is that getGuardedInvocationComponent() already asType()'d it correctly
|
||||
assert nextComponentInvocation.type().equals(type);
|
||||
// The assumption is that getGuardedInvocationComponent() already asType()'d it correctly modulo the
|
||||
// return type.
|
||||
assert nextComponentInvocation.type().changeReturnType(type.returnType()).equals(type);
|
||||
// Since it is part of the foldArgument() target, we have to drop an extra arg it receives.
|
||||
final MethodHandle nextCombinedInvocation = MethodHandles.dropArguments(nextComponentInvocation, 0,
|
||||
DynamicMethod.class);
|
||||
Object.class);
|
||||
// Assemble it all into a fold(guard(isNotNull, identity, nextInvocation), get)
|
||||
final MethodHandle compositeGetter = MethodHandles.foldArguments(MethodHandles.guardWithTest(
|
||||
IS_DYNAMIC_METHOD_NOT_NULL, returnMethodHandle, nextCombinedInvocation), typedGetter);
|
||||
IS_DYNAMIC_METHOD, returnMethodHandle, nextCombinedInvocation), typedGetter);
|
||||
|
||||
return nextComponent.compose(compositeGetter, getClassGuard(type), clazz, ValidationType.EXACT_CLASS);
|
||||
}
|
||||
@ -695,7 +707,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
// No delegation to the next component of the composite operation; if we have a method with that name,
|
||||
// we'll always return it at this point.
|
||||
return getClassGuardedInvocationComponent(linkerServices.asType(MethodHandles.dropArguments(
|
||||
MethodHandles.constant(DynamicMethod.class, method), 0, type.parameterType(0)), type), type);
|
||||
MethodHandles.constant(Object.class, method), 0, type.parameterType(0)), type), type);
|
||||
}
|
||||
default: {
|
||||
// Can't do anything with more than 3 name components
|
||||
@ -704,7 +716,31 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertParameterCount(CallSiteDescriptor descriptor, int paramCount) {
|
||||
static class MethodPair {
|
||||
final MethodHandle method1;
|
||||
final MethodHandle method2;
|
||||
|
||||
MethodPair(final MethodHandle method1, final MethodHandle method2) {
|
||||
this.method1 = method1;
|
||||
this.method2 = method2;
|
||||
}
|
||||
|
||||
MethodHandle guardWithTest(final MethodHandle test) {
|
||||
return MethodHandles.guardWithTest(test, method1, method2);
|
||||
}
|
||||
}
|
||||
|
||||
static MethodPair matchReturnTypes(final MethodHandle m1, final MethodHandle m2) {
|
||||
final MethodType type1 = m1.type();
|
||||
final MethodType type2 = m2.type();
|
||||
final Class<?> commonRetType = TypeUtilities.getCommonLosslessConversionType(type1.returnType(),
|
||||
type2.returnType());
|
||||
return new MethodPair(
|
||||
m1.asType(type1.changeReturnType(commonRetType)),
|
||||
m2.asType(type2.changeReturnType(commonRetType)));
|
||||
}
|
||||
|
||||
private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {
|
||||
if(descriptor.getMethodType().parameterCount() != paramCount) {
|
||||
throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
|
||||
}
|
||||
@ -719,7 +755,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
* @return the method handle for retrieving the property, or null if the property does not exist
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private Object getPropertyGetterHandle(Object id) {
|
||||
private Object getPropertyGetterHandle(final Object id) {
|
||||
return propertyGetters.get(id);
|
||||
}
|
||||
|
||||
@ -733,17 +769,20 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
private final MethodHandle getPropertySetterHandle = GET_PROPERTY_SETTER_HANDLE.bindTo(this);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private MethodHandle getPropertySetterHandle(CallSiteDescriptor setterDescriptor, LinkerServices linkerServices,
|
||||
Object id) {
|
||||
private MethodHandle getPropertySetterHandle(final CallSiteDescriptor setterDescriptor, final LinkerServices linkerServices,
|
||||
final Object id) {
|
||||
return getDynamicMethodInvocation(setterDescriptor, linkerServices, String.valueOf(id), propertySetters);
|
||||
}
|
||||
|
||||
private static MethodHandle GET_DYNAMIC_METHOD = MethodHandles.dropArguments(privateLookup.findOwnSpecial(
|
||||
"getDynamicMethod", DynamicMethod.class, Object.class), 1, Object.class);
|
||||
"getDynamicMethod", Object.class, Object.class), 1, Object.class);
|
||||
private final MethodHandle getDynamicMethod = GET_DYNAMIC_METHOD.bindTo(this);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private DynamicMethod getDynamicMethod(Object name) {
|
||||
// This method is marked to return Object instead of DynamicMethod as it's used as a linking component and we don't
|
||||
// want to make the DynamicMethod type observable externally (e.g. as the return type of a MethodHandle returned for
|
||||
// "dyn:getMethod" linking).
|
||||
private Object getDynamicMethod(final Object name) {
|
||||
return getDynamicMethod(String.valueOf(name), methods);
|
||||
}
|
||||
|
||||
@ -754,7 +793,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
* @return the dynamic method (either {@link SimpleDynamicMethod} or {@link OverloadedDynamicMethod}, or null if the
|
||||
* method with the specified name does not exist.
|
||||
*/
|
||||
DynamicMethod getDynamicMethod(String name) {
|
||||
DynamicMethod getDynamicMethod(final String name) {
|
||||
return getDynamicMethod(name, methods);
|
||||
}
|
||||
|
||||
@ -765,16 +804,16 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
* @param getter the getter
|
||||
* @return getter with same name, declared on the most generic superclass/interface of the declaring class
|
||||
*/
|
||||
private static Method getMostGenericGetter(Method getter) {
|
||||
private static Method getMostGenericGetter(final Method getter) {
|
||||
return getMostGenericGetter(getter.getName(), getter.getReturnType(), getter.getDeclaringClass());
|
||||
}
|
||||
|
||||
private static Method getMostGenericGetter(String name, Class<?> returnType, Class<?> declaringClass) {
|
||||
private static Method getMostGenericGetter(final String name, final Class<?> returnType, final Class<?> declaringClass) {
|
||||
if(declaringClass == null) {
|
||||
return null;
|
||||
}
|
||||
// Prefer interfaces
|
||||
for(Class<?> itf: declaringClass.getInterfaces()) {
|
||||
for(final Class<?> itf: declaringClass.getInterfaces()) {
|
||||
final Method itfGetter = getMostGenericGetter(name, returnType, itf);
|
||||
if(itfGetter != null) {
|
||||
return itfGetter;
|
||||
@ -787,7 +826,7 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
if(!CheckRestrictedPackage.isRestrictedClass(declaringClass)) {
|
||||
try {
|
||||
return declaringClass.getMethod(name);
|
||||
} catch(NoSuchMethodException e) {
|
||||
} catch(final NoSuchMethodException e) {
|
||||
// Intentionally ignored, meant to fall through
|
||||
}
|
||||
}
|
||||
@ -798,18 +837,18 @@ abstract class AbstractJavaLinker implements GuardingDynamicLinker {
|
||||
private final SingleDynamicMethod method;
|
||||
/*private*/ final ValidationType validationType;
|
||||
|
||||
AnnotatedDynamicMethod(SingleDynamicMethod method, ValidationType validationType) {
|
||||
AnnotatedDynamicMethod(final SingleDynamicMethod method, final ValidationType validationType) {
|
||||
this.method = method;
|
||||
this.validationType = validationType;
|
||||
}
|
||||
|
||||
MethodHandle getInvocation(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) {
|
||||
MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
|
||||
return method.getInvocation(callSiteDescriptor, linkerServices);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
MethodHandle getTarget(MethodHandles.Lookup lookup) {
|
||||
MethodHandle inv = method.getTarget(lookup);
|
||||
MethodHandle getTarget(final MethodHandles.Lookup lookup) {
|
||||
final MethodHandle inv = method.getTarget(lookup);
|
||||
assert inv != null;
|
||||
return inv;
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ import java.util.Set;
|
||||
class AccessibleMembersLookup {
|
||||
private final Map<MethodSignature, Method> methods;
|
||||
private final Set<Class<?>> innerClasses;
|
||||
private boolean instance;
|
||||
private final boolean instance;
|
||||
|
||||
/**
|
||||
* Creates a mapping for all accessible methods and inner classes on a class.
|
||||
@ -112,7 +112,7 @@ class AccessibleMembersLookup {
|
||||
* @param clazz the inspected class
|
||||
* @param instance true to inspect instance methods, false to inspect static methods.
|
||||
*/
|
||||
AccessibleMembersLookup(final Class<?> clazz, boolean instance) {
|
||||
AccessibleMembersLookup(final Class<?> clazz, final boolean instance) {
|
||||
this.methods = new HashMap<>();
|
||||
this.innerClasses = new LinkedHashSet<>();
|
||||
this.instance = instance;
|
||||
@ -153,7 +153,7 @@ class AccessibleMembersLookup {
|
||||
* @param name the name of the method this signature represents.
|
||||
* @param args the argument types of the method.
|
||||
*/
|
||||
MethodSignature(String name, Class<?>[] args) {
|
||||
MethodSignature(final String name, final Class<?>[] args) {
|
||||
this.name = name;
|
||||
this.args = args;
|
||||
}
|
||||
@ -210,7 +210,7 @@ class AccessibleMembersLookup {
|
||||
|
||||
if(!CheckRestrictedPackage.isRestrictedClass(clazz)) {
|
||||
searchSuperTypes = false;
|
||||
for(Method method: clazz.getMethods()) {
|
||||
for(final Method method: clazz.getMethods()) {
|
||||
final boolean isStatic = Modifier.isStatic(method.getModifiers());
|
||||
if(instance != isStatic) {
|
||||
final MethodSignature sig = new MethodSignature(method);
|
||||
@ -237,7 +237,7 @@ class AccessibleMembersLookup {
|
||||
}
|
||||
}
|
||||
}
|
||||
for(Class<?> innerClass: clazz.getClasses()) {
|
||||
for(final Class<?> innerClass: clazz.getClasses()) {
|
||||
// Add both static and non-static classes, regardless of instance flag. StaticClassLinker will just
|
||||
// expose non-static classes with explicit constructor outer class argument.
|
||||
// NOTE: getting inner class objects through getClasses() does not resolve them, so if those classes
|
||||
|
@ -108,7 +108,7 @@ class ApplicableOverloadedMethods {
|
||||
ApplicableOverloadedMethods(final List<SingleDynamicMethod> methods, final MethodType callSiteType,
|
||||
final ApplicabilityTest test) {
|
||||
this.methods = new LinkedList<>();
|
||||
for(SingleDynamicMethod m: methods) {
|
||||
for(final SingleDynamicMethod m: methods) {
|
||||
if(test.isApplicable(callSiteType, m)) {
|
||||
this.methods.add(m);
|
||||
}
|
||||
@ -143,7 +143,7 @@ class ApplicableOverloadedMethods {
|
||||
*/
|
||||
static final ApplicabilityTest APPLICABLE_BY_SUBTYPING = new ApplicabilityTest() {
|
||||
@Override
|
||||
boolean isApplicable(MethodType callSiteType, SingleDynamicMethod method) {
|
||||
boolean isApplicable(final MethodType callSiteType, final SingleDynamicMethod method) {
|
||||
final MethodType methodType = method.getMethodType();
|
||||
final int methodArity = methodType.parameterCount();
|
||||
if(methodArity != callSiteType.parameterCount()) {
|
||||
@ -165,7 +165,7 @@ class ApplicableOverloadedMethods {
|
||||
*/
|
||||
static final ApplicabilityTest APPLICABLE_BY_METHOD_INVOCATION_CONVERSION = new ApplicabilityTest() {
|
||||
@Override
|
||||
boolean isApplicable(MethodType callSiteType, SingleDynamicMethod method) {
|
||||
boolean isApplicable(final MethodType callSiteType, final SingleDynamicMethod method) {
|
||||
final MethodType methodType = method.getMethodType();
|
||||
final int methodArity = methodType.parameterCount();
|
||||
if(methodArity != callSiteType.parameterCount()) {
|
||||
@ -188,7 +188,7 @@ class ApplicableOverloadedMethods {
|
||||
*/
|
||||
static final ApplicabilityTest APPLICABLE_BY_VARIABLE_ARITY = new ApplicabilityTest() {
|
||||
@Override
|
||||
boolean isApplicable(MethodType callSiteType, SingleDynamicMethod method) {
|
||||
boolean isApplicable(final MethodType callSiteType, final SingleDynamicMethod method) {
|
||||
if(!method.isVarArgs()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
class BeanIntrospector extends FacetIntrospector {
|
||||
BeanIntrospector(Class<?> clazz) {
|
||||
BeanIntrospector(final Class<?> clazz) {
|
||||
super(clazz, true);
|
||||
}
|
||||
|
||||
@ -98,7 +98,7 @@ class BeanIntrospector extends FacetIntrospector {
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle editMethodHandle(MethodHandle mh) {
|
||||
MethodHandle editMethodHandle(final MethodHandle mh) {
|
||||
return mh;
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ import jdk.internal.dynalink.support.TypeUtilities;
|
||||
* @author Attila Szegedi
|
||||
*/
|
||||
class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicLinker {
|
||||
BeanLinker(Class<?> clazz) {
|
||||
BeanLinker(final Class<?> clazz) {
|
||||
super(clazz, Guards.getClassGuard(clazz), Guards.getInstanceOfGuard(clazz));
|
||||
if(clazz.isArray()) {
|
||||
// Some languages won't have a notion of manipulating collections. Exposing "length" on arrays as an
|
||||
@ -119,7 +119,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canLinkType(Class<?> type) {
|
||||
public boolean canLinkType(final Class<?> type) {
|
||||
return type == clazz;
|
||||
}
|
||||
|
||||
@ -129,8 +129,8 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuardedInvocationComponent getGuardedInvocationComponent(CallSiteDescriptor callSiteDescriptor,
|
||||
LinkerServices linkerServices, List<String> operations) throws Exception {
|
||||
protected GuardedInvocationComponent getGuardedInvocationComponent(final CallSiteDescriptor callSiteDescriptor,
|
||||
final LinkerServices linkerServices, final List<String> operations) throws Exception {
|
||||
final GuardedInvocationComponent superGic = super.getGuardedInvocationComponent(callSiteDescriptor,
|
||||
linkerServices, operations);
|
||||
if(superGic != null) {
|
||||
@ -166,7 +166,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
private static MethodHandle MAP_GUARD = Guards.getInstanceOfGuard(Map.class);
|
||||
|
||||
private GuardedInvocationComponent getElementGetter(final CallSiteDescriptor callSiteDescriptor,
|
||||
final LinkerServices linkerServices, List<String> operations) throws Exception {
|
||||
final LinkerServices linkerServices, final List<String> operations) throws Exception {
|
||||
final MethodType callSiteType = callSiteDescriptor.getMethodType();
|
||||
final Class<?> declaredType = callSiteType.parameterType(0);
|
||||
final GuardedInvocationComponent nextComponent = getGuardedInvocationComponent(callSiteDescriptor,
|
||||
@ -237,8 +237,9 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
} else {
|
||||
checkGuard = convertArgToInt(RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
|
||||
}
|
||||
return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
|
||||
binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
|
||||
final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
|
||||
nextComponent.getGuardedInvocation().getInvocation());
|
||||
return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
|
||||
gic.getValidatorClass(), gic.getValidationType());
|
||||
}
|
||||
|
||||
@ -247,7 +248,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
CallSiteDescriptor.NAME_OPERAND);
|
||||
}
|
||||
|
||||
private static Object convertKeyToInteger(String fixedKey, LinkerServices linkerServices) throws Exception {
|
||||
private static Object convertKeyToInteger(final String fixedKey, final LinkerServices linkerServices) throws Exception {
|
||||
try {
|
||||
if(linkerServices.canConvert(String.class, Number.class)) {
|
||||
try {
|
||||
@ -267,18 +268,18 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
return Integer.valueOf(intIndex);
|
||||
} catch(Exception|Error e) {
|
||||
throw e;
|
||||
} catch(Throwable t) {
|
||||
} catch(final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
return Integer.valueOf(fixedKey);
|
||||
} catch(NumberFormatException e) {
|
||||
} catch(final NumberFormatException e) {
|
||||
// key is not a number
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static MethodHandle convertArgToInt(MethodHandle mh, LinkerServices ls, CallSiteDescriptor desc) {
|
||||
private static MethodHandle convertArgToInt(final MethodHandle mh, final LinkerServices ls, final CallSiteDescriptor desc) {
|
||||
final Class<?> sourceType = desc.getMethodType().parameterType(1);
|
||||
if(TypeUtilities.isMethodInvocationConvertible(sourceType, Number.class)) {
|
||||
return mh;
|
||||
@ -301,21 +302,21 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
private final MethodType methodType;
|
||||
private final Object fixedKey;
|
||||
|
||||
Binder(LinkerServices linkerServices, MethodType methodType, Object fixedKey) {
|
||||
Binder(final LinkerServices linkerServices, final MethodType methodType, final Object fixedKey) {
|
||||
this.linkerServices = linkerServices;
|
||||
this.methodType = fixedKey == null ? methodType : methodType.insertParameterTypes(1, fixedKey.getClass());
|
||||
this.fixedKey = fixedKey;
|
||||
}
|
||||
|
||||
/*private*/ MethodHandle bind(MethodHandle handle) {
|
||||
return bindToFixedKey(linkerServices.asType(handle, methodType));
|
||||
/*private*/ MethodHandle bind(final MethodHandle handle) {
|
||||
return bindToFixedKey(linkerServices.asTypeLosslessReturn(handle, methodType));
|
||||
}
|
||||
|
||||
/*private*/ MethodHandle bindTest(MethodHandle handle) {
|
||||
/*private*/ MethodHandle bindTest(final MethodHandle handle) {
|
||||
return bindToFixedKey(Guards.asType(handle, methodType));
|
||||
}
|
||||
|
||||
private MethodHandle bindToFixedKey(MethodHandle handle) {
|
||||
private MethodHandle bindToFixedKey(final MethodHandle handle) {
|
||||
return fixedKey == null ? handle : MethodHandles.insertArguments(handle, 1, fixedKey);
|
||||
}
|
||||
}
|
||||
@ -325,12 +326,12 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
private static MethodHandle CONTAINS_MAP = Lookup.PUBLIC.findVirtual(Map.class, "containsKey",
|
||||
MethodType.methodType(boolean.class, Object.class));
|
||||
|
||||
private static MethodHandle findRangeCheck(Class<?> collectionType) {
|
||||
private static MethodHandle findRangeCheck(final Class<?> collectionType) {
|
||||
return Lookup.findOwnStatic(MethodHandles.lookup(), "rangeCheck", boolean.class, collectionType, Object.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final boolean rangeCheck(Object array, Object index) {
|
||||
private static final boolean rangeCheck(final Object array, final Object index) {
|
||||
if(!(index instanceof Number)) {
|
||||
return false;
|
||||
}
|
||||
@ -347,7 +348,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final boolean rangeCheck(List<?> list, Object index) {
|
||||
private static final boolean rangeCheck(final List<?> list, final Object index) {
|
||||
if(!(index instanceof Number)) {
|
||||
return false;
|
||||
}
|
||||
@ -369,8 +370,8 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
private static MethodHandle PUT_MAP_ELEMENT = Lookup.PUBLIC.findVirtual(Map.class, "put",
|
||||
MethodType.methodType(Object.class, Object.class, Object.class));
|
||||
|
||||
private GuardedInvocationComponent getElementSetter(CallSiteDescriptor callSiteDescriptor,
|
||||
LinkerServices linkerServices, List<String> operations) throws Exception {
|
||||
private GuardedInvocationComponent getElementSetter(final CallSiteDescriptor callSiteDescriptor,
|
||||
final LinkerServices linkerServices, final List<String> operations) throws Exception {
|
||||
final MethodType callSiteType = callSiteDescriptor.getMethodType();
|
||||
final Class<?> declaredType = callSiteType.parameterType(0);
|
||||
|
||||
@ -440,8 +441,9 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
|
||||
final MethodHandle checkGuard = convertArgToInt(invocation == SET_LIST_ELEMENT ? RANGE_CHECK_LIST :
|
||||
RANGE_CHECK_ARRAY, linkerServices, callSiteDescriptor);
|
||||
return nextComponent.compose(MethodHandles.guardWithTest(binder.bindTest(checkGuard),
|
||||
binder.bind(invocation), nextComponent.getGuardedInvocation().getInvocation()), gi.getGuard(),
|
||||
final MethodPair matchedInvocations = matchReturnTypes(binder.bind(invocation),
|
||||
nextComponent.getGuardedInvocation().getInvocation());
|
||||
return nextComponent.compose(matchedInvocations.guardWithTest(binder.bindTest(checkGuard)), gi.getGuard(),
|
||||
gic.getValidatorClass(), gic.getValidationType());
|
||||
}
|
||||
|
||||
@ -456,7 +458,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
|
||||
private static MethodHandle COLLECTION_GUARD = Guards.getInstanceOfGuard(Collection.class);
|
||||
|
||||
private GuardedInvocationComponent getLengthGetter(CallSiteDescriptor callSiteDescriptor) {
|
||||
private GuardedInvocationComponent getLengthGetter(final CallSiteDescriptor callSiteDescriptor) {
|
||||
assertParameterCount(callSiteDescriptor, 1);
|
||||
final MethodType callSiteType = callSiteDescriptor.getMethodType();
|
||||
final Class<?> declaredType = callSiteType.parameterType(0);
|
||||
@ -486,7 +488,7 @@ class BeanLinker extends AbstractJavaLinker implements TypeBasedGuardingDynamicL
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void assertParameterCount(CallSiteDescriptor descriptor, int paramCount) {
|
||||
private static void assertParameterCount(final CallSiteDescriptor descriptor, final int paramCount) {
|
||||
if(descriptor.getMethodType().parameterCount() != paramCount) {
|
||||
throw new BootstrapMethodError(descriptor.getName() + " must have exactly " + paramCount + " parameters.");
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ import jdk.internal.dynalink.linker.TypeBasedGuardingDynamicLinker;
|
||||
public class BeansLinker implements GuardingDynamicLinker {
|
||||
private static final ClassValue<TypeBasedGuardingDynamicLinker> linkers = new ClassValue<TypeBasedGuardingDynamicLinker>() {
|
||||
@Override
|
||||
protected TypeBasedGuardingDynamicLinker computeValue(Class<?> clazz) {
|
||||
protected TypeBasedGuardingDynamicLinker computeValue(final Class<?> clazz) {
|
||||
// If ClassValue.put() were public, we could just pre-populate with these known mappings...
|
||||
return
|
||||
clazz == Class.class ? new ClassLinker() :
|
||||
@ -154,7 +154,7 @@ public class BeansLinker implements GuardingDynamicLinker {
|
||||
* @param clazz the class
|
||||
* @return a bean linker for that class
|
||||
*/
|
||||
public static TypeBasedGuardingDynamicLinker getLinkerForClass(Class<?> clazz) {
|
||||
public static TypeBasedGuardingDynamicLinker getLinkerForClass(final Class<?> clazz) {
|
||||
return linkers.get(clazz);
|
||||
}
|
||||
|
||||
@ -173,8 +173,8 @@ public class BeansLinker implements GuardingDynamicLinker {
|
||||
* @param clazz the class
|
||||
* @return a collection of names of all readable instance properties of a class.
|
||||
*/
|
||||
public static Collection<String> getReadableInstancePropertyNames(Class<?> clazz) {
|
||||
TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
|
||||
public static Collection<String> getReadableInstancePropertyNames(final Class<?> clazz) {
|
||||
final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
|
||||
if(linker instanceof BeanLinker) {
|
||||
return ((BeanLinker)linker).getReadablePropertyNames();
|
||||
}
|
||||
@ -186,8 +186,8 @@ public class BeansLinker implements GuardingDynamicLinker {
|
||||
* @param clazz the class
|
||||
* @return a collection of names of all writable instance properties of a class.
|
||||
*/
|
||||
public static Collection<String> getWritableInstancePropertyNames(Class<?> clazz) {
|
||||
TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
|
||||
public static Collection<String> getWritableInstancePropertyNames(final Class<?> clazz) {
|
||||
final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
|
||||
if(linker instanceof BeanLinker) {
|
||||
return ((BeanLinker)linker).getWritablePropertyNames();
|
||||
}
|
||||
@ -199,8 +199,8 @@ public class BeansLinker implements GuardingDynamicLinker {
|
||||
* @param clazz the class
|
||||
* @return a collection of names of all instance methods of a class.
|
||||
*/
|
||||
public static Collection<String> getInstanceMethodNames(Class<?> clazz) {
|
||||
TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
|
||||
public static Collection<String> getInstanceMethodNames(final Class<?> clazz) {
|
||||
final TypeBasedGuardingDynamicLinker linker = getLinkerForClass(clazz);
|
||||
if(linker instanceof BeanLinker) {
|
||||
return ((BeanLinker)linker).getMethodNames();
|
||||
}
|
||||
@ -212,7 +212,7 @@ public class BeansLinker implements GuardingDynamicLinker {
|
||||
* @param clazz the class
|
||||
* @return a collection of names of all readable static properties of a class.
|
||||
*/
|
||||
public static Collection<String> getReadableStaticPropertyNames(Class<?> clazz) {
|
||||
public static Collection<String> getReadableStaticPropertyNames(final Class<?> clazz) {
|
||||
return StaticClassLinker.getReadableStaticPropertyNames(clazz);
|
||||
}
|
||||
|
||||
@ -221,7 +221,7 @@ public class BeansLinker implements GuardingDynamicLinker {
|
||||
* @param clazz the class
|
||||
* @return a collection of names of all writable static properties of a class.
|
||||
*/
|
||||
public static Collection<String> getWritableStaticPropertyNames(Class<?> clazz) {
|
||||
public static Collection<String> getWritableStaticPropertyNames(final Class<?> clazz) {
|
||||
return StaticClassLinker.getWritableStaticPropertyNames(clazz);
|
||||
}
|
||||
|
||||
@ -230,12 +230,12 @@ public class BeansLinker implements GuardingDynamicLinker {
|
||||
* @param clazz the class
|
||||
* @return a collection of names of all static methods of a class.
|
||||
*/
|
||||
public static Collection<String> getStaticMethodNames(Class<?> clazz) {
|
||||
public static Collection<String> getStaticMethodNames(final Class<?> clazz) {
|
||||
return StaticClassLinker.getStaticMethodNames(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation getGuardedInvocation(LinkRequest request, final LinkerServices linkerServices)
|
||||
public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)
|
||||
throws Exception {
|
||||
final CallSiteDescriptor callSiteDescriptor = request.getCallSiteDescriptor();
|
||||
final int l = callSiteDescriptor.getNameTokenCount();
|
||||
|
@ -107,14 +107,14 @@ public class CallerSensitiveDetector {
|
||||
|
||||
private static final DetectionStrategy DETECTION_STRATEGY = getDetectionStrategy();
|
||||
|
||||
static boolean isCallerSensitive(AccessibleObject ao) {
|
||||
static boolean isCallerSensitive(final AccessibleObject ao) {
|
||||
return DETECTION_STRATEGY.isCallerSensitive(ao);
|
||||
}
|
||||
|
||||
private static DetectionStrategy getDetectionStrategy() {
|
||||
try {
|
||||
return new PrivilegedDetectionStrategy();
|
||||
} catch(Throwable t) {
|
||||
} catch(final Throwable t) {
|
||||
return new UnprivilegedDetectionStrategy();
|
||||
}
|
||||
}
|
||||
@ -127,7 +127,7 @@ public class CallerSensitiveDetector {
|
||||
private static final Class<? extends Annotation> CALLER_SENSITIVE_ANNOTATION_CLASS = CallerSensitive.class;
|
||||
|
||||
@Override
|
||||
boolean isCallerSensitive(AccessibleObject ao) {
|
||||
boolean isCallerSensitive(final AccessibleObject ao) {
|
||||
return ao.getAnnotation(CALLER_SENSITIVE_ANNOTATION_CLASS) != null;
|
||||
}
|
||||
}
|
||||
@ -136,8 +136,8 @@ public class CallerSensitiveDetector {
|
||||
private static final String CALLER_SENSITIVE_ANNOTATION_STRING = "@sun.reflect.CallerSensitive()";
|
||||
|
||||
@Override
|
||||
boolean isCallerSensitive(AccessibleObject o) {
|
||||
for(Annotation a: o.getAnnotations()) {
|
||||
boolean isCallerSensitive(final AccessibleObject o) {
|
||||
for(final Annotation a: o.getAnnotations()) {
|
||||
if(String.valueOf(a).equals(CALLER_SENSITIVE_ANNOTATION_STRING)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -107,13 +107,13 @@ class CallerSensitiveDynamicMethod extends SingleDynamicMethod {
|
||||
private final AccessibleObject target;
|
||||
private final MethodType type;
|
||||
|
||||
public CallerSensitiveDynamicMethod(AccessibleObject target) {
|
||||
public CallerSensitiveDynamicMethod(final AccessibleObject target) {
|
||||
super(getName(target));
|
||||
this.target = target;
|
||||
this.type = getMethodType(target);
|
||||
}
|
||||
|
||||
private static String getName(AccessibleObject target) {
|
||||
private static String getName(final AccessibleObject target) {
|
||||
final Member m = (Member)target;
|
||||
return getMethodNameWithSignature(getMethodType(target), getClassAndMethodName(m.getDeclaringClass(),
|
||||
m.getName()));
|
||||
@ -124,7 +124,7 @@ class CallerSensitiveDynamicMethod extends SingleDynamicMethod {
|
||||
return type;
|
||||
}
|
||||
|
||||
private static MethodType getMethodType(AccessibleObject ao) {
|
||||
private static MethodType getMethodType(final AccessibleObject ao) {
|
||||
final boolean isMethod = ao instanceof Method;
|
||||
final Class<?> rtype = isMethod ? ((Method)ao).getReturnType() : ((Constructor<?>)ao).getDeclaringClass();
|
||||
final Class<?>[] ptypes = isMethod ? ((Method)ao).getParameterTypes() : ((Constructor<?>)ao).getParameterTypes();
|
||||
@ -144,7 +144,7 @@ class CallerSensitiveDynamicMethod extends SingleDynamicMethod {
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle getTarget(MethodHandles.Lookup lookup) {
|
||||
MethodHandle getTarget(final MethodHandles.Lookup lookup) {
|
||||
if(target instanceof Method) {
|
||||
final MethodHandle mh = Lookup.unreflect(lookup, (Method)target);
|
||||
if(Modifier.isStatic(((Member)target).getModifiers())) {
|
||||
|
@ -101,7 +101,7 @@ class CheckRestrictedPackage {
|
||||
* @param clazz the class to test
|
||||
* @return true if the class is either not public, or it resides in a package with restricted access.
|
||||
*/
|
||||
static boolean isRestrictedClass(Class<?> clazz) {
|
||||
static boolean isRestrictedClass(final Class<?> clazz) {
|
||||
if(!Modifier.isPublic(clazz.getModifiers())) {
|
||||
// Non-public classes are always restricted
|
||||
return true;
|
||||
@ -126,7 +126,7 @@ class CheckRestrictedPackage {
|
||||
return null;
|
||||
}
|
||||
}, NO_PERMISSIONS_CONTEXT);
|
||||
} catch(SecurityException e) {
|
||||
} catch(final SecurityException e) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -104,16 +104,16 @@ final class ClassString {
|
||||
private final Class<?>[] classes;
|
||||
private int hashCode;
|
||||
|
||||
ClassString(Class<?>[] classes) {
|
||||
ClassString(final Class<?>[] classes) {
|
||||
this.classes = classes;
|
||||
}
|
||||
|
||||
ClassString(MethodType type) {
|
||||
ClassString(final MethodType type) {
|
||||
this(type.parameterArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
public boolean equals(final Object other) {
|
||||
if(!(other instanceof ClassString)) {
|
||||
return false;
|
||||
}
|
||||
@ -150,7 +150,7 @@ final class ClassString {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<MethodHandle> getMaximallySpecifics(List<MethodHandle> methods, LinkerServices linkerServices, boolean varArg) {
|
||||
List<MethodHandle> getMaximallySpecifics(final List<MethodHandle> methods, final LinkerServices linkerServices, final boolean varArg) {
|
||||
return MaximallySpecific.getMaximallySpecificMethodHandles(getApplicables(methods, linkerServices, varArg),
|
||||
varArg, classes, linkerServices);
|
||||
}
|
||||
@ -158,7 +158,7 @@ final class ClassString {
|
||||
/**
|
||||
* Returns all methods that are applicable to actual parameter classes represented by this ClassString object.
|
||||
*/
|
||||
LinkedList<MethodHandle> getApplicables(List<MethodHandle> methods, LinkerServices linkerServices, boolean varArg) {
|
||||
LinkedList<MethodHandle> getApplicables(final List<MethodHandle> methods, final LinkerServices linkerServices, final boolean varArg) {
|
||||
final LinkedList<MethodHandle> list = new LinkedList<>();
|
||||
for(final MethodHandle member: methods) {
|
||||
if(isApplicable(member, linkerServices, varArg)) {
|
||||
@ -173,7 +173,7 @@ final class ClassString {
|
||||
* object.
|
||||
*
|
||||
*/
|
||||
private boolean isApplicable(MethodHandle method, LinkerServices linkerServices, boolean varArg) {
|
||||
private boolean isApplicable(final MethodHandle method, final LinkerServices linkerServices, final boolean varArg) {
|
||||
final Class<?>[] formalTypes = method.type().parameterArray();
|
||||
final int cl = classes.length;
|
||||
final int fl = formalTypes.length - (varArg ? 1 : 0);
|
||||
@ -203,7 +203,7 @@ final class ClassString {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean canConvert(LinkerServices ls, Class<?> from, Class<?> to) {
|
||||
private static boolean canConvert(final LinkerServices ls, final Class<?> from, final Class<?> to) {
|
||||
if(from == NULL_CLASS) {
|
||||
return !to.isPrimitive();
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ import jdk.internal.dynalink.linker.LinkerServices;
|
||||
abstract class DynamicMethod {
|
||||
private final String name;
|
||||
|
||||
DynamicMethod(String name) {
|
||||
DynamicMethod(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ abstract class DynamicMethod {
|
||||
*/
|
||||
abstract boolean contains(SingleDynamicMethod method);
|
||||
|
||||
static String getClassAndMethodName(Class<?> clazz, String name) {
|
||||
static String getClassAndMethodName(final Class<?> clazz, final String name) {
|
||||
final String clazzName = clazz.getCanonicalName();
|
||||
return (clazzName == null ? clazz.getName() : clazzName) + "." + name;
|
||||
}
|
||||
|
@ -99,12 +99,12 @@ import jdk.internal.dynalink.support.Guards;
|
||||
*/
|
||||
class DynamicMethodLinker implements TypeBasedGuardingDynamicLinker {
|
||||
@Override
|
||||
public boolean canLinkType(Class<?> type) {
|
||||
public boolean canLinkType(final Class<?> type) {
|
||||
return DynamicMethod.class.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, LinkerServices linkerServices) {
|
||||
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) {
|
||||
final Object receiver = linkRequest.getReceiver();
|
||||
if(!(receiver instanceof DynamicMethod)) {
|
||||
return null;
|
||||
|
@ -106,7 +106,7 @@ abstract class FacetIntrospector {
|
||||
|
||||
protected final AccessibleMembersLookup membersLookup;
|
||||
|
||||
FacetIntrospector(Class<?> clazz, boolean instance) {
|
||||
FacetIntrospector(final Class<?> clazz, final boolean instance) {
|
||||
this.clazz = clazz;
|
||||
this.instance = instance;
|
||||
isRestricted = CheckRestrictedPackage.isRestrictedClass(clazz);
|
||||
@ -135,7 +135,7 @@ abstract class FacetIntrospector {
|
||||
|
||||
final Field[] fields = clazz.getFields();
|
||||
final Collection<Field> cfields = new ArrayList<>(fields.length);
|
||||
for(Field field: fields) {
|
||||
for(final Field field: fields) {
|
||||
final boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
if(isStatic && clazz != field.getDeclaringClass()) {
|
||||
// ignore inherited static fields
|
||||
@ -149,7 +149,7 @@ abstract class FacetIntrospector {
|
||||
return cfields;
|
||||
}
|
||||
|
||||
boolean isAccessible(Member m) {
|
||||
boolean isAccessible(final Member m) {
|
||||
final Class<?> declaring = m.getDeclaringClass();
|
||||
// (declaring == clazz) is just an optimization - we're calling this only from code that operates on a
|
||||
// non-restriced class, so if the declaring class is identical to the class being inspected, then forego
|
||||
@ -166,11 +166,11 @@ abstract class FacetIntrospector {
|
||||
}
|
||||
|
||||
|
||||
MethodHandle unreflectGetter(Field field) {
|
||||
MethodHandle unreflectGetter(final Field field) {
|
||||
return editMethodHandle(Lookup.PUBLIC.unreflectGetter(field));
|
||||
}
|
||||
|
||||
MethodHandle unreflectSetter(Field field) {
|
||||
MethodHandle unreflectSetter(final Field field) {
|
||||
return editMethodHandle(Lookup.PUBLIC.unreflectSetter(field));
|
||||
}
|
||||
|
||||
|
@ -105,38 +105,38 @@ class GuardedInvocationComponent {
|
||||
private final GuardedInvocation guardedInvocation;
|
||||
private final Validator validator;
|
||||
|
||||
GuardedInvocationComponent(MethodHandle invocation) {
|
||||
GuardedInvocationComponent(final MethodHandle invocation) {
|
||||
this(invocation, null, ValidationType.NONE);
|
||||
}
|
||||
|
||||
GuardedInvocationComponent(MethodHandle invocation, MethodHandle guard, ValidationType validationType) {
|
||||
GuardedInvocationComponent(final MethodHandle invocation, final MethodHandle guard, final ValidationType validationType) {
|
||||
this(invocation, guard, null, validationType);
|
||||
}
|
||||
|
||||
GuardedInvocationComponent(MethodHandle invocation, MethodHandle guard, Class<?> validatorClass,
|
||||
ValidationType validationType) {
|
||||
GuardedInvocationComponent(final MethodHandle invocation, final MethodHandle guard, final Class<?> validatorClass,
|
||||
final ValidationType validationType) {
|
||||
this(invocation, guard, new Validator(validatorClass, validationType));
|
||||
}
|
||||
|
||||
GuardedInvocationComponent(GuardedInvocation guardedInvocation, Class<?> validatorClass,
|
||||
ValidationType validationType) {
|
||||
GuardedInvocationComponent(final GuardedInvocation guardedInvocation, final Class<?> validatorClass,
|
||||
final ValidationType validationType) {
|
||||
this(guardedInvocation, new Validator(validatorClass, validationType));
|
||||
}
|
||||
|
||||
GuardedInvocationComponent replaceInvocation(MethodHandle newInvocation) {
|
||||
GuardedInvocationComponent replaceInvocation(final MethodHandle newInvocation) {
|
||||
return replaceInvocation(newInvocation, guardedInvocation.getGuard());
|
||||
}
|
||||
|
||||
GuardedInvocationComponent replaceInvocation(MethodHandle newInvocation, MethodHandle newGuard) {
|
||||
GuardedInvocationComponent replaceInvocation(final MethodHandle newInvocation, final MethodHandle newGuard) {
|
||||
return new GuardedInvocationComponent(guardedInvocation.replaceMethods(newInvocation,
|
||||
newGuard), validator);
|
||||
}
|
||||
|
||||
private GuardedInvocationComponent(MethodHandle invocation, MethodHandle guard, Validator validator) {
|
||||
private GuardedInvocationComponent(final MethodHandle invocation, final MethodHandle guard, final Validator validator) {
|
||||
this(new GuardedInvocation(invocation, guard), validator);
|
||||
}
|
||||
|
||||
private GuardedInvocationComponent(GuardedInvocation guardedInvocation, Validator validator) {
|
||||
private GuardedInvocationComponent(final GuardedInvocation guardedInvocation, final Validator validator) {
|
||||
this.guardedInvocation = guardedInvocation;
|
||||
this.validator = validator;
|
||||
}
|
||||
@ -153,8 +153,8 @@ class GuardedInvocationComponent {
|
||||
return validator.validationType;
|
||||
}
|
||||
|
||||
GuardedInvocationComponent compose(MethodHandle compositeInvocation, MethodHandle otherGuard,
|
||||
Class<?> otherValidatorClass, ValidationType otherValidationType) {
|
||||
GuardedInvocationComponent compose(final MethodHandle compositeInvocation, final MethodHandle otherGuard,
|
||||
final Class<?> otherValidatorClass, final ValidationType otherValidationType) {
|
||||
final Validator compositeValidator = validator.compose(new Validator(otherValidatorClass, otherValidationType));
|
||||
final MethodHandle compositeGuard = compositeValidator == validator ? guardedInvocation.getGuard() : otherGuard;
|
||||
return new GuardedInvocationComponent(compositeInvocation, compositeGuard, compositeValidator);
|
||||
@ -164,12 +164,12 @@ class GuardedInvocationComponent {
|
||||
/*private*/ final Class<?> validatorClass;
|
||||
/*private*/ final ValidationType validationType;
|
||||
|
||||
Validator(Class<?> validatorClass, ValidationType validationType) {
|
||||
Validator(final Class<?> validatorClass, final ValidationType validationType) {
|
||||
this.validatorClass = validatorClass;
|
||||
this.validationType = validationType;
|
||||
}
|
||||
|
||||
Validator compose(Validator other) {
|
||||
Validator compose(final Validator other) {
|
||||
if(other.validationType == ValidationType.NONE) {
|
||||
return this;
|
||||
}
|
||||
@ -240,7 +240,7 @@ class GuardedInvocationComponent {
|
||||
throw new AssertionError("Incompatible composition " + this + " vs " + other);
|
||||
}
|
||||
|
||||
private boolean isAssignableFrom(Validator other) {
|
||||
private boolean isAssignableFrom(final Validator other) {
|
||||
return validatorClass.isAssignableFrom(other.validatorClass);
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ class MaximallySpecific {
|
||||
* @param varArgs whether to assume the methods are varargs
|
||||
* @return the list of maximally specific methods.
|
||||
*/
|
||||
static List<SingleDynamicMethod> getMaximallySpecificMethods(List<SingleDynamicMethod> methods, boolean varArgs) {
|
||||
static List<SingleDynamicMethod> getMaximallySpecificMethods(final List<SingleDynamicMethod> methods, final boolean varArgs) {
|
||||
return getMaximallySpecificSingleDynamicMethods(methods, varArgs, null, null);
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ class MaximallySpecific {
|
||||
private static final MethodTypeGetter<MethodHandle> METHOD_HANDLE_TYPE_GETTER =
|
||||
new MethodTypeGetter<MethodHandle>() {
|
||||
@Override
|
||||
MethodType getMethodType(MethodHandle t) {
|
||||
MethodType getMethodType(final MethodHandle t) {
|
||||
return t.type();
|
||||
}
|
||||
};
|
||||
@ -124,7 +124,7 @@ class MaximallySpecific {
|
||||
private static final MethodTypeGetter<SingleDynamicMethod> DYNAMIC_METHOD_TYPE_GETTER =
|
||||
new MethodTypeGetter<SingleDynamicMethod>() {
|
||||
@Override
|
||||
MethodType getMethodType(SingleDynamicMethod t) {
|
||||
MethodType getMethodType(final SingleDynamicMethod t) {
|
||||
return t.getMethodType();
|
||||
}
|
||||
};
|
||||
@ -138,8 +138,8 @@ class MaximallySpecific {
|
||||
* @param argTypes concrete argument types for the invocation
|
||||
* @return the list of maximally specific method handles.
|
||||
*/
|
||||
static List<MethodHandle> getMaximallySpecificMethodHandles(List<MethodHandle> methods, boolean varArgs,
|
||||
Class<?>[] argTypes, LinkerServices ls) {
|
||||
static List<MethodHandle> getMaximallySpecificMethodHandles(final List<MethodHandle> methods, final boolean varArgs,
|
||||
final Class<?>[] argTypes, final LinkerServices ls) {
|
||||
return getMaximallySpecificMethods(methods, varArgs, argTypes, ls, METHOD_HANDLE_TYPE_GETTER);
|
||||
}
|
||||
|
||||
@ -152,8 +152,8 @@ class MaximallySpecific {
|
||||
* @param argTypes concrete argument types for the invocation
|
||||
* @return the list of maximally specific methods.
|
||||
*/
|
||||
static List<SingleDynamicMethod> getMaximallySpecificSingleDynamicMethods(List<SingleDynamicMethod> methods,
|
||||
boolean varArgs, Class<?>[] argTypes, LinkerServices ls) {
|
||||
static List<SingleDynamicMethod> getMaximallySpecificSingleDynamicMethods(final List<SingleDynamicMethod> methods,
|
||||
final boolean varArgs, final Class<?>[] argTypes, final LinkerServices ls) {
|
||||
return getMaximallySpecificMethods(methods, varArgs, argTypes, ls, DYNAMIC_METHOD_TYPE_GETTER);
|
||||
}
|
||||
|
||||
@ -166,16 +166,16 @@ class MaximallySpecific {
|
||||
* @param argTypes concrete argument types for the invocation
|
||||
* @return the list of maximally specific methods.
|
||||
*/
|
||||
private static <T> List<T> getMaximallySpecificMethods(List<T> methods, boolean varArgs,
|
||||
Class<?>[] argTypes, LinkerServices ls, MethodTypeGetter<T> methodTypeGetter) {
|
||||
private static <T> List<T> getMaximallySpecificMethods(final List<T> methods, final boolean varArgs,
|
||||
final Class<?>[] argTypes, final LinkerServices ls, final MethodTypeGetter<T> methodTypeGetter) {
|
||||
if(methods.size() < 2) {
|
||||
return methods;
|
||||
}
|
||||
final LinkedList<T> maximals = new LinkedList<>();
|
||||
for(T m: methods) {
|
||||
for(final T m: methods) {
|
||||
final MethodType methodType = methodTypeGetter.getMethodType(m);
|
||||
boolean lessSpecific = false;
|
||||
for(Iterator<T> maximal = maximals.iterator(); maximal.hasNext();) {
|
||||
for(final Iterator<T> maximal = maximals.iterator(); maximal.hasNext();) {
|
||||
final T max = maximal.next();
|
||||
switch(isMoreSpecific(methodType, methodTypeGetter.getMethodType(max), varArgs, argTypes, ls)) {
|
||||
case TYPE_1_BETTER: {
|
||||
@ -202,8 +202,8 @@ class MaximallySpecific {
|
||||
return maximals;
|
||||
}
|
||||
|
||||
private static Comparison isMoreSpecific(MethodType t1, MethodType t2, boolean varArgs, Class<?>[] argTypes,
|
||||
LinkerServices ls) {
|
||||
private static Comparison isMoreSpecific(final MethodType t1, final MethodType t2, final boolean varArgs, final Class<?>[] argTypes,
|
||||
final LinkerServices ls) {
|
||||
final int pc1 = t1.parameterCount();
|
||||
final int pc2 = t2.parameterCount();
|
||||
assert varArgs || (pc1 == pc2) && (argTypes == null || argTypes.length == pc1);
|
||||
@ -241,7 +241,7 @@ class MaximallySpecific {
|
||||
return Comparison.INDETERMINATE;
|
||||
}
|
||||
|
||||
private static Comparison compare(Class<?> c1, Class<?> c2, Class<?>[] argTypes, int i, LinkerServices cmp) {
|
||||
private static Comparison compare(final Class<?> c1, final Class<?> c2, final Class<?>[] argTypes, final int i, final LinkerServices cmp) {
|
||||
if(cmp != null) {
|
||||
final Comparison c = cmp.compareConversion(argTypes[i], c1, c2);
|
||||
if(c != Comparison.INDETERMINATE) {
|
||||
@ -256,7 +256,7 @@ class MaximallySpecific {
|
||||
return Comparison.INDETERMINATE;
|
||||
}
|
||||
|
||||
private static Class<?> getParameterClass(MethodType t, int l, int i, boolean varArgs) {
|
||||
private static Class<?> getParameterClass(final MethodType t, final int l, final int i, final boolean varArgs) {
|
||||
return varArgs && i >= l - 1 ? t.parameterType(l - 1).getComponentType() : t.parameterType(i);
|
||||
}
|
||||
}
|
||||
|
@ -115,20 +115,20 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
||||
* @param clazz the class this method belongs to
|
||||
* @param name the name of the method
|
||||
*/
|
||||
OverloadedDynamicMethod(Class<?> clazz, String name) {
|
||||
OverloadedDynamicMethod(final Class<?> clazz, final String name) {
|
||||
this(new LinkedList<SingleDynamicMethod>(), clazz.getClassLoader(), getClassAndMethodName(clazz, name));
|
||||
}
|
||||
|
||||
private OverloadedDynamicMethod(LinkedList<SingleDynamicMethod> methods, ClassLoader classLoader, String name) {
|
||||
private OverloadedDynamicMethod(final LinkedList<SingleDynamicMethod> methods, final ClassLoader classLoader, final String name) {
|
||||
super(name);
|
||||
this.methods = methods;
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
SingleDynamicMethod getMethodForExactParamTypes(String paramTypes) {
|
||||
SingleDynamicMethod getMethodForExactParamTypes(final String paramTypes) {
|
||||
final LinkedList<SingleDynamicMethod> matchingMethods = new LinkedList<>();
|
||||
for(SingleDynamicMethod method: methods) {
|
||||
for(final SingleDynamicMethod method: methods) {
|
||||
final SingleDynamicMethod matchingMethod = method.getMethodForExactParamTypes(paramTypes);
|
||||
if(matchingMethod != null) {
|
||||
matchingMethods.add(matchingMethod);
|
||||
@ -148,7 +148,6 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("fallthrough")
|
||||
@Override
|
||||
public MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
|
||||
final MethodType callSiteType = callSiteDescriptor.getMethodType();
|
||||
@ -207,7 +206,7 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
||||
case 1: {
|
||||
// Very lucky, we ended up with a single candidate method handle based on the call site signature; we
|
||||
// can link it very simply by delegating to the SingleDynamicMethod.
|
||||
invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
|
||||
return invokables.iterator().next().getInvocation(callSiteDescriptor, linkerServices);
|
||||
}
|
||||
default: {
|
||||
// We have more than one candidate. We have no choice but to link to a method that resolves overloads on
|
||||
@ -218,7 +217,7 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
||||
// has an already determined Lookup.
|
||||
final List<MethodHandle> methodHandles = new ArrayList<>(invokables.size());
|
||||
final MethodHandles.Lookup lookup = callSiteDescriptor.getLookup();
|
||||
for(SingleDynamicMethod method: invokables) {
|
||||
for(final SingleDynamicMethod method: invokables) {
|
||||
methodHandles.add(method.getTarget(lookup));
|
||||
}
|
||||
return new OverloadedMethod(methodHandles, this, callSiteType, linkerServices).getInvoker();
|
||||
@ -228,8 +227,8 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(SingleDynamicMethod m) {
|
||||
for(SingleDynamicMethod method: methods) {
|
||||
public boolean contains(final SingleDynamicMethod m) {
|
||||
for(final SingleDynamicMethod method: methods) {
|
||||
if(method.contains(m)) {
|
||||
return true;
|
||||
}
|
||||
@ -241,8 +240,8 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
private static boolean isApplicableDynamically(LinkerServices linkerServices, MethodType callSiteType,
|
||||
SingleDynamicMethod m) {
|
||||
private static boolean isApplicableDynamically(final LinkerServices linkerServices, final MethodType callSiteType,
|
||||
final SingleDynamicMethod m) {
|
||||
final MethodType methodType = m.getMethodType();
|
||||
final boolean varArgs = m.isVarArgs();
|
||||
final int fixedArgLen = methodType.parameterCount() - (varArgs ? 1 : 0);
|
||||
@ -288,13 +287,13 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isApplicableDynamically(LinkerServices linkerServices, Class<?> callSiteType,
|
||||
Class<?> methodType) {
|
||||
private static boolean isApplicableDynamically(final LinkerServices linkerServices, final Class<?> callSiteType,
|
||||
final Class<?> methodType) {
|
||||
return TypeUtilities.isPotentiallyConvertible(callSiteType, methodType)
|
||||
|| linkerServices.canConvert(callSiteType, methodType);
|
||||
}
|
||||
|
||||
private ApplicableOverloadedMethods getApplicables(MethodType callSiteType, ApplicabilityTest test) {
|
||||
private ApplicableOverloadedMethods getApplicables(final MethodType callSiteType, final ApplicabilityTest test) {
|
||||
return new ApplicableOverloadedMethods(methods, callSiteType, test);
|
||||
}
|
||||
|
||||
@ -303,7 +302,7 @@ class OverloadedDynamicMethod extends DynamicMethod {
|
||||
*
|
||||
* @param method a method to add
|
||||
*/
|
||||
public void addMethod(SingleDynamicMethod method) {
|
||||
public void addMethod(final SingleDynamicMethod method) {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import jdk.internal.dynalink.linker.LinkerServices;
|
||||
import jdk.internal.dynalink.support.Lookup;
|
||||
import jdk.internal.dynalink.support.TypeUtilities;
|
||||
|
||||
/**
|
||||
* Represents a subset of overloaded methods for a certain method name on a certain class. It can be either a fixarg or
|
||||
@ -111,16 +112,18 @@ class OverloadedMethod {
|
||||
private final ArrayList<MethodHandle> fixArgMethods;
|
||||
private final ArrayList<MethodHandle> varArgMethods;
|
||||
|
||||
OverloadedMethod(List<MethodHandle> methodHandles, OverloadedDynamicMethod parent, MethodType callSiteType,
|
||||
LinkerServices linkerServices) {
|
||||
OverloadedMethod(final List<MethodHandle> methodHandles, final OverloadedDynamicMethod parent, final MethodType callSiteType,
|
||||
final LinkerServices linkerServices) {
|
||||
this.parent = parent;
|
||||
this.callSiteType = callSiteType;
|
||||
final Class<?> commonRetType = getCommonReturnType(methodHandles);
|
||||
this.callSiteType = callSiteType.changeReturnType(commonRetType);
|
||||
this.linkerServices = linkerServices;
|
||||
|
||||
fixArgMethods = new ArrayList<>(methodHandles.size());
|
||||
varArgMethods = new ArrayList<>(methodHandles.size());
|
||||
final int argNum = callSiteType.parameterCount();
|
||||
for(MethodHandle mh: methodHandles) {
|
||||
mh = mh.asType(mh.type().changeReturnType(commonRetType));
|
||||
if(mh.isVarargsCollector()) {
|
||||
final MethodHandle asFixed = mh.asFixedArity();
|
||||
if(argNum == asFixed.type().parameterCount()) {
|
||||
@ -137,7 +140,7 @@ class OverloadedMethod {
|
||||
final MethodHandle bound = SELECT_METHOD.bindTo(this);
|
||||
final MethodHandle collecting = SingleDynamicMethod.collectArguments(bound, argNum).asType(
|
||||
callSiteType.changeReturnType(MethodHandle.class));
|
||||
invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(callSiteType), collecting);
|
||||
invoker = MethodHandles.foldArguments(MethodHandles.exactInvoker(this.callSiteType), collecting);
|
||||
}
|
||||
|
||||
MethodHandle getInvoker() {
|
||||
@ -148,7 +151,7 @@ class OverloadedMethod {
|
||||
MethodHandle.class, Object[].class);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private MethodHandle selectMethod(Object[] args) throws NoSuchMethodException {
|
||||
private MethodHandle selectMethod(final Object[] args) throws NoSuchMethodException {
|
||||
final Class<?>[] argTypes = new Class[args.length];
|
||||
for(int i = 0; i < argTypes.length; ++i) {
|
||||
final Object arg = args[i];
|
||||
@ -185,7 +188,7 @@ class OverloadedMethod {
|
||||
return method;
|
||||
}
|
||||
|
||||
private MethodHandle getNoSuchMethodThrower(Class<?>[] argTypes) {
|
||||
private MethodHandle getNoSuchMethodThrower(final Class<?>[] argTypes) {
|
||||
return adaptThrower(MethodHandles.insertArguments(THROW_NO_SUCH_METHOD, 0, this, argTypes));
|
||||
}
|
||||
|
||||
@ -193,7 +196,7 @@ class OverloadedMethod {
|
||||
"throwNoSuchMethod", void.class, Class[].class);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void throwNoSuchMethod(Class<?>[] argTypes) throws NoSuchMethodException {
|
||||
private void throwNoSuchMethod(final Class<?>[] argTypes) throws NoSuchMethodException {
|
||||
if(varArgMethods.isEmpty()) {
|
||||
throw new NoSuchMethodException("None of the fixed arity signatures " + getSignatureList(fixArgMethods) +
|
||||
" of method " + parent.getName() + " match the argument types " + argTypesString(argTypes));
|
||||
@ -203,11 +206,11 @@ class OverloadedMethod {
|
||||
parent.getName() + " match the argument types " + argTypesString(argTypes));
|
||||
}
|
||||
|
||||
private MethodHandle getAmbiguousMethodThrower(Class<?>[] argTypes, List<MethodHandle> methods) {
|
||||
private MethodHandle getAmbiguousMethodThrower(final Class<?>[] argTypes, final List<MethodHandle> methods) {
|
||||
return adaptThrower(MethodHandles.insertArguments(THROW_AMBIGUOUS_METHOD, 0, this, argTypes, methods));
|
||||
}
|
||||
|
||||
private MethodHandle adaptThrower(MethodHandle rawThrower) {
|
||||
private MethodHandle adaptThrower(final MethodHandle rawThrower) {
|
||||
return MethodHandles.dropArguments(rawThrower, 0, callSiteType.parameterList()).asType(callSiteType);
|
||||
}
|
||||
|
||||
@ -215,20 +218,20 @@ class OverloadedMethod {
|
||||
"throwAmbiguousMethod", void.class, Class[].class, List.class);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void throwAmbiguousMethod(Class<?>[] argTypes, List<MethodHandle> methods) throws NoSuchMethodException {
|
||||
private void throwAmbiguousMethod(final Class<?>[] argTypes, final List<MethodHandle> methods) throws NoSuchMethodException {
|
||||
final String arity = methods.get(0).isVarargsCollector() ? "variable" : "fixed";
|
||||
throw new NoSuchMethodException("Can't unambiguously select between " + arity + " arity signatures " +
|
||||
getSignatureList(methods) + " of the method " + parent.getName() + " for argument types " +
|
||||
argTypesString(argTypes));
|
||||
}
|
||||
|
||||
private static String argTypesString(Class<?>[] classes) {
|
||||
private static String argTypesString(final Class<?>[] classes) {
|
||||
final StringBuilder b = new StringBuilder().append('[');
|
||||
appendTypes(b, classes, false);
|
||||
return b.append(']').toString();
|
||||
}
|
||||
|
||||
private static String getSignatureList(List<MethodHandle> methods) {
|
||||
private static String getSignatureList(final List<MethodHandle> methods) {
|
||||
final StringBuilder b = new StringBuilder().append('[');
|
||||
final Iterator<MethodHandle> it = methods.iterator();
|
||||
if(it.hasNext()) {
|
||||
@ -240,13 +243,13 @@ class OverloadedMethod {
|
||||
return b.append(']').toString();
|
||||
}
|
||||
|
||||
private static void appendSig(StringBuilder b, MethodHandle m) {
|
||||
private static void appendSig(final StringBuilder b, final MethodHandle m) {
|
||||
b.append('(');
|
||||
appendTypes(b, m.type().parameterArray(), m.isVarargsCollector());
|
||||
b.append(')');
|
||||
}
|
||||
|
||||
private static void appendTypes(StringBuilder b, Class<?>[] classes, boolean varArg) {
|
||||
private static void appendTypes(final StringBuilder b, final Class<?>[] classes, final boolean varArg) {
|
||||
final int l = classes.length;
|
||||
if(!varArg) {
|
||||
if(l > 1) {
|
||||
@ -262,4 +265,13 @@ class OverloadedMethod {
|
||||
b.append(classes[l - 1].getComponentType().getCanonicalName()).append("...");
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> getCommonReturnType(final List<MethodHandle> methodHandles) {
|
||||
final Iterator<MethodHandle> it = methodHandles.iterator();
|
||||
Class<?> retType = it.next().type().returnType();
|
||||
while(it.hasNext()) {
|
||||
retType = TypeUtilities.getCommonLosslessConversionType(retType, it.next().type().returnType());
|
||||
}
|
||||
return retType;
|
||||
}
|
||||
}
|
||||
|
@ -107,12 +107,12 @@ class SimpleDynamicMethod extends SingleDynamicMethod {
|
||||
* @param clazz the class declaring the method
|
||||
* @param name the simple name of the method
|
||||
*/
|
||||
SimpleDynamicMethod(MethodHandle target, Class<?> clazz, String name) {
|
||||
SimpleDynamicMethod(final MethodHandle target, final Class<?> clazz, final String name) {
|
||||
super(getName(target, clazz, name));
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
private static String getName(MethodHandle target, Class<?> clazz, String name) {
|
||||
private static String getName(final MethodHandle target, final Class<?> clazz, final String name) {
|
||||
return getMethodNameWithSignature(target.type(), getClassAndMethodName(clazz, name));
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ class SimpleDynamicMethod extends SingleDynamicMethod {
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle getTarget(Lookup lookup) {
|
||||
MethodHandle getTarget(final Lookup lookup) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
||||
|
||||
private static final MethodHandle CAN_CONVERT_TO = Lookup.findOwnStatic(MethodHandles.lookup(), "canConvertTo", boolean.class, LinkerServices.class, Class.class, Object.class);
|
||||
|
||||
SingleDynamicMethod(String name) {
|
||||
SingleDynamicMethod(final String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@ -128,22 +128,22 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
||||
abstract MethodHandle getTarget(MethodHandles.Lookup lookup);
|
||||
|
||||
@Override
|
||||
MethodHandle getInvocation(CallSiteDescriptor callSiteDescriptor, LinkerServices linkerServices) {
|
||||
MethodHandle getInvocation(final CallSiteDescriptor callSiteDescriptor, final LinkerServices linkerServices) {
|
||||
return getInvocation(getTarget(callSiteDescriptor.getLookup()), callSiteDescriptor.getMethodType(),
|
||||
linkerServices);
|
||||
}
|
||||
|
||||
@Override
|
||||
SingleDynamicMethod getMethodForExactParamTypes(String paramTypes) {
|
||||
SingleDynamicMethod getMethodForExactParamTypes(final String paramTypes) {
|
||||
return typeMatchesDescription(paramTypes, getMethodType()) ? this : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean contains(SingleDynamicMethod method) {
|
||||
boolean contains(final SingleDynamicMethod method) {
|
||||
return getMethodType().parameterList().equals(method.getMethodType().parameterList());
|
||||
}
|
||||
|
||||
static String getMethodNameWithSignature(MethodType type, String methodName) {
|
||||
static String getMethodNameWithSignature(final MethodType type, final String methodName) {
|
||||
final String typeStr = type.toString();
|
||||
final int retTypeIndex = typeStr.lastIndexOf(')') + 1;
|
||||
int secondParamIndex = typeStr.indexOf(',') + 1;
|
||||
@ -156,13 +156,15 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
||||
/**
|
||||
* Given a method handle and a call site type, adapts the method handle to the call site type. Performs type
|
||||
* conversions as needed using the specified linker services, and in case that the method handle is a vararg
|
||||
* collector, matches it to the arity of the call site.
|
||||
* collector, matches it to the arity of the call site. The type of the return value is only changed if it can be
|
||||
* converted using a conversion that loses neither precision nor magnitude, see
|
||||
* {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)}.
|
||||
* @param target the method handle to adapt
|
||||
* @param callSiteType the type of the call site
|
||||
* @param linkerServices the linker services used for type conversions
|
||||
* @return the adapted method handle.
|
||||
*/
|
||||
static MethodHandle getInvocation(MethodHandle target, MethodType callSiteType, LinkerServices linkerServices) {
|
||||
static MethodHandle getInvocation(final MethodHandle target, final MethodType callSiteType, final LinkerServices linkerServices) {
|
||||
final MethodType methodType = target.type();
|
||||
final int paramsLen = methodType.parameterCount();
|
||||
final boolean varArgs = target.isVarargsCollector();
|
||||
@ -264,7 +266,7 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean canConvertTo(final LinkerServices linkerServices, Class<?> to, Object obj) {
|
||||
private static boolean canConvertTo(final LinkerServices linkerServices, final Class<?> to, final Object obj) {
|
||||
return obj == null ? false : linkerServices.canConvert(obj.getClass(), to);
|
||||
}
|
||||
|
||||
@ -277,7 +279,7 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
||||
* @param parameterCount the total number of arguments in the new method handle
|
||||
* @return a collecting method handle
|
||||
*/
|
||||
static MethodHandle collectArguments(MethodHandle target, final int parameterCount) {
|
||||
static MethodHandle collectArguments(final MethodHandle target, final int parameterCount) {
|
||||
final MethodType methodType = target.type();
|
||||
final int fixParamsLen = methodType.parameterCount() - 1;
|
||||
final Class<?> arrayType = methodType.parameterType(fixParamsLen);
|
||||
@ -286,10 +288,10 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
||||
|
||||
private static MethodHandle createConvertingInvocation(final MethodHandle sizedMethod,
|
||||
final LinkerServices linkerServices, final MethodType callSiteType) {
|
||||
return linkerServices.asType(sizedMethod, callSiteType);
|
||||
return linkerServices.asTypeLosslessReturn(sizedMethod, callSiteType);
|
||||
}
|
||||
|
||||
private static boolean typeMatchesDescription(String paramTypes, MethodType type) {
|
||||
private static boolean typeMatchesDescription(final String paramTypes, final MethodType type) {
|
||||
final StringTokenizer tok = new StringTokenizer(paramTypes, ", ");
|
||||
for(int i = 1; i < type.parameterCount(); ++i) { // i = 1 as we ignore the receiver
|
||||
if(!(tok.hasMoreTokens() && typeNameMatches(tok.nextToken(), type.parameterType(i)))) {
|
||||
@ -299,7 +301,7 @@ abstract class SingleDynamicMethod extends DynamicMethod {
|
||||
return !tok.hasMoreTokens();
|
||||
}
|
||||
|
||||
private static boolean typeNameMatches(String typeName, Class<?> type) {
|
||||
private static boolean typeNameMatches(final String typeName, final Class<?> type) {
|
||||
return typeName.equals(typeName.indexOf('.') == -1 ? type.getSimpleName() : type.getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ import java.io.Serializable;
|
||||
public class StaticClass implements Serializable {
|
||||
private static final ClassValue<StaticClass> staticClasses = new ClassValue<StaticClass>() {
|
||||
@Override
|
||||
protected StaticClass computeValue(Class<?> type) {
|
||||
protected StaticClass computeValue(final Class<?> type) {
|
||||
return new StaticClass(type);
|
||||
}
|
||||
};
|
||||
@ -105,7 +105,7 @@ public class StaticClass implements Serializable {
|
||||
|
||||
private final Class<?> clazz;
|
||||
|
||||
/*private*/ StaticClass(Class<?> clazz) {
|
||||
/*private*/ StaticClass(final Class<?> clazz) {
|
||||
clazz.getClass(); // NPE check
|
||||
this.clazz = clazz;
|
||||
}
|
||||
@ -115,7 +115,7 @@ public class StaticClass implements Serializable {
|
||||
* @param clazz the class for which the static facet is requested.
|
||||
* @return the {@link StaticClass} instance representing the specified class.
|
||||
*/
|
||||
public static StaticClass forClass(Class<?> clazz) {
|
||||
public static StaticClass forClass(final Class<?> clazz) {
|
||||
return staticClasses.get(clazz);
|
||||
}
|
||||
|
||||
|
@ -90,14 +90,14 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class StaticClassIntrospector extends FacetIntrospector {
|
||||
StaticClassIntrospector(Class<?> clazz) {
|
||||
StaticClassIntrospector(final Class<?> clazz) {
|
||||
super(clazz, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
Map<String, MethodHandle> getInnerClassGetters() {
|
||||
final Map<String, MethodHandle> map = new HashMap<>();
|
||||
for(Class<?> innerClass: membersLookup.getInnerClasses()) {
|
||||
for(final Class<?> innerClass: membersLookup.getInnerClasses()) {
|
||||
map.put(innerClass.getSimpleName(), editMethodHandle(MethodHandles.constant(StaticClass.class,
|
||||
StaticClass.forClass(innerClass))));
|
||||
}
|
||||
@ -105,15 +105,15 @@ class StaticClassIntrospector extends FacetIntrospector {
|
||||
}
|
||||
|
||||
@Override
|
||||
MethodHandle editMethodHandle(MethodHandle mh) {
|
||||
MethodHandle editMethodHandle(final MethodHandle mh) {
|
||||
return editStaticMethodHandle(mh);
|
||||
}
|
||||
|
||||
static MethodHandle editStaticMethodHandle(MethodHandle mh) {
|
||||
static MethodHandle editStaticMethodHandle(final MethodHandle mh) {
|
||||
return dropReceiver(mh, Object.class);
|
||||
}
|
||||
|
||||
static MethodHandle editConstructorMethodHandle(MethodHandle cmh) {
|
||||
static MethodHandle editConstructorMethodHandle(final MethodHandle cmh) {
|
||||
return dropReceiver(cmh, StaticClass.class);
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ import jdk.internal.dynalink.support.Lookup;
|
||||
class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
|
||||
private static final ClassValue<SingleClassStaticsLinker> linkers = new ClassValue<SingleClassStaticsLinker>() {
|
||||
@Override
|
||||
protected SingleClassStaticsLinker computeValue(Class<?> clazz) {
|
||||
protected SingleClassStaticsLinker computeValue(final Class<?> clazz) {
|
||||
return new SingleClassStaticsLinker(clazz);
|
||||
}
|
||||
};
|
||||
@ -112,7 +112,7 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
|
||||
private static class SingleClassStaticsLinker extends AbstractJavaLinker {
|
||||
private final DynamicMethod constructor;
|
||||
|
||||
SingleClassStaticsLinker(Class<?> clazz) {
|
||||
SingleClassStaticsLinker(final Class<?> clazz) {
|
||||
super(clazz, IS_CLASS.bindTo(clazz));
|
||||
// Map "staticClassObject.class" to StaticClass.getRepresentedClass(). Some adventurous soul could subclass
|
||||
// StaticClass, so we use INSTANCE_OF validation instead of EXACT_CLASS.
|
||||
@ -126,7 +126,7 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
|
||||
* @return a dynamic method containing all overloads of a class' public constructor. If the class has no public
|
||||
* constructors, returns null.
|
||||
*/
|
||||
private static DynamicMethod createConstructorMethod(Class<?> clazz) {
|
||||
private static DynamicMethod createConstructorMethod(final Class<?> clazz) {
|
||||
if(clazz.isArray()) {
|
||||
final MethodHandle boundArrayCtor = ARRAY_CTOR.bindTo(clazz.getComponentType());
|
||||
return new SimpleDynamicMethod(StaticClassIntrospector.editConstructorMethodHandle(
|
||||
@ -144,7 +144,7 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation getGuardedInvocation(LinkRequest request, LinkerServices linkerServices)
|
||||
public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices)
|
||||
throws Exception {
|
||||
final GuardedInvocation gi = super.getGuardedInvocation(request, linkerServices);
|
||||
if(gi != null) {
|
||||
@ -162,20 +162,20 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
|
||||
}
|
||||
}
|
||||
|
||||
static Collection<String> getReadableStaticPropertyNames(Class<?> clazz) {
|
||||
static Collection<String> getReadableStaticPropertyNames(final Class<?> clazz) {
|
||||
return linkers.get(clazz).getReadablePropertyNames();
|
||||
}
|
||||
|
||||
static Collection<String> getWritableStaticPropertyNames(Class<?> clazz) {
|
||||
static Collection<String> getWritableStaticPropertyNames(final Class<?> clazz) {
|
||||
return linkers.get(clazz).getWritablePropertyNames();
|
||||
}
|
||||
|
||||
static Collection<String> getStaticMethodNames(Class<?> clazz) {
|
||||
static Collection<String> getStaticMethodNames(final Class<?> clazz) {
|
||||
return linkers.get(clazz).getMethodNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation getGuardedInvocation(LinkRequest request, LinkerServices linkerServices) throws Exception {
|
||||
public GuardedInvocation getGuardedInvocation(final LinkRequest request, final LinkerServices linkerServices) throws Exception {
|
||||
final Object receiver = request.getReceiver();
|
||||
if(receiver instanceof StaticClass) {
|
||||
return linkers.get(((StaticClass)receiver).getRepresentedClass()).getGuardedInvocation(request,
|
||||
@ -185,7 +185,7 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canLinkType(Class<?> type) {
|
||||
public boolean canLinkType(final Class<?> type) {
|
||||
return type == StaticClass.class;
|
||||
}
|
||||
|
||||
@ -201,7 +201,7 @@ class StaticClassLinker implements TypeBasedGuardingDynamicLinker {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isClass(Class<?> clazz, Object obj) {
|
||||
private static boolean isClass(final Class<?> clazz, final Object obj) {
|
||||
return obj instanceof StaticClass && ((StaticClass)obj).getRepresentedClass() == clazz;
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,8 @@
|
||||
|
||||
package jdk.internal.dynalink.linker;
|
||||
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
@ -104,7 +106,18 @@ import jdk.internal.dynalink.support.Guards;
|
||||
public class GuardedInvocation {
|
||||
private final MethodHandle invocation;
|
||||
private final MethodHandle guard;
|
||||
private final SwitchPoint switchPoint;
|
||||
private final Class<? extends Throwable> exception;
|
||||
private final SwitchPoint[] switchPoints;
|
||||
|
||||
/**
|
||||
* Creates a new guarded invocation. This invocation is unconditional as it has no invalidations.
|
||||
*
|
||||
* @param invocation the method handle representing the invocation. Must not be null.
|
||||
* @throws NullPointerException if invocation is null.
|
||||
*/
|
||||
public GuardedInvocation(final MethodHandle invocation) {
|
||||
this(invocation, null, (SwitchPoint)null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new guarded invocation.
|
||||
@ -115,8 +128,19 @@ public class GuardedInvocation {
|
||||
* an unconditional invocation, although that is unusual.
|
||||
* @throws NullPointerException if invocation is null.
|
||||
*/
|
||||
public GuardedInvocation(MethodHandle invocation, MethodHandle guard) {
|
||||
this(invocation, guard, null);
|
||||
public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard) {
|
||||
this(invocation, guard, (SwitchPoint)null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new guarded invocation.
|
||||
*
|
||||
* @param invocation the method handle representing the invocation. Must not be null.
|
||||
* @param switchPoint the optional switch point that can be used to invalidate this linkage.
|
||||
* @throws NullPointerException if invocation is null.
|
||||
*/
|
||||
public GuardedInvocation(final MethodHandle invocation, final SwitchPoint switchPoint) {
|
||||
this(invocation, null, switchPoint, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,26 +153,50 @@ public class GuardedInvocation {
|
||||
* @param switchPoint the optional switch point that can be used to invalidate this linkage.
|
||||
* @throws NullPointerException if invocation is null.
|
||||
*/
|
||||
public GuardedInvocation(MethodHandle invocation, MethodHandle guard, SwitchPoint switchPoint) {
|
||||
public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint switchPoint) {
|
||||
this(invocation, guard, switchPoint, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new guarded invocation.
|
||||
*
|
||||
* @param invocation the method handle representing the invocation. Must not be null.
|
||||
* @param guard the method handle representing the guard. Must have the same method type as the invocation, except
|
||||
* it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
|
||||
* and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
|
||||
* @param switchPoint the optional switch point that can be used to invalidate this linkage.
|
||||
* @param exception the optional exception type that is expected to be thrown by the invocation and that also
|
||||
* invalidates the linkage.
|
||||
* @throws NullPointerException if invocation is null.
|
||||
*/
|
||||
public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint switchPoint, final Class<? extends Throwable> exception) {
|
||||
invocation.getClass(); // NPE check
|
||||
this.invocation = invocation;
|
||||
this.guard = guard;
|
||||
this.switchPoint = switchPoint;
|
||||
this.switchPoints = switchPoint == null ? null : new SwitchPoint[] { switchPoint };
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new guarded invocation.
|
||||
* Creates a new guarded invocation
|
||||
*
|
||||
* @param invocation the method handle representing the invocation. Must not be null.
|
||||
* @param switchPoint the optional switch point that can be used to invalidate this linkage.
|
||||
* @param guard the method handle representing the guard. Must have the same method type as the invocation, except
|
||||
* it must return boolean. For some useful guards, check out the {@link Guards} class. It can be null. If both it
|
||||
* and the switch point are null, this represents an unconditional invocation, which is legal but unusual.
|
||||
* @param switchPoints the optional switch points that can be used to invalidate this linkage.
|
||||
* @param exception the optional exception type that is expected to be thrown by the invocation and that also
|
||||
* invalidates the linkage.
|
||||
* @throws NullPointerException if invocation is null.
|
||||
*/
|
||||
public GuardedInvocation(MethodHandle invocation, SwitchPoint switchPoint, MethodHandle guard) {
|
||||
this(invocation, guard, switchPoint);
|
||||
public GuardedInvocation(final MethodHandle invocation, final MethodHandle guard, final SwitchPoint[] switchPoints, final Class<? extends Throwable> exception) {
|
||||
invocation.getClass(); // NPE check
|
||||
this.invocation = invocation;
|
||||
this.guard = guard;
|
||||
this.switchPoints = switchPoints;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the invocation method handle.
|
||||
*
|
||||
@ -172,8 +220,17 @@ public class GuardedInvocation {
|
||||
*
|
||||
* @return the switch point that can be used to invalidate the invocation handle. Can be null.
|
||||
*/
|
||||
public SwitchPoint getSwitchPoint() {
|
||||
return switchPoint;
|
||||
public SwitchPoint[] getSwitchPoints() {
|
||||
return switchPoints == null ? null : switchPoints.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exception type that if thrown should be used to invalidate the linkage.
|
||||
*
|
||||
* @return the exception type that if thrown should be used to invalidate the linkage. Can be null.
|
||||
*/
|
||||
public Class<? extends Throwable> getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -181,7 +238,15 @@ public class GuardedInvocation {
|
||||
* @return true if and only if this guarded invocation has a switchpoint, and that switchpoint has been invalidated.
|
||||
*/
|
||||
public boolean hasBeenInvalidated() {
|
||||
return switchPoint != null && switchPoint.hasBeenInvalidated();
|
||||
if (switchPoints == null) {
|
||||
return false;
|
||||
}
|
||||
for (final SwitchPoint sp : switchPoints) {
|
||||
if (sp.hasBeenInvalidated()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,9 +256,9 @@ public class GuardedInvocation {
|
||||
* @param type the asserted type
|
||||
* @throws WrongMethodTypeException if the invocation and the guard are not of the expected method type.
|
||||
*/
|
||||
public void assertType(MethodType type) {
|
||||
public void assertType(final MethodType type) {
|
||||
assertType(invocation, type);
|
||||
if(guard != null) {
|
||||
if (guard != null) {
|
||||
assertType(guard, type.changeReturnType(Boolean.TYPE));
|
||||
}
|
||||
}
|
||||
@ -205,12 +270,34 @@ public class GuardedInvocation {
|
||||
* @param newGuard the new guard
|
||||
* @return a new guarded invocation with the replaced methods and the same switch point as this invocation.
|
||||
*/
|
||||
public GuardedInvocation replaceMethods(MethodHandle newInvocation, MethodHandle newGuard) {
|
||||
return new GuardedInvocation(newInvocation, newGuard, switchPoint);
|
||||
public GuardedInvocation replaceMethods(final MethodHandle newInvocation, final MethodHandle newGuard) {
|
||||
return new GuardedInvocation(newInvocation, newGuard, switchPoints, exception);
|
||||
}
|
||||
|
||||
private GuardedInvocation replaceMethodsOrThis(MethodHandle newInvocation, MethodHandle newGuard) {
|
||||
if(newInvocation == invocation && newGuard == guard) {
|
||||
/**
|
||||
* Add a switchpoint to this guarded invocation
|
||||
* @param newSwitchPoint new switchpoint, or null for nop
|
||||
* @return new guarded invocation with the extra switchpoint
|
||||
*/
|
||||
public GuardedInvocation addSwitchPoint(final SwitchPoint newSwitchPoint) {
|
||||
if (newSwitchPoint == null) {
|
||||
return this;
|
||||
}
|
||||
|
||||
final SwitchPoint[] newSwitchPoints;
|
||||
if (switchPoints != null) {
|
||||
newSwitchPoints = new SwitchPoint[switchPoints.length + 1];
|
||||
System.arraycopy(switchPoints, 0, newSwitchPoints, 0, switchPoints.length);
|
||||
newSwitchPoints[switchPoints.length] = newSwitchPoint;
|
||||
} else {
|
||||
newSwitchPoints = new SwitchPoint[] { newSwitchPoint };
|
||||
}
|
||||
|
||||
return new GuardedInvocation(invocation, guard, newSwitchPoints, exception);
|
||||
}
|
||||
|
||||
private GuardedInvocation replaceMethodsOrThis(final MethodHandle newInvocation, final MethodHandle newGuard) {
|
||||
if (newInvocation == invocation && newGuard == guard) {
|
||||
return this;
|
||||
}
|
||||
return replaceMethods(newInvocation, newGuard);
|
||||
@ -223,7 +310,7 @@ public class GuardedInvocation {
|
||||
* @param newType the new type of the invocation.
|
||||
* @return a guarded invocation with the new type applied to it.
|
||||
*/
|
||||
public GuardedInvocation asType(MethodType newType) {
|
||||
public GuardedInvocation asType(final MethodType newType) {
|
||||
return replaceMethodsOrThis(invocation.asType(newType), guard == null ? null : Guards.asType(guard, newType));
|
||||
}
|
||||
|
||||
@ -235,11 +322,25 @@ public class GuardedInvocation {
|
||||
* @param newType the new type of the invocation.
|
||||
* @return a guarded invocation with the new type applied to it.
|
||||
*/
|
||||
public GuardedInvocation asType(LinkerServices linkerServices, MethodType newType) {
|
||||
public GuardedInvocation asType(final LinkerServices linkerServices, final MethodType newType) {
|
||||
return replaceMethodsOrThis(linkerServices.asType(invocation, newType), guard == null ? null :
|
||||
Guards.asType(linkerServices, guard, newType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the type of the invocation, as if {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)} was
|
||||
* applied to its invocation and {@link LinkerServices#asType(MethodHandle, MethodType)} applied to its guard, if it
|
||||
* has one (with return type changed to boolean, and parameter count potentially truncated for the guard). If the
|
||||
* invocation doesn't change its type, returns this object.
|
||||
* @param linkerServices the linker services to use for the conversion
|
||||
* @param newType the new type of the invocation.
|
||||
* @return a guarded invocation with the new type applied to it.
|
||||
*/
|
||||
public GuardedInvocation asTypeSafeReturn(final LinkerServices linkerServices, final MethodType newType) {
|
||||
return replaceMethodsOrThis(linkerServices.asTypeLosslessReturn(invocation, newType), guard == null ? null :
|
||||
Guards.asType(linkerServices, guard, newType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the type of the invocation, as if {@link MethodHandle#asType(MethodType)} was applied to its invocation
|
||||
* and its guard, if it has one (with return type changed to boolean for guard). If the invocation already is of the
|
||||
@ -247,7 +348,7 @@ public class GuardedInvocation {
|
||||
* @param desc a call descriptor whose method type is adapted.
|
||||
* @return a guarded invocation with the new type applied to it.
|
||||
*/
|
||||
public GuardedInvocation asType(CallSiteDescriptor desc) {
|
||||
public GuardedInvocation asType(final CallSiteDescriptor desc) {
|
||||
return asType(desc.getMethodType());
|
||||
}
|
||||
|
||||
@ -257,7 +358,7 @@ public class GuardedInvocation {
|
||||
* @param filters the argument filters
|
||||
* @return a filtered invocation
|
||||
*/
|
||||
public GuardedInvocation filterArguments(int pos, MethodHandle... filters) {
|
||||
public GuardedInvocation filterArguments(final int pos, final MethodHandle... filters) {
|
||||
return replaceMethods(MethodHandles.filterArguments(invocation, pos, filters), guard == null ? null :
|
||||
MethodHandles.filterArguments(guard, pos, filters));
|
||||
}
|
||||
@ -268,7 +369,7 @@ public class GuardedInvocation {
|
||||
* @param valueTypes the types of the values being dropped
|
||||
* @return an invocation that drops arguments
|
||||
*/
|
||||
public GuardedInvocation dropArguments(int pos, List<Class<?>> valueTypes) {
|
||||
public GuardedInvocation dropArguments(final int pos, final List<Class<?>> valueTypes) {
|
||||
return replaceMethods(MethodHandles.dropArguments(invocation, pos, valueTypes), guard == null ? null :
|
||||
MethodHandles.dropArguments(guard, pos, valueTypes));
|
||||
}
|
||||
@ -279,7 +380,7 @@ public class GuardedInvocation {
|
||||
* @param valueTypes the types of the values being dropped
|
||||
* @return an invocation that drops arguments
|
||||
*/
|
||||
public GuardedInvocation dropArguments(int pos, Class<?>... valueTypes) {
|
||||
public GuardedInvocation dropArguments(final int pos, final Class<?>... valueTypes) {
|
||||
return replaceMethods(MethodHandles.dropArguments(invocation, pos, valueTypes), guard == null ? null :
|
||||
MethodHandles.dropArguments(guard, pos, valueTypes));
|
||||
}
|
||||
@ -290,23 +391,50 @@ public class GuardedInvocation {
|
||||
* @param fallback the fallback method handle in case switchpoint is invalidated or guard returns false.
|
||||
* @return a composite method handle.
|
||||
*/
|
||||
public MethodHandle compose(MethodHandle fallback) {
|
||||
return compose(fallback, fallback);
|
||||
public MethodHandle compose(final MethodHandle fallback) {
|
||||
return compose(fallback, fallback, fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes the invocation, switchpoint, and the guard into a composite method handle that knows how to fall back.
|
||||
* @param switchpointFallback the fallback method handle in case switchpoint is invalidated.
|
||||
* @param guardFallback the fallback method handle in case guard returns false.
|
||||
* @param catchFallback the fallback method in case the exception handler triggers
|
||||
* @return a composite method handle.
|
||||
*/
|
||||
public MethodHandle compose(MethodHandle switchpointFallback, MethodHandle guardFallback) {
|
||||
public MethodHandle compose(final MethodHandle guardFallback, final MethodHandle switchpointFallback, final MethodHandle catchFallback) {
|
||||
final MethodHandle guarded =
|
||||
guard == null ? invocation : MethodHandles.guardWithTest(guard, invocation, guardFallback);
|
||||
return switchPoint == null ? guarded : switchPoint.guardWithTest(guarded, switchpointFallback);
|
||||
guard == null ?
|
||||
invocation :
|
||||
MethodHandles.guardWithTest(
|
||||
guard,
|
||||
invocation,
|
||||
guardFallback);
|
||||
|
||||
final MethodHandle catchGuarded =
|
||||
exception == null ?
|
||||
guarded :
|
||||
MH.catchException(
|
||||
guarded,
|
||||
exception,
|
||||
MethodHandles.dropArguments(
|
||||
catchFallback,
|
||||
0,
|
||||
exception));
|
||||
|
||||
if (switchPoints == null) {
|
||||
return catchGuarded;
|
||||
}
|
||||
|
||||
MethodHandle spGuarded = catchGuarded;
|
||||
for (final SwitchPoint sp : switchPoints) {
|
||||
spGuarded = sp.guardWithTest(spGuarded, switchpointFallback);
|
||||
}
|
||||
|
||||
return spGuarded;
|
||||
}
|
||||
|
||||
private static void assertType(MethodHandle mh, MethodType type) {
|
||||
private static void assertType(final MethodHandle mh, final MethodType type) {
|
||||
if(!mh.type().equals(type)) {
|
||||
throw new WrongMethodTypeException("Expected type: " + type + " actual type: " + mh.type());
|
||||
}
|
||||
|
@ -83,19 +83,35 @@
|
||||
|
||||
package jdk.internal.dynalink.linker;
|
||||
|
||||
/**
|
||||
* Guarded type conversion
|
||||
*/
|
||||
public class GuardedTypeConversion {
|
||||
private final GuardedInvocation conversionInvocation;
|
||||
private final boolean cacheable;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param conversionInvocation guarded invocation for this type conversion
|
||||
* @param cacheable is this invocation cacheable
|
||||
*/
|
||||
public GuardedTypeConversion(final GuardedInvocation conversionInvocation, final boolean cacheable) {
|
||||
this.conversionInvocation = conversionInvocation;
|
||||
this.cacheable = cacheable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the invocation
|
||||
* @return invocation
|
||||
*/
|
||||
public GuardedInvocation getConversionInvocation() {
|
||||
return conversionInvocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if invocation is cacheable
|
||||
* @return true if cachable, false otherwise
|
||||
*/
|
||||
public boolean isCacheable() {
|
||||
return cacheable;
|
||||
}
|
||||
|
@ -101,10 +101,16 @@ public interface GuardingDynamicLinker {
|
||||
* @return a guarded invocation with a method handle suitable for the arguments, as well as a guard condition that
|
||||
* if fails should trigger relinking. Must return null if it can't resolve the invocation. If the returned
|
||||
* invocation is unconditional (which is actually quite rare), the guard in the return value can be null. The
|
||||
* invocation can also have a switch point for asynchronous invalidation of the linkage. If the linker does not
|
||||
* recognize any native language runtime contexts in arguments, or does recognize its own, but receives a call site
|
||||
* descriptor without its recognized context in the arguments, it should invoke
|
||||
* {@link LinkRequest#withoutRuntimeContext()} and link for that.
|
||||
* invocation can also have a switch point for asynchronous invalidation of the linkage, as well as a
|
||||
* {@link Throwable} subclass that describes an expected exception condition that also triggers relinking (often it
|
||||
* is faster to rely on an infrequent but expected {@link ClassCastException} than on an always evaluated
|
||||
* {@code instanceof} guard). If the linker does not recognize any native language runtime contexts in arguments, or
|
||||
* does recognize its own, but receives a call site descriptor without its recognized context in the arguments, it
|
||||
* should invoke {@link LinkRequest#withoutRuntimeContext()} and link for that. While the linker must produce an
|
||||
* invocation with parameter types matching those in the call site descriptor of the link request, it should not try
|
||||
* to match the return type expected at the call site except when it can do it with only the conversions that lose
|
||||
* neither precision nor magnitude, see {@link LinkerServices#asTypeLosslessReturn(java.lang.invoke.MethodHandle,
|
||||
* java.lang.invoke.MethodType)}.
|
||||
* @throws Exception if the operation fails for whatever reason
|
||||
*/
|
||||
public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, LinkerServices linkerServices)
|
||||
|
@ -100,6 +100,17 @@ public interface LinkRequest {
|
||||
*/
|
||||
public CallSiteDescriptor getCallSiteDescriptor();
|
||||
|
||||
/**
|
||||
* Returns the call site token for the call site being linked. This token is an opaque object that is guaranteed to
|
||||
* have different identity for different call sites, and is also guaranteed to not become weakly reachable before
|
||||
* the call site does and to become weakly reachable some time after the call site does. This makes it ideal as a
|
||||
* candidate for a key in a weak hash map in which a linker might want to keep per-call site linking state (usually
|
||||
* profiling information).
|
||||
*
|
||||
* @return the call site token for the call site being linked.
|
||||
*/
|
||||
public Object getCallSiteToken();
|
||||
|
||||
/**
|
||||
* Returns the arguments for the invocation being linked. The returned array is a clone; modifications to it won't
|
||||
* affect the arguments in this request.
|
||||
@ -115,6 +126,17 @@ public interface LinkRequest {
|
||||
*/
|
||||
public Object getReceiver();
|
||||
|
||||
/**
|
||||
* Returns the number of times this callsite has been linked/relinked. This can be useful if you want to
|
||||
* change e.g. exception based relinking to guard based relinking. It's probably not a good idea to keep,
|
||||
* for example, expensive exception throwing relinkage based on failed type checks/ClassCastException in
|
||||
* a nested callsite tree where the exception is thrown repeatedly for the common case. There it would be
|
||||
* much more performant to use exact type guards instead.
|
||||
*
|
||||
* @return link count for call site
|
||||
*/
|
||||
public int getLinkCount();
|
||||
|
||||
/**
|
||||
* Returns true if the call site is considered unstable, that is, it has been relinked more times than was
|
||||
* specified in {@link DynamicLinkerFactory#setUnstableRelinkThreshold(int)}. Linkers should use this as a
|
||||
|
@ -87,7 +87,9 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import jdk.internal.dynalink.DynamicLinker;
|
||||
import jdk.internal.dynalink.DynamicLinkerFactory;
|
||||
import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
|
||||
import jdk.internal.dynalink.support.TypeUtilities;
|
||||
|
||||
/**
|
||||
* Interface for services provided to {@link GuardingDynamicLinker} instances by the {@link DynamicLinker} that owns
|
||||
@ -103,17 +105,33 @@ public interface LinkerServices {
|
||||
* parameters. It will apply {@link MethodHandle#asType(MethodType)} for all primitive-to-primitive,
|
||||
* wrapper-to-primitive, primitive-to-wrapper conversions as well as for all upcasts. For all other conversions,
|
||||
* it'll insert {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with composite filters
|
||||
* provided by {@link GuardingTypeConverterFactory} implementations. It doesn't use language-specific conversions on
|
||||
* the return type.
|
||||
* provided by {@link GuardingTypeConverterFactory} implementations.
|
||||
*
|
||||
* @param handle target method handle
|
||||
* @param fromType the types of source arguments
|
||||
* @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)} and
|
||||
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
|
||||
* {@link GuardingTypeConverterFactory} produced type converters as filters.
|
||||
* @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)},
|
||||
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)}, and
|
||||
* {@link MethodHandles#filterReturnValue(MethodHandle, MethodHandle)} with
|
||||
* {@link GuardingTypeConverterFactory}-produced type converters as filters.
|
||||
*/
|
||||
public MethodHandle asType(MethodHandle handle, MethodType fromType);
|
||||
|
||||
/**
|
||||
* Similar to {@link #asType(MethodHandle, MethodType)} except it only converts the return type of the method handle
|
||||
* when it can be done using a conversion that loses neither precision nor magnitude, otherwise it leaves it
|
||||
* unchanged. The idea is that other conversions should not be performed by individual linkers, but instead the
|
||||
* {@link DynamicLinkerFactory#setPrelinkFilter(jdk.internal.dynalink.GuardedInvocationFilter) pre-link filter of
|
||||
* the dynamic linker} should implement the strategy of dealing with potentially lossy return type conversions in a
|
||||
* manner specific to the language runtime.
|
||||
*
|
||||
* @param handle target method handle
|
||||
* @param fromType the types of source arguments
|
||||
* @return a method handle that is a suitable combination of {@link MethodHandle#asType(MethodType)}, and
|
||||
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
|
||||
* {@link GuardingTypeConverterFactory}-produced type converters as filters.
|
||||
*/
|
||||
public MethodHandle asTypeLosslessReturn(MethodHandle handle, MethodType fromType);
|
||||
|
||||
/**
|
||||
* Given a source and target type, returns a method handle that converts between them. Never returns null; in worst
|
||||
* case it will return an identity conversion (that might fail for some values at runtime). You rarely need to use
|
||||
@ -161,4 +179,23 @@ public interface LinkerServices {
|
||||
* conversion.
|
||||
*/
|
||||
public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2);
|
||||
|
||||
/**
|
||||
* If we could just use Java 8 constructs, then {@code asTypeSafeReturn} would be a method with default
|
||||
* implementation. Since we can't do that, we extract common default implementations into this static class.
|
||||
*/
|
||||
public static class Implementation {
|
||||
/**
|
||||
* Default implementation for {@link LinkerServices#asTypeLosslessReturn(MethodHandle, MethodType)}.
|
||||
* @param linkerServices the linker services that delegates to this implementation
|
||||
* @param handle the passed handle
|
||||
* @param fromType the passed type
|
||||
* @return the converted method handle, as per the {@code asTypeSafeReturn} semantics.
|
||||
*/
|
||||
public static MethodHandle asTypeLosslessReturn(final LinkerServices linkerServices, final MethodHandle handle, final MethodType fromType) {
|
||||
final Class<?> handleReturnType = handle.type().returnType();
|
||||
return linkerServices.asType(handle, TypeUtilities.isConvertibleWithoutLoss(handleReturnType, fromType.returnType()) ?
|
||||
fromType : fromType.changeReturnType(handleReturnType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ public abstract class AbstractCallSiteDescriptor implements CallSiteDescriptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public boolean equals(final Object obj) {
|
||||
return obj instanceof CallSiteDescriptor && equals((CallSiteDescriptor)obj);
|
||||
}
|
||||
|
||||
@ -115,7 +115,7 @@ public abstract class AbstractCallSiteDescriptor implements CallSiteDescriptor {
|
||||
* @param csd the other call site descriptor.
|
||||
* @return true if they are equal.
|
||||
*/
|
||||
public boolean equals(CallSiteDescriptor csd) {
|
||||
public boolean equals(final CallSiteDescriptor csd) {
|
||||
if(csd == null) {
|
||||
return false;
|
||||
}
|
||||
@ -165,7 +165,7 @@ public abstract class AbstractCallSiteDescriptor implements CallSiteDescriptor {
|
||||
return l + c - 1;
|
||||
}
|
||||
|
||||
private StringBuilder appendName(StringBuilder b) {
|
||||
private StringBuilder appendName(final StringBuilder b) {
|
||||
b.append(getNameToken(0));
|
||||
final int c = getNameTokenCount();
|
||||
for(int i = 1; i < c; ++i) {
|
||||
@ -174,7 +174,7 @@ public abstract class AbstractCallSiteDescriptor implements CallSiteDescriptor {
|
||||
return b;
|
||||
}
|
||||
|
||||
private static boolean lookupsEqual(Lookup l1, Lookup l2) {
|
||||
private static boolean lookupsEqual(final Lookup l1, final Lookup l2) {
|
||||
if(l1 == l2) {
|
||||
return true;
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ public abstract class AbstractRelinkableCallSite extends MutableCallSite impleme
|
||||
* Creates a new relinkable call site.
|
||||
* @param descriptor the descriptor for this call site
|
||||
*/
|
||||
protected AbstractRelinkableCallSite(CallSiteDescriptor descriptor) {
|
||||
protected AbstractRelinkableCallSite(final CallSiteDescriptor descriptor) {
|
||||
super(descriptor.getMethodType());
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
@ -111,7 +111,7 @@ public abstract class AbstractRelinkableCallSite extends MutableCallSite impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(MethodHandle relinkAndInvoke) {
|
||||
public void initialize(final MethodHandle relinkAndInvoke) {
|
||||
setTarget(relinkAndInvoke);
|
||||
}
|
||||
}
|
||||
|
@ -116,14 +116,14 @@ public class AutoDiscovery {
|
||||
* @return a list of guarding dynamic linkers available through the specified class loader. Can be zero-length list
|
||||
* but not null.
|
||||
*/
|
||||
public static List<GuardingDynamicLinker> loadLinkers(ClassLoader cl) {
|
||||
public static List<GuardingDynamicLinker> loadLinkers(final ClassLoader cl) {
|
||||
return getLinkers(ServiceLoader.load(GuardingDynamicLinker.class, cl));
|
||||
}
|
||||
|
||||
/**
|
||||
* I can't believe there's no Collections API for making a List given an Iterator...
|
||||
*/
|
||||
private static <T> List<T> getLinkers(ServiceLoader<T> loader) {
|
||||
private static <T> List<T> getLinkers(final ServiceLoader<T> loader) {
|
||||
final List<T> list = new LinkedList<>();
|
||||
for(final T linker: loader) {
|
||||
list.add(linker);
|
||||
|
@ -105,12 +105,12 @@ public class BottomGuardingDynamicLinker implements TypeBasedGuardingDynamicLink
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canLinkType(Class<?> type) {
|
||||
public boolean canLinkType(final Class<?> type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, LinkerServices linkerServices) {
|
||||
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +86,7 @@ package jdk.internal.dynalink.support;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -103,7 +104,7 @@ import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
* @author Attila Szegedi
|
||||
*/
|
||||
public class CallSiteDescriptorFactory {
|
||||
private static final WeakHashMap<CallSiteDescriptor, WeakReference<CallSiteDescriptor>> publicDescs =
|
||||
private static final WeakHashMap<CallSiteDescriptor, Reference<CallSiteDescriptor>> publicDescs =
|
||||
new WeakHashMap<>();
|
||||
|
||||
|
||||
@ -121,7 +122,7 @@ public class CallSiteDescriptorFactory {
|
||||
* @return a call site descriptor representing the input. Note that although the method name is "create", it will
|
||||
* in fact return a weakly-referenced canonical instance.
|
||||
*/
|
||||
public static CallSiteDescriptor create(Lookup lookup, String name, MethodType methodType) {
|
||||
public static CallSiteDescriptor create(final Lookup lookup, final String name, final MethodType methodType) {
|
||||
name.getClass(); // NPE check
|
||||
methodType.getClass(); // NPE check
|
||||
lookup.getClass(); // NPE check
|
||||
@ -134,19 +135,28 @@ public class CallSiteDescriptorFactory {
|
||||
|
||||
static CallSiteDescriptor getCanonicalPublicDescriptor(final CallSiteDescriptor desc) {
|
||||
synchronized(publicDescs) {
|
||||
final WeakReference<CallSiteDescriptor> ref = publicDescs.get(desc);
|
||||
final Reference<CallSiteDescriptor> ref = publicDescs.get(desc);
|
||||
if(ref != null) {
|
||||
final CallSiteDescriptor canonical = ref.get();
|
||||
if(canonical != null) {
|
||||
return canonical;
|
||||
}
|
||||
}
|
||||
publicDescs.put(desc, new WeakReference<>(desc));
|
||||
publicDescs.put(desc, createReference(desc));
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
|
||||
private static CallSiteDescriptor createPublicCallSiteDescriptor(String[] tokenizedName, MethodType methodType) {
|
||||
/**
|
||||
* Override this to use a different kind of references for the cache
|
||||
* @param desc desc
|
||||
* @return reference
|
||||
*/
|
||||
protected static Reference<CallSiteDescriptor> createReference(final CallSiteDescriptor desc) {
|
||||
return new WeakReference<>(desc);
|
||||
}
|
||||
|
||||
private static CallSiteDescriptor createPublicCallSiteDescriptor(final String[] tokenizedName, final MethodType methodType) {
|
||||
final int l = tokenizedName.length;
|
||||
if(l > 0 && tokenizedName[0] == "dyn") {
|
||||
if(l == 2) {
|
||||
@ -158,7 +168,7 @@ public class CallSiteDescriptorFactory {
|
||||
return new DefaultCallSiteDescriptor(tokenizedName, methodType);
|
||||
}
|
||||
|
||||
private static boolean isPublicLookup(Lookup lookup) {
|
||||
private static boolean isPublicLookup(final Lookup lookup) {
|
||||
return lookup == MethodHandles.publicLookup();
|
||||
}
|
||||
|
||||
@ -169,7 +179,7 @@ public class CallSiteDescriptorFactory {
|
||||
* @param name the composite name consisting of colon-separated, possibly mangled tokens.
|
||||
* @return an array of tokens
|
||||
*/
|
||||
public static String[] tokenizeName(String name) {
|
||||
public static String[] tokenizeName(final String name) {
|
||||
final StringTokenizer tok = new StringTokenizer(name, CallSiteDescriptor.TOKEN_DELIMITER);
|
||||
final String[] tokens = new String[tok.countTokens()];
|
||||
for(int i = 0; i < tokens.length; ++i) {
|
||||
@ -188,7 +198,7 @@ public class CallSiteDescriptorFactory {
|
||||
* @param desc the call site descriptor with the operation
|
||||
* @return a list of tokens
|
||||
*/
|
||||
public static List<String> tokenizeOperators(CallSiteDescriptor desc) {
|
||||
public static List<String> tokenizeOperators(final CallSiteDescriptor desc) {
|
||||
final String ops = desc.getNameToken(CallSiteDescriptor.OPERATOR);
|
||||
final StringTokenizer tok = new StringTokenizer(ops, CallSiteDescriptor.OPERATOR_DELIMITER);
|
||||
final int count = tok.countTokens();
|
||||
@ -210,7 +220,7 @@ public class CallSiteDescriptorFactory {
|
||||
* @param end index of the first parameter to not remove
|
||||
* @return a new call site descriptor with modified method type
|
||||
*/
|
||||
public static CallSiteDescriptor dropParameterTypes(CallSiteDescriptor desc, int start, int end) {
|
||||
public static CallSiteDescriptor dropParameterTypes(final CallSiteDescriptor desc, final int start, final int end) {
|
||||
return desc.changeMethodType(desc.getMethodType().dropParameterTypes(start, end));
|
||||
}
|
||||
|
||||
@ -222,7 +232,7 @@ public class CallSiteDescriptorFactory {
|
||||
* @param nptype the new parameter type
|
||||
* @return a new call site descriptor with modified method type
|
||||
*/
|
||||
public static CallSiteDescriptor changeParameterType(CallSiteDescriptor desc, int num, Class<?> nptype) {
|
||||
public static CallSiteDescriptor changeParameterType(final CallSiteDescriptor desc, final int num, final Class<?> nptype) {
|
||||
return desc.changeMethodType(desc.getMethodType().changeParameterType(num, nptype));
|
||||
}
|
||||
|
||||
@ -233,7 +243,7 @@ public class CallSiteDescriptorFactory {
|
||||
* @param nrtype the new return type
|
||||
* @return a new call site descriptor with modified method type
|
||||
*/
|
||||
public static CallSiteDescriptor changeReturnType(CallSiteDescriptor desc, Class<?> nrtype) {
|
||||
public static CallSiteDescriptor changeReturnType(final CallSiteDescriptor desc, final Class<?> nrtype) {
|
||||
return desc.changeMethodType(desc.getMethodType().changeReturnType(nrtype));
|
||||
}
|
||||
|
||||
@ -245,7 +255,7 @@ public class CallSiteDescriptorFactory {
|
||||
* @param ptypesToInsert the new types to insert
|
||||
* @return a new call site descriptor with modified method type
|
||||
*/
|
||||
public static CallSiteDescriptor insertParameterTypes(CallSiteDescriptor desc, int num, Class<?>... ptypesToInsert) {
|
||||
public static CallSiteDescriptor insertParameterTypes(final CallSiteDescriptor desc, final int num, final Class<?>... ptypesToInsert) {
|
||||
return desc.changeMethodType(desc.getMethodType().insertParameterTypes(num, ptypesToInsert));
|
||||
}
|
||||
|
||||
@ -257,7 +267,7 @@ public class CallSiteDescriptorFactory {
|
||||
* @param ptypesToInsert the new types to insert
|
||||
* @return a new call site descriptor with modified method type
|
||||
*/
|
||||
public static CallSiteDescriptor insertParameterTypes(CallSiteDescriptor desc, int num, List<Class<?>> ptypesToInsert) {
|
||||
public static CallSiteDescriptor insertParameterTypes(final CallSiteDescriptor desc, final int num, final List<Class<?>> ptypesToInsert) {
|
||||
return desc.changeMethodType(desc.getMethodType().insertParameterTypes(num, ptypesToInsert));
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ public abstract class ClassMap<T> {
|
||||
*
|
||||
* @param classLoader the classloader that determines strong referenceability.
|
||||
*/
|
||||
protected ClassMap(ClassLoader classLoader) {
|
||||
protected ClassMap(final ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
@ -109,16 +109,16 @@ public class CompositeGuardingDynamicLinker implements GuardingDynamicLinker, Se
|
||||
*
|
||||
* @param linkers a list of component linkers.
|
||||
*/
|
||||
public CompositeGuardingDynamicLinker(Iterable<? extends GuardingDynamicLinker> linkers) {
|
||||
public CompositeGuardingDynamicLinker(final Iterable<? extends GuardingDynamicLinker> linkers) {
|
||||
final List<GuardingDynamicLinker> l = new LinkedList<>();
|
||||
for(GuardingDynamicLinker linker: linkers) {
|
||||
for(final GuardingDynamicLinker linker: linkers) {
|
||||
l.add(linker);
|
||||
}
|
||||
this.linkers = l.toArray(new GuardingDynamicLinker[l.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, final LinkerServices linkerServices)
|
||||
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
|
||||
throws Exception {
|
||||
for(final GuardingDynamicLinker linker: linkers) {
|
||||
final GuardedInvocation invocation = linker.getGuardedInvocation(linkRequest, linkerServices);
|
||||
|
@ -112,7 +112,7 @@ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardin
|
||||
private final List<TypeBasedGuardingDynamicLinker>[] singletonLinkers;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ClassToLinker(TypeBasedGuardingDynamicLinker[] linkers) {
|
||||
ClassToLinker(final TypeBasedGuardingDynamicLinker[] linkers) {
|
||||
this.linkers = linkers;
|
||||
singletonLinkers = new List[linkers.length];
|
||||
for(int i = 0; i < linkers.length; ++i) {
|
||||
@ -121,7 +121,7 @@ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardin
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<TypeBasedGuardingDynamicLinker> computeValue(Class<?> clazz) {
|
||||
protected List<TypeBasedGuardingDynamicLinker> computeValue(final Class<?> clazz) {
|
||||
List<TypeBasedGuardingDynamicLinker> list = NO_LINKER;
|
||||
for(int i = 0; i < linkers.length; ++i) {
|
||||
final TypeBasedGuardingDynamicLinker linker = linkers[i];
|
||||
@ -152,27 +152,27 @@ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardin
|
||||
*
|
||||
* @param linkers the component linkers
|
||||
*/
|
||||
public CompositeTypeBasedGuardingDynamicLinker(Iterable<? extends TypeBasedGuardingDynamicLinker> linkers) {
|
||||
public CompositeTypeBasedGuardingDynamicLinker(final Iterable<? extends TypeBasedGuardingDynamicLinker> linkers) {
|
||||
final List<TypeBasedGuardingDynamicLinker> l = new LinkedList<>();
|
||||
for(TypeBasedGuardingDynamicLinker linker: linkers) {
|
||||
for(final TypeBasedGuardingDynamicLinker linker: linkers) {
|
||||
l.add(linker);
|
||||
}
|
||||
this.classToLinker = new ClassToLinker(l.toArray(new TypeBasedGuardingDynamicLinker[l.size()]));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canLinkType(Class<?> type) {
|
||||
public boolean canLinkType(final Class<?> type) {
|
||||
return !classToLinker.get(type).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest, final LinkerServices linkerServices)
|
||||
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest, final LinkerServices linkerServices)
|
||||
throws Exception {
|
||||
final Object obj = linkRequest.getReceiver();
|
||||
if(obj == null) {
|
||||
return null;
|
||||
}
|
||||
for(TypeBasedGuardingDynamicLinker linker: classToLinker.get(obj.getClass())) {
|
||||
for(final TypeBasedGuardingDynamicLinker linker: classToLinker.get(obj.getClass())) {
|
||||
final GuardedInvocation invocation = linker.getGuardedInvocation(linkRequest, linkerServices);
|
||||
if(invocation != null) {
|
||||
return invocation;
|
||||
@ -189,10 +189,10 @@ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardin
|
||||
* @param linkers the list of linkers to optimize
|
||||
* @return the optimized list
|
||||
*/
|
||||
public static List<GuardingDynamicLinker> optimize(Iterable<? extends GuardingDynamicLinker> linkers) {
|
||||
public static List<GuardingDynamicLinker> optimize(final Iterable<? extends GuardingDynamicLinker> linkers) {
|
||||
final List<GuardingDynamicLinker> llinkers = new LinkedList<>();
|
||||
final List<TypeBasedGuardingDynamicLinker> tblinkers = new LinkedList<>();
|
||||
for(GuardingDynamicLinker linker: linkers) {
|
||||
for(final GuardingDynamicLinker linker: linkers) {
|
||||
if(linker instanceof TypeBasedGuardingDynamicLinker) {
|
||||
tblinkers.add((TypeBasedGuardingDynamicLinker)linker);
|
||||
} else {
|
||||
@ -204,8 +204,8 @@ public class CompositeTypeBasedGuardingDynamicLinker implements TypeBasedGuardin
|
||||
return llinkers;
|
||||
}
|
||||
|
||||
private static void addTypeBased(List<GuardingDynamicLinker> llinkers,
|
||||
List<TypeBasedGuardingDynamicLinker> tblinkers) {
|
||||
private static void addTypeBased(final List<GuardingDynamicLinker> llinkers,
|
||||
final List<TypeBasedGuardingDynamicLinker> tblinkers) {
|
||||
switch(tblinkers.size()) {
|
||||
case 0: {
|
||||
break;
|
||||
|
@ -98,7 +98,7 @@ class DefaultCallSiteDescriptor extends AbstractCallSiteDescriptor {
|
||||
private final String[] tokenizedName;
|
||||
private final MethodType methodType;
|
||||
|
||||
DefaultCallSiteDescriptor(String[] tokenizedName, MethodType methodType) {
|
||||
DefaultCallSiteDescriptor(final String[] tokenizedName, final MethodType methodType) {
|
||||
this.tokenizedName = tokenizedName;
|
||||
this.methodType = methodType;
|
||||
}
|
||||
@ -109,10 +109,10 @@ class DefaultCallSiteDescriptor extends AbstractCallSiteDescriptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameToken(int i) {
|
||||
public String getNameToken(final int i) {
|
||||
try {
|
||||
return tokenizedName[i];
|
||||
} catch(ArrayIndexOutOfBoundsException e) {
|
||||
} catch(final ArrayIndexOutOfBoundsException e) {
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -127,7 +127,7 @@ class DefaultCallSiteDescriptor extends AbstractCallSiteDescriptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallSiteDescriptor changeMethodType(MethodType newMethodType) {
|
||||
public CallSiteDescriptor changeMethodType(final MethodType newMethodType) {
|
||||
return CallSiteDescriptorFactory.getCanonicalPublicDescriptor(new DefaultCallSiteDescriptor(tokenizedName,
|
||||
newMethodType));
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, 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 file is available under and governed by the GNU General Public
|
||||
* License version 2 only, as published by the Free Software Foundation.
|
||||
* However, the following notice accompanied the original version of this
|
||||
* file, and Oracle licenses the original version of this file under the BSD
|
||||
* license:
|
||||
*/
|
||||
/*
|
||||
Copyright 2009-2013 Attila Szegedi
|
||||
|
||||
Licensed under both the Apache License, Version 2.0 (the "Apache License")
|
||||
and the BSD License (the "BSD License"), with licensee being free to
|
||||
choose either of the two at their discretion.
|
||||
|
||||
You may not use this file except in compliance with either the Apache
|
||||
License or the BSD License.
|
||||
|
||||
If you choose to use this file in compliance with the Apache License, the
|
||||
following notice applies to you:
|
||||
|
||||
You may obtain a copy of the Apache License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied. See the License for the specific language governing
|
||||
permissions and limitations under the License.
|
||||
|
||||
If you choose to use this file in compliance with the BSD License, the
|
||||
following notice applies to you:
|
||||
|
||||
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 the copyright holder nor the names of
|
||||
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 COPYRIGHT HOLDER
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package jdk.internal.dynalink.support;
|
||||
|
||||
import jdk.internal.dynalink.GuardedInvocationFilter;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.internal.dynalink.linker.LinkerServices;
|
||||
|
||||
/**
|
||||
* Default filter for guarded invocation pre link filtering
|
||||
*/
|
||||
public class DefaultPrelinkFilter implements GuardedInvocationFilter {
|
||||
@Override
|
||||
public GuardedInvocation filter(final GuardedInvocation inv, final LinkRequest request, final LinkerServices linkerServices) {
|
||||
return inv.asType(linkerServices, request.getCallSiteDescriptor().getMethodType());
|
||||
}
|
||||
}
|
@ -113,7 +113,7 @@ public class Guards {
|
||||
* @return a method handle testing whether its first argument is of the specified class.
|
||||
*/
|
||||
@SuppressWarnings("boxing")
|
||||
public static MethodHandle isOfClass(Class<?> clazz, MethodType type) {
|
||||
public static MethodHandle isOfClass(final Class<?> clazz, final MethodType type) {
|
||||
final Class<?> declaredType = type.parameterType(0);
|
||||
if(clazz == declaredType) {
|
||||
LOG.log(Level.WARNING, "isOfClassGuardAlwaysTrue", new Object[] { clazz.getName(), 0, type, DynamicLinker.getLinkedCallSiteLocation() });
|
||||
@ -135,7 +135,7 @@ public class Guards {
|
||||
* @param type the method type
|
||||
* @return a method handle testing whether its first argument is of the specified class or subclass.
|
||||
*/
|
||||
public static MethodHandle isInstance(Class<?> clazz, MethodType type) {
|
||||
public static MethodHandle isInstance(final Class<?> clazz, final MethodType type) {
|
||||
return isInstance(clazz, 0, type);
|
||||
}
|
||||
|
||||
@ -150,7 +150,7 @@ public class Guards {
|
||||
* @return a method handle testing whether its first argument is of the specified class or subclass.
|
||||
*/
|
||||
@SuppressWarnings("boxing")
|
||||
public static MethodHandle isInstance(Class<?> clazz, int pos, MethodType type) {
|
||||
public static MethodHandle isInstance(final Class<?> clazz, final int pos, final MethodType type) {
|
||||
final Class<?> declaredType = type.parameterType(pos);
|
||||
if(clazz.isAssignableFrom(declaredType)) {
|
||||
LOG.log(Level.WARNING, "isInstanceGuardAlwaysTrue", new Object[] { clazz.getName(), pos, type, DynamicLinker.getLinkedCallSiteLocation() });
|
||||
@ -172,7 +172,7 @@ public class Guards {
|
||||
* the arguments are ignored.
|
||||
*/
|
||||
@SuppressWarnings("boxing")
|
||||
public static MethodHandle isArray(int pos, MethodType type) {
|
||||
public static MethodHandle isArray(final int pos, final MethodType type) {
|
||||
final Class<?> declaredType = type.parameterType(pos);
|
||||
if(declaredType.isArray()) {
|
||||
LOG.log(Level.WARNING, "isArrayGuardAlwaysTrue", new Object[] { pos, type, DynamicLinker.getLinkedCallSiteLocation() });
|
||||
@ -193,7 +193,7 @@ public class Guards {
|
||||
* @param referredLoader the referred class loader
|
||||
* @return true if it is safe to strongly reference the class
|
||||
*/
|
||||
public static boolean canReferenceDirectly(ClassLoader referrerLoader, final ClassLoader referredLoader) {
|
||||
public static boolean canReferenceDirectly(final ClassLoader referrerLoader, final ClassLoader referredLoader) {
|
||||
if(referredLoader == null) {
|
||||
// Can always refer directly to a system class
|
||||
return true;
|
||||
@ -215,7 +215,7 @@ public class Guards {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static MethodHandle getClassBoundArgumentTest(MethodHandle test, Class<?> clazz, int pos, MethodType type) {
|
||||
private static MethodHandle getClassBoundArgumentTest(final MethodHandle test, final Class<?> clazz, final int pos, final MethodType type) {
|
||||
// Bind the class to the first argument of the test
|
||||
return asType(test.bindTo(clazz), pos, type);
|
||||
}
|
||||
@ -227,7 +227,7 @@ public class Guards {
|
||||
* @param type the type to adapt the method handle to
|
||||
* @return the adapted method handle
|
||||
*/
|
||||
public static MethodHandle asType(MethodHandle test, MethodType type) {
|
||||
public static MethodHandle asType(final MethodHandle test, final MethodType type) {
|
||||
return test.asType(getTestType(test, type));
|
||||
}
|
||||
|
||||
@ -239,16 +239,16 @@ public class Guards {
|
||||
* @param type the type to adapt the method handle to
|
||||
* @return the adapted method handle
|
||||
*/
|
||||
public static MethodHandle asType(LinkerServices linkerServices, MethodHandle test, MethodType type) {
|
||||
public static MethodHandle asType(final LinkerServices linkerServices, final MethodHandle test, final MethodType type) {
|
||||
return linkerServices.asType(test, getTestType(test, type));
|
||||
}
|
||||
|
||||
private static MethodType getTestType(MethodHandle test, MethodType type) {
|
||||
private static MethodType getTestType(final MethodHandle test, final MethodType type) {
|
||||
return type.dropParameterTypes(test.type().parameterCount(),
|
||||
type.parameterCount()).changeReturnType(boolean.class);
|
||||
}
|
||||
|
||||
private static MethodHandle asType(MethodHandle test, int pos, MethodType type) {
|
||||
private static MethodHandle asType(final MethodHandle test, final int pos, final MethodType type) {
|
||||
assert test != null;
|
||||
assert type != null;
|
||||
assert type.parameterCount() > 0;
|
||||
@ -283,7 +283,7 @@ public class Guards {
|
||||
* @param clazz the class to test for.
|
||||
* @return the desired guard method.
|
||||
*/
|
||||
public static MethodHandle getClassGuard(Class<?> clazz) {
|
||||
public static MethodHandle getClassGuard(final Class<?> clazz) {
|
||||
return IS_OF_CLASS.bindTo(clazz);
|
||||
}
|
||||
|
||||
@ -292,7 +292,7 @@ public class Guards {
|
||||
* @param clazz the class to test for.
|
||||
* @return the desired guard method.
|
||||
*/
|
||||
public static MethodHandle getInstanceOfGuard(Class<?> clazz) {
|
||||
public static MethodHandle getInstanceOfGuard(final Class<?> clazz) {
|
||||
return IS_INSTANCE.bindTo(clazz);
|
||||
}
|
||||
|
||||
@ -301,7 +301,7 @@ public class Guards {
|
||||
* @param obj the object used as referential identity test
|
||||
* @return the desired guard method.
|
||||
*/
|
||||
public static MethodHandle getIdentityGuard(Object obj) {
|
||||
public static MethodHandle getIdentityGuard(final Object obj) {
|
||||
return IS_IDENTICAL.bindTo(obj);
|
||||
}
|
||||
|
||||
@ -322,39 +322,39 @@ public class Guards {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isNull(Object obj) {
|
||||
private static boolean isNull(final Object obj) {
|
||||
return obj == null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isNotNull(Object obj) {
|
||||
private static boolean isNotNull(final Object obj) {
|
||||
return obj != null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isArray(Object o) {
|
||||
private static boolean isArray(final Object o) {
|
||||
return o != null && o.getClass().isArray();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isOfClass(Class<?> c, Object o) {
|
||||
private static boolean isOfClass(final Class<?> c, final Object o) {
|
||||
return o != null && o.getClass() == c;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isIdentical(Object o1, Object o2) {
|
||||
private static boolean isIdentical(final Object o1, final Object o2) {
|
||||
return o1 == o2;
|
||||
}
|
||||
|
||||
private static MethodHandle constantTrue(MethodType type) {
|
||||
private static MethodHandle constantTrue(final MethodType type) {
|
||||
return constantBoolean(Boolean.TRUE, type);
|
||||
}
|
||||
|
||||
private static MethodHandle constantFalse(MethodType type) {
|
||||
private static MethodHandle constantFalse(final MethodType type) {
|
||||
return constantBoolean(Boolean.FALSE, type);
|
||||
}
|
||||
|
||||
private static MethodHandle constantBoolean(Boolean value, MethodType type) {
|
||||
private static MethodHandle constantBoolean(final Boolean value, final MethodType type) {
|
||||
return MethodHandles.permuteArguments(MethodHandles.constant(Boolean.TYPE, value),
|
||||
type.changeReturnType(Boolean.TYPE));
|
||||
}
|
||||
|
@ -95,18 +95,24 @@ import jdk.internal.dynalink.linker.LinkRequest;
|
||||
public class LinkRequestImpl implements LinkRequest {
|
||||
|
||||
private final CallSiteDescriptor callSiteDescriptor;
|
||||
private final Object callSiteToken;
|
||||
private final Object[] arguments;
|
||||
private final boolean callSiteUnstable;
|
||||
private final int linkCount;
|
||||
|
||||
/**
|
||||
* Creates a new link request.
|
||||
*
|
||||
* @param callSiteDescriptor the descriptor for the call site being linked
|
||||
* @param callSiteToken the opaque token for the call site being linked.
|
||||
* @param linkCount how many times this callsite has been linked/relinked
|
||||
* @param callSiteUnstable true if the call site being linked is considered unstable
|
||||
* @param arguments the arguments for the invocation
|
||||
*/
|
||||
public LinkRequestImpl(CallSiteDescriptor callSiteDescriptor, boolean callSiteUnstable, Object... arguments) {
|
||||
public LinkRequestImpl(final CallSiteDescriptor callSiteDescriptor, final Object callSiteToken, final int linkCount, final boolean callSiteUnstable, final Object... arguments) {
|
||||
this.callSiteDescriptor = callSiteDescriptor;
|
||||
this.callSiteToken = callSiteToken;
|
||||
this.linkCount = linkCount;
|
||||
this.callSiteUnstable = callSiteUnstable;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
@ -126,18 +132,28 @@ public class LinkRequestImpl implements LinkRequest {
|
||||
return callSiteDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCallSiteToken() {
|
||||
return callSiteToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCallSiteUnstable() {
|
||||
return callSiteUnstable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLinkCount() {
|
||||
return linkCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkRequest withoutRuntimeContext() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkRequest replaceArguments(CallSiteDescriptor newCallSiteDescriptor, Object[] newArguments) {
|
||||
return new LinkRequestImpl(newCallSiteDescriptor, callSiteUnstable, newArguments);
|
||||
public LinkRequest replaceArguments(final CallSiteDescriptor newCallSiteDescriptor, final Object[] newArguments) {
|
||||
return new LinkRequestImpl(newCallSiteDescriptor, callSiteToken, linkCount, callSiteUnstable, newArguments);
|
||||
}
|
||||
}
|
||||
|
@ -117,27 +117,32 @@ public class LinkerServicesImpl implements LinkerServices {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canConvert(Class<?> from, Class<?> to) {
|
||||
public boolean canConvert(final Class<?> from, final Class<?> to) {
|
||||
return typeConverterFactory.canConvert(from, to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle asType(MethodHandle handle, MethodType fromType) {
|
||||
public MethodHandle asType(final MethodHandle handle, final MethodType fromType) {
|
||||
return typeConverterFactory.asType(handle, fromType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle getTypeConverter(Class<?> sourceType, Class<?> targetType) {
|
||||
public MethodHandle asTypeLosslessReturn(final MethodHandle handle, final MethodType fromType) {
|
||||
return Implementation.asTypeLosslessReturn(this, handle, fromType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
|
||||
return typeConverterFactory.getTypeConverter(sourceType, targetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2) {
|
||||
public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
|
||||
return typeConverterFactory.compareConversion(sourceType, targetType1, targetType2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation getGuardedInvocation(LinkRequest linkRequest) throws Exception {
|
||||
public GuardedInvocation getGuardedInvocation(final LinkRequest linkRequest) throws Exception {
|
||||
final LinkRequest prevLinkRequest = threadLinkRequest.get();
|
||||
threadLinkRequest.set(linkRequest);
|
||||
try {
|
||||
@ -154,7 +159,7 @@ public class LinkerServicesImpl implements LinkerServices {
|
||||
* permission.
|
||||
*/
|
||||
public static LinkRequest getCurrentLinkRequest() {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
final SecurityManager sm = System.getSecurityManager();
|
||||
if(sm != null) {
|
||||
sm.checkPermission(GET_CURRENT_LINK_REQUEST);
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ public class Lookup {
|
||||
*
|
||||
* @param lookup the {@link java.lang.invoke.MethodHandles.Lookup} it delegates to.
|
||||
*/
|
||||
public Lookup(MethodHandles.Lookup lookup) {
|
||||
public Lookup(final MethodHandles.Lookup lookup) {
|
||||
this.lookup = lookup;
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ public class Lookup {
|
||||
* @param m the method to unreflect
|
||||
* @return the unreflected method handle.
|
||||
*/
|
||||
public MethodHandle unreflect(Method m) {
|
||||
public MethodHandle unreflect(final Method m) {
|
||||
return unreflect(lookup, m);
|
||||
}
|
||||
|
||||
@ -132,10 +132,10 @@ public class Lookup {
|
||||
* @param m the method to unreflect
|
||||
* @return the unreflected method handle.
|
||||
*/
|
||||
public static MethodHandle unreflect(MethodHandles.Lookup lookup, Method m) {
|
||||
public static MethodHandle unreflect(final MethodHandles.Lookup lookup, final Method m) {
|
||||
try {
|
||||
return lookup.unreflect(m);
|
||||
} catch(IllegalAccessException e) {
|
||||
} catch(final IllegalAccessException e) {
|
||||
final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect method " + m);
|
||||
ee.initCause(e);
|
||||
throw ee;
|
||||
@ -149,10 +149,10 @@ public class Lookup {
|
||||
* @param f the field for which a getter is unreflected
|
||||
* @return the unreflected field getter handle.
|
||||
*/
|
||||
public MethodHandle unreflectGetter(Field f) {
|
||||
public MethodHandle unreflectGetter(final Field f) {
|
||||
try {
|
||||
return lookup.unreflectGetter(f);
|
||||
} catch(IllegalAccessException e) {
|
||||
} catch(final IllegalAccessException e) {
|
||||
final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect getter for field " + f);
|
||||
ee.initCause(e);
|
||||
throw ee;
|
||||
@ -171,15 +171,15 @@ public class Lookup {
|
||||
* @throws IllegalAccessError if the field is inaccessible.
|
||||
* @throws NoSuchFieldError if the field does not exist.
|
||||
*/
|
||||
public MethodHandle findGetter(Class<?>refc, String name, Class<?> type) {
|
||||
public MethodHandle findGetter(final Class<?>refc, final String name, final Class<?> type) {
|
||||
try {
|
||||
return lookup.findGetter(refc, name, type);
|
||||
} catch(IllegalAccessException e) {
|
||||
} catch(final IllegalAccessException e) {
|
||||
final IllegalAccessError ee = new IllegalAccessError("Failed to access getter for field " + refc.getName() +
|
||||
"." + name + " of type " + type.getName());
|
||||
ee.initCause(e);
|
||||
throw ee;
|
||||
} catch(NoSuchFieldException e) {
|
||||
} catch(final NoSuchFieldException e) {
|
||||
final NoSuchFieldError ee = new NoSuchFieldError("Failed to find getter for field " + refc.getName() +
|
||||
"." + name + " of type " + type.getName());
|
||||
ee.initCause(e);
|
||||
@ -194,10 +194,10 @@ public class Lookup {
|
||||
* @param f the field for which a setter is unreflected
|
||||
* @return the unreflected field setter handle.
|
||||
*/
|
||||
public MethodHandle unreflectSetter(Field f) {
|
||||
public MethodHandle unreflectSetter(final Field f) {
|
||||
try {
|
||||
return lookup.unreflectSetter(f);
|
||||
} catch(IllegalAccessException e) {
|
||||
} catch(final IllegalAccessException e) {
|
||||
final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect setter for field " + f);
|
||||
ee.initCause(e);
|
||||
throw ee;
|
||||
@ -211,7 +211,7 @@ public class Lookup {
|
||||
* @param c the constructor to unreflect
|
||||
* @return the unreflected constructor handle.
|
||||
*/
|
||||
public MethodHandle unreflectConstructor(Constructor<?> c) {
|
||||
public MethodHandle unreflectConstructor(final Constructor<?> c) {
|
||||
return unreflectConstructor(lookup, c);
|
||||
}
|
||||
|
||||
@ -223,10 +223,10 @@ public class Lookup {
|
||||
* @param c the constructor to unreflect
|
||||
* @return the unreflected constructor handle.
|
||||
*/
|
||||
public static MethodHandle unreflectConstructor(MethodHandles.Lookup lookup, Constructor<?> c) {
|
||||
public static MethodHandle unreflectConstructor(final MethodHandles.Lookup lookup, final Constructor<?> c) {
|
||||
try {
|
||||
return lookup.unreflectConstructor(c);
|
||||
} catch(IllegalAccessException e) {
|
||||
} catch(final IllegalAccessException e) {
|
||||
final IllegalAccessError ee = new IllegalAccessError("Failed to unreflect constructor " + c);
|
||||
ee.initCause(e);
|
||||
throw ee;
|
||||
@ -244,15 +244,15 @@ public class Lookup {
|
||||
* @throws IllegalAccessError if the method is inaccessible.
|
||||
* @throws NoSuchMethodError if the method does not exist.
|
||||
*/
|
||||
public MethodHandle findSpecial(Class<?> declaringClass, String name, MethodType type) {
|
||||
public MethodHandle findSpecial(final Class<?> declaringClass, final String name, final MethodType type) {
|
||||
try {
|
||||
return lookup.findSpecial(declaringClass, name, type, declaringClass);
|
||||
} catch(IllegalAccessException e) {
|
||||
} catch(final IllegalAccessException e) {
|
||||
final IllegalAccessError ee = new IllegalAccessError("Failed to access special method " + methodDescription(
|
||||
declaringClass, name, type));
|
||||
ee.initCause(e);
|
||||
throw ee;
|
||||
} catch(NoSuchMethodException e) {
|
||||
} catch(final NoSuchMethodException e) {
|
||||
final NoSuchMethodError ee = new NoSuchMethodError("Failed to find special method " + methodDescription(
|
||||
declaringClass, name, type));
|
||||
ee.initCause(e);
|
||||
@ -260,7 +260,7 @@ public class Lookup {
|
||||
}
|
||||
}
|
||||
|
||||
private static String methodDescription(Class<?> declaringClass, String name, MethodType type) {
|
||||
private static String methodDescription(final Class<?> declaringClass, final String name, final MethodType type) {
|
||||
return declaringClass.getName() + "#" + name + type;
|
||||
}
|
||||
|
||||
@ -275,15 +275,15 @@ public class Lookup {
|
||||
* @throws IllegalAccessError if the method is inaccessible.
|
||||
* @throws NoSuchMethodError if the method does not exist.
|
||||
*/
|
||||
public MethodHandle findStatic(Class<?> declaringClass, String name, MethodType type) {
|
||||
public MethodHandle findStatic(final Class<?> declaringClass, final String name, final MethodType type) {
|
||||
try {
|
||||
return lookup.findStatic(declaringClass, name, type);
|
||||
} catch(IllegalAccessException e) {
|
||||
} catch(final IllegalAccessException e) {
|
||||
final IllegalAccessError ee = new IllegalAccessError("Failed to access static method " + methodDescription(
|
||||
declaringClass, name, type));
|
||||
ee.initCause(e);
|
||||
throw ee;
|
||||
} catch(NoSuchMethodException e) {
|
||||
} catch(final NoSuchMethodException e) {
|
||||
final NoSuchMethodError ee = new NoSuchMethodError("Failed to find static method " + methodDescription(
|
||||
declaringClass, name, type));
|
||||
ee.initCause(e);
|
||||
@ -302,15 +302,15 @@ public class Lookup {
|
||||
* @throws IllegalAccessError if the method is inaccessible.
|
||||
* @throws NoSuchMethodError if the method does not exist.
|
||||
*/
|
||||
public MethodHandle findVirtual(Class<?> declaringClass, String name, MethodType type) {
|
||||
public MethodHandle findVirtual(final Class<?> declaringClass, final String name, final MethodType type) {
|
||||
try {
|
||||
return lookup.findVirtual(declaringClass, name, type);
|
||||
} catch(IllegalAccessException e) {
|
||||
} catch(final IllegalAccessException e) {
|
||||
final IllegalAccessError ee = new IllegalAccessError("Failed to access virtual method " + methodDescription(
|
||||
declaringClass, name, type));
|
||||
ee.initCause(e);
|
||||
throw ee;
|
||||
} catch(NoSuchMethodException e) {
|
||||
} catch(final NoSuchMethodException e) {
|
||||
final NoSuchMethodError ee = new NoSuchMethodError("Failed to find virtual method " + methodDescription(
|
||||
declaringClass, name, type));
|
||||
ee.initCause(e);
|
||||
@ -327,7 +327,7 @@ public class Lookup {
|
||||
* @param ptypes the parameter types of the method
|
||||
* @return the method handle for the method
|
||||
*/
|
||||
public static MethodHandle findOwnSpecial(MethodHandles.Lookup lookup, String name, Class<?> rtype, Class<?>... ptypes) {
|
||||
public static MethodHandle findOwnSpecial(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes) {
|
||||
return new Lookup(lookup).findOwnSpecial(name, rtype, ptypes);
|
||||
}
|
||||
|
||||
@ -341,7 +341,7 @@ public class Lookup {
|
||||
* @param ptypes the parameter types of the method
|
||||
* @return the method handle for the method
|
||||
*/
|
||||
public MethodHandle findOwnSpecial(String name, Class<?> rtype, Class<?>... ptypes) {
|
||||
public MethodHandle findOwnSpecial(final String name, final Class<?> rtype, final Class<?>... ptypes) {
|
||||
return findSpecial(lookup.lookupClass(), name, MethodType.methodType(rtype, ptypes));
|
||||
}
|
||||
|
||||
@ -355,7 +355,7 @@ public class Lookup {
|
||||
* @param ptypes the parameter types of the method
|
||||
* @return the method handle for the method
|
||||
*/
|
||||
public static MethodHandle findOwnStatic(MethodHandles.Lookup lookup, String name, Class<?> rtype, Class<?>... ptypes) {
|
||||
public static MethodHandle findOwnStatic(final MethodHandles.Lookup lookup, final String name, final Class<?> rtype, final Class<?>... ptypes) {
|
||||
return new Lookup(lookup).findOwnStatic(name, rtype, ptypes);
|
||||
}
|
||||
|
||||
@ -368,7 +368,7 @@ public class Lookup {
|
||||
* @param ptypes the parameter types of the method
|
||||
* @return the method handle for the method
|
||||
*/
|
||||
public MethodHandle findOwnStatic(String name, Class<?> rtype, Class<?>... ptypes) {
|
||||
public MethodHandle findOwnStatic(final String name, final Class<?> rtype, final Class<?>... ptypes) {
|
||||
return findStatic(lookup.lookupClass(), name, MethodType.methodType(rtype, ptypes));
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
* @author Attila Szegedi
|
||||
*/
|
||||
class LookupCallSiteDescriptor extends DefaultCallSiteDescriptor {
|
||||
private Lookup lookup;
|
||||
private final Lookup lookup;
|
||||
|
||||
/**
|
||||
* Create a new call site descriptor from explicit information.
|
||||
@ -100,7 +100,7 @@ class LookupCallSiteDescriptor extends DefaultCallSiteDescriptor {
|
||||
* @param methodType the method type
|
||||
* @param lookup the lookup
|
||||
*/
|
||||
LookupCallSiteDescriptor(String[] tokenizedName, MethodType methodType, Lookup lookup) {
|
||||
LookupCallSiteDescriptor(final String[] tokenizedName, final MethodType methodType, final Lookup lookup) {
|
||||
super(tokenizedName, methodType);
|
||||
this.lookup = lookup;
|
||||
}
|
||||
@ -111,7 +111,7 @@ class LookupCallSiteDescriptor extends DefaultCallSiteDescriptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallSiteDescriptor changeMethodType(MethodType newMethodType) {
|
||||
public CallSiteDescriptor changeMethodType(final MethodType newMethodType) {
|
||||
return new LookupCallSiteDescriptor(getTokenizedName(), newMethodType, lookup);
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ public class NameCodec {
|
||||
* @param name the symbolic name to mangle
|
||||
* @return the mangled form of the symbolic name.
|
||||
*/
|
||||
public static String encode(String name) {
|
||||
public static String encode(final String name) {
|
||||
final int l = name.length();
|
||||
if(l == 0) {
|
||||
return EMPTY_NAME;
|
||||
@ -176,7 +176,7 @@ public class NameCodec {
|
||||
* @param name the symbolic name to demangle
|
||||
* @return the demangled form of the symbolic name.
|
||||
*/
|
||||
public static String decode(String name) {
|
||||
public static String decode(final String name) {
|
||||
if(name.charAt(0) != ESCAPE_CHAR) {
|
||||
return name;
|
||||
}
|
||||
@ -184,11 +184,11 @@ public class NameCodec {
|
||||
if(l == 2 && name.charAt(1) == EMPTY_CHAR) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder b = new StringBuilder(name.length());
|
||||
final StringBuilder b = new StringBuilder(name.length());
|
||||
int lastEscape = -2;
|
||||
int lastBackslash = -1;
|
||||
for(;;) {
|
||||
int nextBackslash = name.indexOf(ESCAPE_CHAR, lastBackslash + 1);
|
||||
final int nextBackslash = name.indexOf(ESCAPE_CHAR, lastBackslash + 1);
|
||||
if(nextBackslash == -1 || nextBackslash == l - 1) {
|
||||
break;
|
||||
}
|
||||
@ -211,7 +211,7 @@ public class NameCodec {
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private static void addEncoding(char from, char to) {
|
||||
private static void addEncoding(final char from, final char to) {
|
||||
ENCODING[from - MIN_ENCODING] = to;
|
||||
DECODING[to - MIN_DECODING] = from;
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
class NamedDynCallSiteDescriptor extends UnnamedDynCallSiteDescriptor {
|
||||
private final String name;
|
||||
|
||||
NamedDynCallSiteDescriptor(String op, String name, MethodType methodType) {
|
||||
NamedDynCallSiteDescriptor(final String op, final String name, final MethodType methodType) {
|
||||
super(op, methodType);
|
||||
this.name = name;
|
||||
}
|
||||
@ -100,7 +100,7 @@ class NamedDynCallSiteDescriptor extends UnnamedDynCallSiteDescriptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameToken(int i) {
|
||||
public String getNameToken(final int i) {
|
||||
switch(i) {
|
||||
case 0: return "dyn";
|
||||
case 1: return getOp();
|
||||
@ -110,7 +110,7 @@ class NamedDynCallSiteDescriptor extends UnnamedDynCallSiteDescriptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallSiteDescriptor changeMethodType(MethodType newMethodType) {
|
||||
public CallSiteDescriptor changeMethodType(final MethodType newMethodType) {
|
||||
return CallSiteDescriptorFactory.getCanonicalPublicDescriptor(new NamedDynCallSiteDescriptor(getOp(), name,
|
||||
newMethodType));
|
||||
}
|
||||
|
@ -101,15 +101,17 @@ public class RuntimeContextLinkRequestImpl extends LinkRequestImpl {
|
||||
* Creates a new link request.
|
||||
*
|
||||
* @param callSiteDescriptor the descriptor for the call site being linked
|
||||
* @param callSiteToken the opaque token for the call site being linked.
|
||||
* @param arguments the arguments for the invocation
|
||||
* @param linkCount number of times callsite has been linked/relinked
|
||||
* @param callSiteUnstable true if the call site being linked is considered unstable
|
||||
* @param runtimeContextArgCount the number of the leading arguments on the stack that represent the language
|
||||
* runtime specific context arguments.
|
||||
* @throws IllegalArgumentException if runtimeContextArgCount is less than 1.
|
||||
*/
|
||||
public RuntimeContextLinkRequestImpl(CallSiteDescriptor callSiteDescriptor, boolean callSiteUnstable,
|
||||
Object[] arguments, int runtimeContextArgCount) {
|
||||
super(callSiteDescriptor, callSiteUnstable, arguments);
|
||||
public RuntimeContextLinkRequestImpl(final CallSiteDescriptor callSiteDescriptor, final Object callSiteToken,
|
||||
final int linkCount, final boolean callSiteUnstable, final Object[] arguments, final int runtimeContextArgCount) {
|
||||
super(callSiteDescriptor, callSiteToken, linkCount, callSiteUnstable, arguments);
|
||||
if(runtimeContextArgCount < 1) {
|
||||
throw new IllegalArgumentException("runtimeContextArgCount < 1");
|
||||
}
|
||||
@ -121,14 +123,14 @@ public class RuntimeContextLinkRequestImpl extends LinkRequestImpl {
|
||||
if(contextStrippedRequest == null) {
|
||||
contextStrippedRequest =
|
||||
new LinkRequestImpl(CallSiteDescriptorFactory.dropParameterTypes(getCallSiteDescriptor(), 1,
|
||||
runtimeContextArgCount + 1), isCallSiteUnstable(), getTruncatedArguments());
|
||||
runtimeContextArgCount + 1), getCallSiteToken(), getLinkCount(), isCallSiteUnstable(), getTruncatedArguments());
|
||||
}
|
||||
return contextStrippedRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkRequest replaceArguments(CallSiteDescriptor callSiteDescriptor, Object[] arguments) {
|
||||
return new RuntimeContextLinkRequestImpl(callSiteDescriptor, isCallSiteUnstable(), arguments,
|
||||
public LinkRequest replaceArguments(final CallSiteDescriptor callSiteDescriptor, final Object[] arguments) {
|
||||
return new RuntimeContextLinkRequestImpl(callSiteDescriptor, getCallSiteToken(), getLinkCount(), isCallSiteUnstable(), arguments,
|
||||
runtimeContextArgCount);
|
||||
}
|
||||
|
||||
|
@ -115,12 +115,12 @@ public class TypeConverterFactory {
|
||||
protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) {
|
||||
return new ClassMap<MethodHandle>(getClassLoader(sourceType)) {
|
||||
@Override
|
||||
protected MethodHandle computeValue(Class<?> targetType) {
|
||||
protected MethodHandle computeValue(final Class<?> targetType) {
|
||||
try {
|
||||
return createConverter(sourceType, targetType);
|
||||
} catch (RuntimeException e) {
|
||||
} catch (final RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
@ -133,7 +133,7 @@ public class TypeConverterFactory {
|
||||
protected ClassMap<MethodHandle> computeValue(final Class<?> sourceType) {
|
||||
return new ClassMap<MethodHandle>(getClassLoader(sourceType)) {
|
||||
@Override
|
||||
protected MethodHandle computeValue(Class<?> targetType) {
|
||||
protected MethodHandle computeValue(final Class<?> targetType) {
|
||||
if(!canAutoConvert(sourceType, targetType)) {
|
||||
final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType);
|
||||
if(converter != IDENTITY_CONVERSION) {
|
||||
@ -151,12 +151,12 @@ public class TypeConverterFactory {
|
||||
protected ClassMap<Boolean> computeValue(final Class<?> sourceType) {
|
||||
return new ClassMap<Boolean>(getClassLoader(sourceType)) {
|
||||
@Override
|
||||
protected Boolean computeValue(Class<?> targetType) {
|
||||
protected Boolean computeValue(final Class<?> targetType) {
|
||||
try {
|
||||
return getTypeConverterNull(sourceType, targetType) != null;
|
||||
} catch (RuntimeException e) {
|
||||
} catch (final RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
@ -178,10 +178,10 @@ public class TypeConverterFactory {
|
||||
*
|
||||
* @param factories the {@link GuardingTypeConverterFactory} instances to compose.
|
||||
*/
|
||||
public TypeConverterFactory(Iterable<? extends GuardingTypeConverterFactory> factories) {
|
||||
public TypeConverterFactory(final Iterable<? extends GuardingTypeConverterFactory> factories) {
|
||||
final List<GuardingTypeConverterFactory> l = new LinkedList<>();
|
||||
final List<ConversionComparator> c = new LinkedList<>();
|
||||
for(GuardingTypeConverterFactory factory: factories) {
|
||||
for(final GuardingTypeConverterFactory factory: factories) {
|
||||
l.add(factory);
|
||||
if(factory instanceof ConversionComparator) {
|
||||
c.add((ConversionComparator)factory);
|
||||
@ -206,7 +206,7 @@ public class TypeConverterFactory {
|
||||
* {@link MethodHandles#filterArguments(MethodHandle, int, MethodHandle...)} with
|
||||
* {@link GuardingTypeConverterFactory} produced type converters as filters.
|
||||
*/
|
||||
public MethodHandle asType(MethodHandle handle, final MethodType fromType) {
|
||||
public MethodHandle asType(final MethodHandle handle, final MethodType fromType) {
|
||||
MethodHandle newHandle = handle;
|
||||
final MethodType toType = newHandle.type();
|
||||
final int l = toType.parameterCount();
|
||||
@ -250,7 +250,7 @@ public class TypeConverterFactory {
|
||||
return newHandle.asType(fromType);
|
||||
}
|
||||
|
||||
private static MethodHandle applyConverters(MethodHandle handle, int pos, List<MethodHandle> converters) {
|
||||
private static MethodHandle applyConverters(final MethodHandle handle, final int pos, final List<MethodHandle> converters) {
|
||||
if(converters.isEmpty()) {
|
||||
return handle;
|
||||
}
|
||||
@ -285,8 +285,8 @@ public class TypeConverterFactory {
|
||||
* @return one of Comparison constants that establish which - if any - of the target types is preferable for the
|
||||
* conversion.
|
||||
*/
|
||||
public Comparison compareConversion(Class<?> sourceType, Class<?> targetType1, Class<?> targetType2) {
|
||||
for(ConversionComparator comparator: comparators) {
|
||||
public Comparison compareConversion(final Class<?> sourceType, final Class<?> targetType1, final Class<?> targetType2) {
|
||||
for(final ConversionComparator comparator: comparators) {
|
||||
final Comparison result = comparator.compareConversion(sourceType, targetType1, targetType2);
|
||||
if(result != Comparison.INDETERMINATE) {
|
||||
return result;
|
||||
@ -313,20 +313,20 @@ public class TypeConverterFactory {
|
||||
return TypeUtilities.isMethodInvocationConvertible(fromType, toType);
|
||||
}
|
||||
|
||||
/*private*/ MethodHandle getCacheableTypeConverterNull(Class<?> sourceType, Class<?> targetType) {
|
||||
/*private*/ MethodHandle getCacheableTypeConverterNull(final Class<?> sourceType, final Class<?> targetType) {
|
||||
final MethodHandle converter = getCacheableTypeConverter(sourceType, targetType);
|
||||
return converter == IDENTITY_CONVERSION ? null : converter;
|
||||
}
|
||||
|
||||
/*private*/ MethodHandle getTypeConverterNull(Class<?> sourceType, Class<?> targetType) {
|
||||
/*private*/ MethodHandle getTypeConverterNull(final Class<?> sourceType, final Class<?> targetType) {
|
||||
try {
|
||||
return getCacheableTypeConverterNull(sourceType, targetType);
|
||||
} catch(NotCacheableConverter e) {
|
||||
} catch(final NotCacheableConverter e) {
|
||||
return e.converter;
|
||||
}
|
||||
}
|
||||
|
||||
/*private*/ MethodHandle getCacheableTypeConverter(Class<?> sourceType, Class<?> targetType) {
|
||||
/*private*/ MethodHandle getCacheableTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
|
||||
return converterMap.get(sourceType).get(targetType);
|
||||
}
|
||||
|
||||
@ -339,15 +339,15 @@ public class TypeConverterFactory {
|
||||
* @param targetType the type to convert to
|
||||
* @return a method handle performing the conversion.
|
||||
*/
|
||||
public MethodHandle getTypeConverter(Class<?> sourceType, Class<?> targetType) {
|
||||
public MethodHandle getTypeConverter(final Class<?> sourceType, final Class<?> targetType) {
|
||||
try {
|
||||
return converterIdentityMap.get(sourceType).get(targetType);
|
||||
} catch(NotCacheableConverter e) {
|
||||
} catch(final NotCacheableConverter e) {
|
||||
return e.converter;
|
||||
}
|
||||
}
|
||||
|
||||
/*private*/ MethodHandle createConverter(Class<?> sourceType, Class<?> targetType) throws Exception {
|
||||
/*private*/ MethodHandle createConverter(final Class<?> sourceType, final Class<?> targetType) throws Exception {
|
||||
final MethodType type = MethodType.methodType(targetType, sourceType);
|
||||
final MethodHandle identity = IDENTITY_CONVERSION.asType(type);
|
||||
MethodHandle last = identity;
|
||||
@ -372,6 +372,7 @@ public class TypeConverterFactory {
|
||||
|
||||
/*private*/ static final MethodHandle IDENTITY_CONVERSION = MethodHandles.identity(Object.class);
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class NotCacheableConverter extends RuntimeException {
|
||||
final MethodHandle converter;
|
||||
|
||||
|
@ -106,38 +106,49 @@ public class TypeUtilities {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two types represented by c1 and c2, returns a type that is their most specific common superclass or
|
||||
* superinterface.
|
||||
* Given two types represented by c1 and c2, returns a type that is their most specific common supertype for
|
||||
* purposes of lossless conversions.
|
||||
*
|
||||
* @param c1 one type
|
||||
* @param c2 another type
|
||||
* @return their most common superclass or superinterface. If they have several unrelated superinterfaces as their
|
||||
* most specific common type, or the types themselves are completely unrelated interfaces, {@link java.lang.Object}
|
||||
* is returned.
|
||||
* @return their most common superclass or superinterface for purposes of lossless conversions. If they have several
|
||||
* unrelated superinterfaces as their most specific common type, or the types themselves are completely
|
||||
* unrelated interfaces, {@link java.lang.Object} is returned.
|
||||
*/
|
||||
public static Class<?> getMostSpecificCommonType(Class<?> c1, Class<?> c2) {
|
||||
public static Class<?> getCommonLosslessConversionType(final Class<?> c1, final Class<?> c2) {
|
||||
if(c1 == c2) {
|
||||
return c1;
|
||||
} else if(isConvertibleWithoutLoss(c2, c1)) {
|
||||
return c1;
|
||||
} else if(isConvertibleWithoutLoss(c1, c2)) {
|
||||
return c2;
|
||||
}
|
||||
Class<?> c3 = c2;
|
||||
if(c3.isPrimitive()) {
|
||||
if(c3 == Byte.TYPE)
|
||||
c3 = Byte.class;
|
||||
else if(c3 == Short.TYPE)
|
||||
c3 = Short.class;
|
||||
else if(c3 == Character.TYPE)
|
||||
c3 = Character.class;
|
||||
else if(c3 == Integer.TYPE)
|
||||
c3 = Integer.class;
|
||||
else if(c3 == Float.TYPE)
|
||||
c3 = Float.class;
|
||||
else if(c3 == Long.TYPE)
|
||||
c3 = Long.class;
|
||||
else if(c3 == Double.TYPE)
|
||||
c3 = Double.class;
|
||||
if(c1 == void.class) {
|
||||
return c2;
|
||||
} else if(c2 == void.class) {
|
||||
return c1;
|
||||
}
|
||||
Set<Class<?>> a1 = getAssignables(c1, c3);
|
||||
Set<Class<?>> a2 = getAssignables(c3, c1);
|
||||
if(c1.isPrimitive() && c2.isPrimitive()) {
|
||||
if((c1 == byte.class && c2 == char.class) || (c1 == char.class && c2 == byte.class)) {
|
||||
// byte + char = int
|
||||
return int.class;
|
||||
} else if((c1 == short.class && c2 == char.class) || (c1 == char.class && c2 == short.class)) {
|
||||
// short + char = int
|
||||
return int.class;
|
||||
} else if((c1 == int.class && c2 == float.class) || (c1 == float.class && c2 == int.class)) {
|
||||
// int + float = double
|
||||
return double.class;
|
||||
}
|
||||
}
|
||||
// For all other cases. This will handle long + (float|double) = Number case as well as boolean + anything = Object case too.
|
||||
return getMostSpecificCommonTypeUnequalNonprimitives(c1, c2);
|
||||
}
|
||||
|
||||
private static Class<?> getMostSpecificCommonTypeUnequalNonprimitives(final Class<?> c1, final Class<?> c2) {
|
||||
final Class<?> npc1 = c1.isPrimitive() ? getWrapperType(c1) : c1;
|
||||
final Class<?> npc2 = c2.isPrimitive() ? getWrapperType(c2) : c2;
|
||||
final Set<Class<?>> a1 = getAssignables(npc1, npc2);
|
||||
final Set<Class<?>> a2 = getAssignables(npc2, npc1);
|
||||
a1.retainAll(a2);
|
||||
if(a1.isEmpty()) {
|
||||
// Can happen when at least one of the arguments is an interface,
|
||||
@ -148,10 +159,10 @@ public class TypeUtilities {
|
||||
// thank to interfaces. I.e., if you call this method for String.class
|
||||
// and Number.class, you'll have Comparable, Serializable, and Object
|
||||
// as maximal elements.
|
||||
List<Class<?>> max = new ArrayList<>();
|
||||
outer: for(Class<?> clazz: a1) {
|
||||
for(Iterator<Class<?>> maxiter = max.iterator(); maxiter.hasNext();) {
|
||||
Class<?> maxClazz = maxiter.next();
|
||||
final List<Class<?>> max = new ArrayList<>();
|
||||
outer: for(final Class<?> clazz: a1) {
|
||||
for(final Iterator<Class<?>> maxiter = max.iterator(); maxiter.hasNext();) {
|
||||
final Class<?> maxClazz = maxiter.next();
|
||||
if(isSubtype(maxClazz, clazz)) {
|
||||
// It can't be maximal, if there's already a more specific
|
||||
// maximal than it.
|
||||
@ -168,26 +179,26 @@ public class TypeUtilities {
|
||||
max.add(clazz);
|
||||
}
|
||||
if(max.size() > 1) {
|
||||
return OBJECT_CLASS;
|
||||
return Object.class;
|
||||
}
|
||||
return max.get(0);
|
||||
}
|
||||
|
||||
private static Set<Class<?>> getAssignables(Class<?> c1, Class<?> c2) {
|
||||
Set<Class<?>> s = new HashSet<>();
|
||||
private static Set<Class<?>> getAssignables(final Class<?> c1, final Class<?> c2) {
|
||||
final Set<Class<?>> s = new HashSet<>();
|
||||
collectAssignables(c1, c2, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
private static void collectAssignables(Class<?> c1, Class<?> c2, Set<Class<?>> s) {
|
||||
private static void collectAssignables(final Class<?> c1, final Class<?> c2, final Set<Class<?>> s) {
|
||||
if(c1.isAssignableFrom(c2)) {
|
||||
s.add(c1);
|
||||
}
|
||||
Class<?> sc = c1.getSuperclass();
|
||||
final Class<?> sc = c1.getSuperclass();
|
||||
if(sc != null) {
|
||||
collectAssignables(sc, c2, s);
|
||||
}
|
||||
Class<?>[] itf = c1.getInterfaces();
|
||||
final Class<?>[] itf = c1.getInterfaces();
|
||||
for(int i = 0; i < itf.length; ++i) {
|
||||
collectAssignables(itf[i], c2, s);
|
||||
}
|
||||
@ -210,17 +221,17 @@ public class TypeUtilities {
|
||||
return Collections.unmodifiableMap(wrapperTypes);
|
||||
}
|
||||
|
||||
private static Map<String, Class<?>> createClassNameMapping(Collection<Class<?>> classes) {
|
||||
private static Map<String, Class<?>> createClassNameMapping(final Collection<Class<?>> classes) {
|
||||
final Map<String, Class<?>> map = new HashMap<>();
|
||||
for(Class<?> clazz: classes) {
|
||||
for(final Class<?> clazz: classes) {
|
||||
map.put(clazz.getName(), clazz);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static <K, V> Map<V, K> invertMap(Map<K, V> map) {
|
||||
private static <K, V> Map<V, K> invertMap(final Map<K, V> map) {
|
||||
final Map<V, K> inverted = new IdentityHashMap<>(map.size());
|
||||
for(Map.Entry<K, V> entry: map.entrySet()) {
|
||||
for(final Map.Entry<K, V> entry: map.entrySet()) {
|
||||
inverted.put(entry.getValue(), entry.getKey());
|
||||
}
|
||||
return Collections.unmodifiableMap(inverted);
|
||||
@ -232,29 +243,58 @@ public class TypeUtilities {
|
||||
* {@link #isSubtype(Class, Class)}) as well as boxing conversion (JLS 5.1.7) optionally followed by widening
|
||||
* reference conversion and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion.
|
||||
*
|
||||
* @param callSiteType the parameter type at the call site
|
||||
* @param methodType the parameter type in the method declaration
|
||||
* @return true if callSiteType is method invocation convertible to the methodType.
|
||||
* @param sourceType the type being converted from (call site type for parameter types, method type for return types)
|
||||
* @param targetType the parameter type being converted to (method type for parameter types, call site type for return types)
|
||||
* @return true if source type is method invocation convertible to target type.
|
||||
*/
|
||||
public static boolean isMethodInvocationConvertible(Class<?> callSiteType, Class<?> methodType) {
|
||||
if(methodType.isAssignableFrom(callSiteType)) {
|
||||
public static boolean isMethodInvocationConvertible(final Class<?> sourceType, final Class<?> targetType) {
|
||||
if(targetType.isAssignableFrom(sourceType)) {
|
||||
return true;
|
||||
}
|
||||
if(callSiteType.isPrimitive()) {
|
||||
if(methodType.isPrimitive()) {
|
||||
return isProperPrimitiveSubtype(callSiteType, methodType);
|
||||
if(sourceType.isPrimitive()) {
|
||||
if(targetType.isPrimitive()) {
|
||||
return isProperPrimitiveSubtype(sourceType, targetType);
|
||||
}
|
||||
// Boxing + widening reference conversion
|
||||
return methodType.isAssignableFrom(WRAPPER_TYPES.get(callSiteType));
|
||||
assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
|
||||
return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
|
||||
}
|
||||
if(methodType.isPrimitive()) {
|
||||
final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(callSiteType);
|
||||
if(targetType.isPrimitive()) {
|
||||
final Class<?> unboxedCallSiteType = PRIMITIVE_TYPES.get(sourceType);
|
||||
return unboxedCallSiteType != null
|
||||
&& (unboxedCallSiteType == methodType || isProperPrimitiveSubtype(unboxedCallSiteType, methodType));
|
||||
&& (unboxedCallSiteType == targetType || isProperPrimitiveSubtype(unboxedCallSiteType, targetType));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a type can be converted to another without losing any
|
||||
* precision.
|
||||
*
|
||||
* @param sourceType the source type
|
||||
* @param targetType the target type
|
||||
* @return true if lossless conversion is possible
|
||||
*/
|
||||
public static boolean isConvertibleWithoutLoss(final Class<?> sourceType, final Class<?> targetType) {
|
||||
if(targetType.isAssignableFrom(sourceType)) {
|
||||
return true;
|
||||
}
|
||||
if(sourceType.isPrimitive()) {
|
||||
if(sourceType == void.class) {
|
||||
return false; // Void can't be losslessly represented by any type
|
||||
}
|
||||
if(targetType.isPrimitive()) {
|
||||
return isProperPrimitiveLosslessSubtype(sourceType, targetType);
|
||||
}
|
||||
// Boxing + widening reference conversion
|
||||
assert WRAPPER_TYPES.get(sourceType) != null : sourceType.getName();
|
||||
return targetType.isAssignableFrom(WRAPPER_TYPES.get(sourceType));
|
||||
}
|
||||
// Can't convert from any non-primitive type to any primitive type without data loss because of null.
|
||||
// Also, can't convert non-assignable reference types.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether one type can be potentially converted to another type at runtime. Allows a conversion between
|
||||
* any subtype and supertype in either direction, and also allows a conversion between any two primitive types, as
|
||||
@ -264,9 +304,9 @@ public class TypeUtilities {
|
||||
* @param methodType the parameter type in the method declaration
|
||||
* @return true if callSiteType is potentially convertible to the methodType.
|
||||
*/
|
||||
public static boolean isPotentiallyConvertible(Class<?> callSiteType, Class<?> methodType) {
|
||||
public static boolean isPotentiallyConvertible(final Class<?> callSiteType, final Class<?> methodType) {
|
||||
// Widening or narrowing reference conversion
|
||||
if(methodType.isAssignableFrom(callSiteType) || callSiteType.isAssignableFrom(methodType)) {
|
||||
if(areAssignable(callSiteType, methodType)) {
|
||||
return true;
|
||||
}
|
||||
if(callSiteType.isPrimitive()) {
|
||||
@ -286,6 +326,16 @@ public class TypeUtilities {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if either of the types is assignable from the other.
|
||||
* @param c1 one of the types
|
||||
* @param c2 another one of the types
|
||||
* @return true if either c1 is assignable from c2 or c2 is assignable from c1.
|
||||
*/
|
||||
public static boolean areAssignable(final Class<?> c1, final Class<?> c2) {
|
||||
return c1.isAssignableFrom(c2) || c2.isAssignableFrom(c1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether one type is a subtype of another type, as per JLS 4.10 "Subtyping". Note: this is not strict
|
||||
* or proper subtype, therefore true is also returned for identical types; to be completely precise, it allows
|
||||
@ -297,7 +347,7 @@ public class TypeUtilities {
|
||||
* @return true if subType can be converted by identity conversion, widening primitive conversion, or widening
|
||||
* reference conversion to superType.
|
||||
*/
|
||||
public static boolean isSubtype(Class<?> subType, Class<?> superType) {
|
||||
public static boolean isSubtype(final Class<?> subType, final Class<?> superType) {
|
||||
// Covers both JLS 4.10.2 "Subtyping among Class and Interface Types"
|
||||
// and JLS 4.10.3 "Subtyping among Array Types", as well as primitive
|
||||
// type identity.
|
||||
@ -328,7 +378,7 @@ public class TypeUtilities {
|
||||
* @param superType the supposed supertype
|
||||
* @return true if subType is a proper (not identical to) primitive subtype of the superType
|
||||
*/
|
||||
private static boolean isProperPrimitiveSubtype(Class<?> subType, Class<?> superType) {
|
||||
private static boolean isProperPrimitiveSubtype(final Class<?> subType, final Class<?> superType) {
|
||||
if(superType == boolean.class || subType == boolean.class) {
|
||||
return false;
|
||||
}
|
||||
@ -353,6 +403,37 @@ public class TypeUtilities {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #isProperPrimitiveSubtype(Class, Class)}, except it disallows conversions from int and long to
|
||||
* float, and from long to double, as those can lose precision. It also disallows conversion from and to char and
|
||||
* anything else (similar to boolean) as char is not meant to be an arithmetic type.
|
||||
* @param subType the supposed subtype
|
||||
* @param superType the supposed supertype
|
||||
* @return true if subType is a proper (not identical to) primitive subtype of the superType that can be represented
|
||||
* by the supertype without no precision loss.
|
||||
*/
|
||||
private static boolean isProperPrimitiveLosslessSubtype(final Class<?> subType, final Class<?> superType) {
|
||||
if(superType == boolean.class || subType == boolean.class) {
|
||||
return false;
|
||||
}
|
||||
if(superType == char.class || subType == char.class) {
|
||||
return false;
|
||||
}
|
||||
if(subType == byte.class) {
|
||||
return true;
|
||||
}
|
||||
if(subType == short.class) {
|
||||
return superType != byte.class;
|
||||
}
|
||||
if(subType == int.class) {
|
||||
return superType == long.class || superType == double.class;
|
||||
}
|
||||
if(subType == float.class) {
|
||||
return superType == double.class;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVE_TYPES = createWrapperToPrimitiveTypes();
|
||||
|
||||
private static Map<Class<?>, Class<?>> createWrapperToPrimitiveTypes() {
|
||||
@ -384,13 +465,13 @@ public class TypeUtilities {
|
||||
return classes.keySet();
|
||||
}
|
||||
|
||||
private static void addClassHierarchy(Map<Class<?>, Class<?>> map, Class<?> clazz) {
|
||||
private static void addClassHierarchy(final Map<Class<?>, Class<?>> map, final Class<?> clazz) {
|
||||
if(clazz == null) {
|
||||
return;
|
||||
}
|
||||
map.put(clazz, clazz);
|
||||
addClassHierarchy(map, clazz.getSuperclass());
|
||||
for(Class<?> itf: clazz.getInterfaces()) {
|
||||
for(final Class<?> itf: clazz.getInterfaces()) {
|
||||
addClassHierarchy(map, itf);
|
||||
}
|
||||
}
|
||||
@ -402,7 +483,7 @@ public class TypeUtilities {
|
||||
* @return true if the class can be assigned from any boxed primitive. Basically, it is true if the class is any
|
||||
* primitive wrapper class, or a superclass or superinterface of any primitive wrapper class.
|
||||
*/
|
||||
private static boolean isAssignableFromBoxedPrimitive(Class<?> clazz) {
|
||||
private static boolean isAssignableFromBoxedPrimitive(final Class<?> clazz) {
|
||||
return PRIMITIVE_WRAPPER_TYPES.contains(clazz);
|
||||
}
|
||||
|
||||
@ -413,7 +494,7 @@ public class TypeUtilities {
|
||||
* @return the class representing the primitive type, or null if the name does not correspond to a primitive type
|
||||
* or is "void".
|
||||
*/
|
||||
public static Class<?> getPrimitiveTypeByName(String name) {
|
||||
public static Class<?> getPrimitiveTypeByName(final String name) {
|
||||
return PRIMITIVE_TYPES_BY_NAME.get(name);
|
||||
}
|
||||
|
||||
@ -424,7 +505,7 @@ public class TypeUtilities {
|
||||
* @param wrapperType the class object representing a wrapper for a primitive type
|
||||
* @return the class object representing the primitive type, or null if the passed class is not a primitive wrapper.
|
||||
*/
|
||||
public static Class<?> getPrimitiveType(Class<?> wrapperType) {
|
||||
public static Class<?> getPrimitiveType(final Class<?> wrapperType) {
|
||||
return WRAPPER_TO_PRIMITIVE_TYPES.get(wrapperType);
|
||||
}
|
||||
|
||||
@ -436,7 +517,7 @@ public class TypeUtilities {
|
||||
* @param primitiveType the class object representing a primitive type
|
||||
* @return the class object representing the wrapper type, or null if the passed class is not a primitive.
|
||||
*/
|
||||
public static Class<?> getWrapperType(Class<?> primitiveType) {
|
||||
public static Class<?> getWrapperType(final Class<?> primitiveType) {
|
||||
return WRAPPER_TYPES.get(primitiveType);
|
||||
}
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ class UnnamedDynCallSiteDescriptor extends AbstractCallSiteDescriptor {
|
||||
private final MethodType methodType;
|
||||
private final String op;
|
||||
|
||||
UnnamedDynCallSiteDescriptor(String op, MethodType methodType) {
|
||||
UnnamedDynCallSiteDescriptor(final String op, final MethodType methodType) {
|
||||
this.op = op;
|
||||
this.methodType = methodType;
|
||||
}
|
||||
@ -105,7 +105,7 @@ class UnnamedDynCallSiteDescriptor extends AbstractCallSiteDescriptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameToken(int i) {
|
||||
public String getNameToken(final int i) {
|
||||
switch(i) {
|
||||
case 0: return "dyn";
|
||||
case 1: return op;
|
||||
@ -119,7 +119,7 @@ class UnnamedDynCallSiteDescriptor extends AbstractCallSiteDescriptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallSiteDescriptor changeMethodType(MethodType newMethodType) {
|
||||
public CallSiteDescriptor changeMethodType(final MethodType newMethodType) {
|
||||
return CallSiteDescriptorFactory.getCanonicalPublicDescriptor(new UnnamedDynCallSiteDescriptor(op,
|
||||
newMethodType));
|
||||
}
|
||||
|
@ -83,4 +83,4 @@ isOfClassGuardAlwaysTrue=isOfClass guard for {0} in position {1} in method type
|
||||
isOfClassGuardAlwaysFalse=isOfClass guard for {0} in position {1} in method type {2} at {3} will always return false
|
||||
|
||||
isArrayGuardAlwaysTrue=isArray guard in position {0} in method type {1} at {2} will always return true
|
||||
isArrayGuardAlwaysFalse=isArray guard in position {0} in method type {1} at {2} will always return false
|
||||
isArrayGuardAlwaysFalse=isArray guard in position {0} in method type {1} at {2} will always return false
|
||||
|
@ -65,8 +65,8 @@ final class Formatter {
|
||||
|
||||
while (m.find()) {
|
||||
int index = index(m.group(1));
|
||||
boolean previous = isPreviousArgument(m.group(2));
|
||||
char conversion = m.group(6).charAt(0);
|
||||
final boolean previous = isPreviousArgument(m.group(2));
|
||||
final char conversion = m.group(6).charAt(0);
|
||||
|
||||
// skip over some formats
|
||||
if (index < 0 || previous
|
||||
@ -85,7 +85,7 @@ final class Formatter {
|
||||
}
|
||||
|
||||
// current argument
|
||||
Object arg = args[index - 1];
|
||||
final Object arg = args[index - 1];
|
||||
|
||||
// for date we convert double to long
|
||||
if (m.group(5) != null) {
|
||||
|
@ -26,7 +26,6 @@
|
||||
package jdk.nashorn.api.scripting;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -182,7 +182,7 @@ public abstract class NashornException extends RuntimeException {
|
||||
if (ECMAErrors.isScriptFrame(st)) {
|
||||
final String className = "<" + st.getFileName() + ">";
|
||||
String methodName = st.getMethodName();
|
||||
if (methodName.equals(CompilerConstants.RUN_SCRIPT.symbolName())) {
|
||||
if (methodName.equals(CompilerConstants.PROGRAM.symbolName())) {
|
||||
methodName = "<program>";
|
||||
}
|
||||
|
||||
@ -224,10 +224,22 @@ public abstract class NashornException extends RuntimeException {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thrown object. Subclass responsibility
|
||||
* @return thrown object
|
||||
*/
|
||||
protected Object getThrown() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization function for ECMA errors. Stores the error
|
||||
* in the ecmaError field of this class. It is only initialized
|
||||
* once, and then reused
|
||||
*
|
||||
* @param global the global
|
||||
* @return initialized exception
|
||||
*/
|
||||
protected NashornException initEcmaError(final ScriptObject global) {
|
||||
if (ecmaError != null) {
|
||||
return this; // initialized already!
|
||||
|
@ -123,7 +123,6 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
}
|
||||
|
||||
// load engine.js
|
||||
@SuppressWarnings("resource")
|
||||
private static Source loadEngineJSSource() {
|
||||
final String script = "resources/engine.js";
|
||||
try {
|
||||
@ -281,7 +280,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
private static Source makeSource(final Reader reader, final ScriptContext ctxt) throws ScriptException {
|
||||
try {
|
||||
return sourceFor(getScriptName(ctxt), reader);
|
||||
} catch (IOException e) {
|
||||
} catch (final IOException e) {
|
||||
throw new ScriptException(e);
|
||||
}
|
||||
}
|
||||
@ -380,7 +379,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
}
|
||||
|
||||
// Arbitrary user Bindings implementation. Look for NASHORN_GLOBAL in it!
|
||||
Object scope = bindings.get(NASHORN_GLOBAL);
|
||||
final Object scope = bindings.get(NASHORN_GLOBAL);
|
||||
if (scope instanceof ScriptObjectMirror) {
|
||||
final Global glob = globalFromMirror((ScriptObjectMirror)scope);
|
||||
if (glob != null) {
|
||||
@ -397,7 +396,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
|
||||
// Retrieve nashorn Global object from a given ScriptObjectMirror
|
||||
private Global globalFromMirror(final ScriptObjectMirror mirror) {
|
||||
ScriptObject sobj = mirror.getScriptObject();
|
||||
final ScriptObject sobj = mirror.getScriptObject();
|
||||
if (sobj instanceof Global && isOfContext((Global)sobj, nashornContext)) {
|
||||
return (Global)sobj;
|
||||
}
|
||||
@ -456,7 +455,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
private void setContextVariables(final Global ctxtGlobal, final ScriptContext ctxt) {
|
||||
// set "context" global variable via contextProperty - because this
|
||||
// property is non-writable
|
||||
contextProperty.setObjectValue(ctxtGlobal, ctxtGlobal, ctxt, false);
|
||||
contextProperty.setValue(ctxtGlobal, ctxtGlobal, ctxt, false);
|
||||
Object args = ScriptObjectMirror.unwrap(ctxt.getAttribute("arguments"), ctxtGlobal);
|
||||
if (args == null || args == UNDEFINED) {
|
||||
args = ScriptRuntime.EMPTY_ARRAY;
|
||||
@ -671,7 +670,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
|
||||
continue;
|
||||
}
|
||||
|
||||
Object obj = sobj.get(method.getName());
|
||||
final Object obj = sobj.get(method.getName());
|
||||
if (! (obj instanceof ScriptFunction)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ public final class NashornScriptEngineFactory implements ScriptEngineFactory {
|
||||
* @param args arguments array passed to script engine.
|
||||
* @return newly created script engine.
|
||||
*/
|
||||
public ScriptEngine getScriptEngine(final String[] args) {
|
||||
public ScriptEngine getScriptEngine(final String... args) {
|
||||
checkConfigPermission();
|
||||
return new NashornScriptEngine(this, args, getAppClassLoader());
|
||||
}
|
||||
@ -220,7 +220,7 @@ public final class NashornScriptEngineFactory implements ScriptEngineFactory {
|
||||
// Revisit: script engine implementation needs the capability to
|
||||
// find the class loader of the context in which the script engine
|
||||
// is running so that classes will be found and loaded properly
|
||||
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
|
||||
final ClassLoader ccl = Thread.currentThread().getContextClassLoader();
|
||||
return (ccl == null)? NashornScriptEngineFactory.class.getClassLoader() : ccl;
|
||||
}
|
||||
}
|
||||
|
@ -43,13 +43,13 @@ import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.script.Bindings;
|
||||
import jdk.nashorn.internal.objects.Global;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.ConsString;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
|
||||
/**
|
||||
* Mirror object that wraps a given Nashorn Script object.
|
||||
@ -169,6 +169,12 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Call member function
|
||||
* @param functionName function name
|
||||
* @param args arguments
|
||||
* @return return value of function
|
||||
*/
|
||||
public Object callMember(final String functionName, final Object... args) {
|
||||
functionName.getClass(); // null check
|
||||
final Global oldGlobal = Context.getGlobal();
|
||||
@ -496,7 +502,7 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin
|
||||
public void setProto(final Object proto) {
|
||||
inGlobal(new Callable<Void>() {
|
||||
@Override public Void call() {
|
||||
sobj.setProtoCheck(unwrap(proto, global));
|
||||
sobj.setPrototypeOf(unwrap(proto, global));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@ -729,7 +735,7 @@ public final class ScriptObjectMirror extends AbstractJSObject implements Bindin
|
||||
return global;
|
||||
}
|
||||
|
||||
static Object translateUndefined(Object obj) {
|
||||
static Object translateUndefined(final Object obj) {
|
||||
return (obj == ScriptRuntime.UNDEFINED)? null : obj;
|
||||
}
|
||||
|
||||
|
@ -28,11 +28,11 @@ package jdk.nashorn.api.scripting;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import jdk.internal.dynalink.beans.StaticClass;
|
||||
import jdk.internal.dynalink.linker.LinkerServices;
|
||||
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||
|
||||
/**
|
||||
* Utilities that are to be called from script code.
|
||||
|
@ -81,7 +81,7 @@ public final class URLReader extends Reader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(char cbuf[], int off, int len) throws IOException {
|
||||
public int read(final char cbuf[], final int off, final int len) throws IOException {
|
||||
return getReader().read(cbuf, off, len);
|
||||
}
|
||||
|
||||
|
87
nashorn/src/jdk/nashorn/internal/IntDeque.java
Normal file
87
nashorn/src/jdk/nashorn/internal/IntDeque.java
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, 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;
|
||||
|
||||
/**
|
||||
* Small helper class for fast int deques
|
||||
*/
|
||||
public class IntDeque {
|
||||
private int[] deque = new int[16];
|
||||
private int nextFree = 0;
|
||||
|
||||
/**
|
||||
* Push an int value
|
||||
* @param value value
|
||||
*/
|
||||
public void push(final int value) {
|
||||
if (nextFree == deque.length) {
|
||||
final int[] newDeque = new int[nextFree * 2];
|
||||
System.arraycopy(deque, 0, newDeque, 0, nextFree);
|
||||
deque = newDeque;
|
||||
}
|
||||
deque[nextFree++] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop an int value
|
||||
* @return value
|
||||
*/
|
||||
public int pop() {
|
||||
return deque[--nextFree];
|
||||
}
|
||||
|
||||
/**
|
||||
* Peek
|
||||
* @return top value
|
||||
*/
|
||||
public int peek() {
|
||||
return deque[nextFree - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of the top element and increment it.
|
||||
* @return top value
|
||||
*/
|
||||
public int getAndIncrement() {
|
||||
return deque[nextFree - 1]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement the value of the top element and return it.
|
||||
* @return decremented top value
|
||||
*/
|
||||
public int decrementAndGet() {
|
||||
return --deque[nextFree - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if deque is empty
|
||||
* @return true if empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return nextFree == 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. 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.ARGUMENTS_VAR;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import jdk.nashorn.internal.ir.AccessNode;
|
||||
import jdk.nashorn.internal.ir.CallNode;
|
||||
import jdk.nashorn.internal.ir.Expression;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.IdentNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.Node;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.objects.Global;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.logging.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.logging.Loggable;
|
||||
import jdk.nashorn.internal.runtime.logging.Logger;
|
||||
import jdk.nashorn.internal.runtime.options.Options;
|
||||
|
||||
/**
|
||||
* An optimization that attempts to turn applies into calls. This pattern
|
||||
* is very common for fake class instance creation, and apply
|
||||
* introduces expensive args collection and boxing
|
||||
*
|
||||
* <pre>
|
||||
* var Class = {
|
||||
* create: function() {
|
||||
* return function() { //vararg
|
||||
* this.initialize.apply(this, arguments);
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* Color = Class.create();
|
||||
*
|
||||
* Color.prototype = {
|
||||
* red: 0, green: 0, blue: 0,
|
||||
* initialize: function(r,g,b) {
|
||||
* this.red = r;
|
||||
* this.green = g;
|
||||
* this.blue = b;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* new Color(17, 47, 11);
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
@Logger(name="apply2call")
|
||||
public final class ApplySpecialization extends NodeVisitor<LexicalContext> implements Loggable {
|
||||
|
||||
private static final boolean USE_APPLY2CALL = Options.getBooleanProperty("nashorn.apply2call", true);
|
||||
|
||||
private final DebugLogger log;
|
||||
|
||||
private final Compiler compiler;
|
||||
|
||||
private final Set<Integer> changed = new HashSet<>();
|
||||
|
||||
private final Deque<List<IdentNode>> explodedArguments = new ArrayDeque<>();
|
||||
|
||||
private static final String ARGUMENTS = ARGUMENTS_VAR.symbolName();
|
||||
|
||||
/**
|
||||
* Apply specialization optimization. Try to explode arguments and call
|
||||
* applies as calls if they just pass on the "arguments" array and
|
||||
* "arguments" doesn't escape.
|
||||
*
|
||||
* @param compiler compiler
|
||||
*/
|
||||
public ApplySpecialization(final Compiler compiler) {
|
||||
super(new LexicalContext());
|
||||
this.compiler = compiler;
|
||||
this.log = initLogger(compiler.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugLogger getLogger() {
|
||||
return log;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugLogger initLogger(final Context context) {
|
||||
return context.getLogger(this.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Arguments may only be used as args to the apply. Everything else is disqualified
|
||||
* We cannot control arguments if they escape from the method and go into an unknown
|
||||
* scope, thus we are conservative and treat any access to arguments outside the
|
||||
* apply call as a case of "we cannot apply the optimization".
|
||||
*
|
||||
* @return true if arguments escape
|
||||
*/
|
||||
private boolean argumentsEscape(final FunctionNode functionNode) {
|
||||
|
||||
final Deque<Set<Expression>> stack = new ArrayDeque<>();
|
||||
//ensure that arguments is only passed as arg to apply
|
||||
try {
|
||||
functionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
private boolean isCurrentArg(final Expression expr) {
|
||||
return !stack.isEmpty() && stack.peek().contains(expr); //args to current apply call
|
||||
}
|
||||
|
||||
private boolean isArguments(final Expression expr) {
|
||||
return expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName());
|
||||
}
|
||||
|
||||
private boolean isParam(final String name) {
|
||||
for (final IdentNode param : functionNode.getParameters()) {
|
||||
if (param.getName().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveIdentNode(final IdentNode identNode) {
|
||||
if (isParam(identNode.getName()) || ARGUMENTS.equals(identNode.getName()) && !isCurrentArg(identNode)) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
return identNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterCallNode(final CallNode callNode) {
|
||||
final Set<Expression> callArgs = new HashSet<>();
|
||||
if (isApply(callNode)) {
|
||||
final List<Expression> argList = callNode.getArgs();
|
||||
if (argList.size() != 2 || !isArguments(argList.get(argList.size() - 1))) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
callArgs.addAll(callNode.getArgs());
|
||||
}
|
||||
stack.push(callArgs);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveCallNode(final CallNode callNode) {
|
||||
stack.pop();
|
||||
return callNode;
|
||||
}
|
||||
});
|
||||
} catch (final UnsupportedOperationException e) {
|
||||
log.fine("'arguments' escapes, is not used in standard call dispatch, or is reassigned in '" + functionNode.getName() + "'. Aborting");
|
||||
return true; //bad
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterCallNode(final CallNode callNode) {
|
||||
return !explodedArguments.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveCallNode(final CallNode callNode) {
|
||||
//apply needs to be a global symbol or we don't allow it
|
||||
|
||||
final List<IdentNode> newParams = explodedArguments.peek();
|
||||
if (isApply(callNode)) {
|
||||
final List<Expression> newArgs = new ArrayList<>();
|
||||
for (final Expression arg : callNode.getArgs()) {
|
||||
if (arg instanceof IdentNode && ARGUMENTS.equals(((IdentNode)arg).getName())) {
|
||||
newArgs.addAll(newParams);
|
||||
} else {
|
||||
newArgs.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
changed.add(lc.getCurrentFunction().getId());
|
||||
|
||||
final CallNode newCallNode = callNode.setArgs(newArgs).setIsApplyToCall();
|
||||
|
||||
log.fine("Transformed ",
|
||||
callNode,
|
||||
" from apply to call => ",
|
||||
newCallNode,
|
||||
" in ",
|
||||
DebugLogger.quote(lc.getCurrentFunction().getName()));
|
||||
|
||||
return newCallNode;
|
||||
}
|
||||
|
||||
return callNode;
|
||||
}
|
||||
|
||||
private boolean pushExplodedArgs(final FunctionNode functionNode) {
|
||||
int start = 0;
|
||||
|
||||
final MethodType actualCallSiteType = compiler.getCallSiteType(functionNode);
|
||||
if (actualCallSiteType == null) {
|
||||
return false;
|
||||
}
|
||||
assert actualCallSiteType.parameterType(actualCallSiteType.parameterCount() - 1) != Object[].class : "error vararg callsite passed to apply2call " + functionNode.getName() + " " + actualCallSiteType;
|
||||
|
||||
final TypeMap ptm = compiler.getTypeMap();
|
||||
if (ptm.needsCallee()) {
|
||||
start++;
|
||||
}
|
||||
|
||||
start++; //we always uses this
|
||||
|
||||
final List<IdentNode> params = functionNode.getParameters();
|
||||
final List<IdentNode> newParams = new ArrayList<>();
|
||||
final long to = Math.max(params.size(), actualCallSiteType.parameterCount() - start);
|
||||
for (int i = 0; i < to; i++) {
|
||||
if (i >= params.size()) {
|
||||
newParams.add(new IdentNode(functionNode.getToken(), functionNode.getFinish(), EXPLODED_ARGUMENT_PREFIX.symbolName() + (i)));
|
||||
} else {
|
||||
newParams.add(params.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
explodedArguments.push(newParams);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
if (!USE_APPLY2CALL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Global.instance().isSpecialNameValid("apply")) {
|
||||
log.fine("Apply transform disabled: apply/call overridden");
|
||||
assert !Global.instance().isSpecialNameValid("call") : "call and apply should have the same SwitchPoint";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!compiler.isOnDemandCompilation()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (functionNode.hasEval()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (argumentsEscape(functionNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pushExplodedArgs(functionNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to do the apply to call transformation
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode0) {
|
||||
FunctionNode newFunctionNode = functionNode0;
|
||||
final String functionName = newFunctionNode.getName();
|
||||
|
||||
if (changed.contains(newFunctionNode.getId())) {
|
||||
newFunctionNode = newFunctionNode.clearFlag(lc, FunctionNode.USES_ARGUMENTS).
|
||||
setFlag(lc, FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION).
|
||||
setParameters(lc, explodedArguments.peek());
|
||||
|
||||
if (log.isEnabled()) {
|
||||
log.info("Successfully specialized apply to call in '",
|
||||
functionName,
|
||||
" params=",
|
||||
explodedArguments.peek(),
|
||||
"' id=",
|
||||
newFunctionNode.getId(),
|
||||
" source=",
|
||||
newFunctionNode.getSource().getURL());
|
||||
}
|
||||
}
|
||||
|
||||
explodedArguments.pop();
|
||||
|
||||
return newFunctionNode;
|
||||
}
|
||||
|
||||
private static boolean isApply(final CallNode callNode) {
|
||||
final Expression f = callNode.getFunction();
|
||||
return f instanceof AccessNode && "apply".equals(((AccessNode)f).getProperty());
|
||||
}
|
||||
|
||||
}
|
946
nashorn/src/jdk/nashorn/internal/codegen/AssignSymbols.java
Normal file
946
nashorn/src/jdk/nashorn/internal/codegen/AssignSymbols.java
Normal file
@ -0,0 +1,946 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, 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.ARGUMENTS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.CALLEE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.EXCEPTION_PREFIX;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.ITERATOR_PREFIX;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.RETURN;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.SWITCH_TAG_PREFIX;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
|
||||
import static jdk.nashorn.internal.ir.Symbol.HAS_OBJECT_VALUE;
|
||||
import static jdk.nashorn.internal.ir.Symbol.IS_FUNCTION_SELF;
|
||||
import static jdk.nashorn.internal.ir.Symbol.IS_GLOBAL;
|
||||
import static jdk.nashorn.internal.ir.Symbol.IS_INTERNAL;
|
||||
import static jdk.nashorn.internal.ir.Symbol.IS_LET;
|
||||
import static jdk.nashorn.internal.ir.Symbol.IS_PARAM;
|
||||
import static jdk.nashorn.internal.ir.Symbol.IS_PROGRAM_LEVEL;
|
||||
import static jdk.nashorn.internal.ir.Symbol.IS_SCOPE;
|
||||
import static jdk.nashorn.internal.ir.Symbol.IS_THIS;
|
||||
import static jdk.nashorn.internal.ir.Symbol.IS_VAR;
|
||||
import static jdk.nashorn.internal.ir.Symbol.KINDMASK;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import jdk.nashorn.internal.ir.AccessNode;
|
||||
import jdk.nashorn.internal.ir.BinaryNode;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.CatchNode;
|
||||
import jdk.nashorn.internal.ir.Expression;
|
||||
import jdk.nashorn.internal.ir.ForNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
|
||||
import jdk.nashorn.internal.ir.IdentNode;
|
||||
import jdk.nashorn.internal.ir.IndexNode;
|
||||
import jdk.nashorn.internal.ir.LexicalContext;
|
||||
import jdk.nashorn.internal.ir.LexicalContextNode;
|
||||
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.RuntimeNode;
|
||||
import jdk.nashorn.internal.ir.RuntimeNode.Request;
|
||||
import jdk.nashorn.internal.ir.SplitNode;
|
||||
import jdk.nashorn.internal.ir.Statement;
|
||||
import jdk.nashorn.internal.ir.SwitchNode;
|
||||
import jdk.nashorn.internal.ir.Symbol;
|
||||
import jdk.nashorn.internal.ir.TryNode;
|
||||
import jdk.nashorn.internal.ir.UnaryNode;
|
||||
import jdk.nashorn.internal.ir.VarNode;
|
||||
import jdk.nashorn.internal.ir.WithNode;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeOperatorVisitor;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.Property;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.logging.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.logging.Loggable;
|
||||
import jdk.nashorn.internal.runtime.logging.Logger;
|
||||
|
||||
/**
|
||||
* This visitor assigns symbols to identifiers denoting variables. It does few more minor calculations that are only
|
||||
* possible after symbols have been assigned; such is the transformation of "delete" and "typeof" operators into runtime
|
||||
* nodes and counting of number of properties assigned to "this" in constructor functions. This visitor is also notable
|
||||
* for what it doesn't do, most significantly it does no type calculations as in JavaScript variables can change types
|
||||
* during runtime and as such symbols don't have types. Calculation of expression types is performed by a separate
|
||||
* visitor.
|
||||
*/
|
||||
@Logger(name="symbols")
|
||||
final class AssignSymbols extends NodeOperatorVisitor<LexicalContext> implements Loggable {
|
||||
private final DebugLogger log;
|
||||
private final boolean debug;
|
||||
|
||||
private static boolean isParamOrVar(final IdentNode identNode) {
|
||||
final Symbol symbol = identNode.getSymbol();
|
||||
return symbol.isParam() || symbol.isVar();
|
||||
}
|
||||
|
||||
private static String name(final Node node) {
|
||||
final String cn = node.getClass().getName();
|
||||
final int lastDot = cn.lastIndexOf('.');
|
||||
if (lastDot == -1) {
|
||||
return cn;
|
||||
}
|
||||
return cn.substring(lastDot + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if various symbols that were provisionally marked as needing a slot ended up unused, and marks them as not
|
||||
* needing a slot after all.
|
||||
* @param functionNode the function node
|
||||
* @return the passed in node, for easy chaining
|
||||
*/
|
||||
private static FunctionNode removeUnusedSlots(final FunctionNode functionNode) {
|
||||
if (!functionNode.needsCallee()) {
|
||||
functionNode.compilerConstant(CALLEE).setNeedsSlot(false);
|
||||
}
|
||||
if (!(functionNode.hasScopeBlock() || functionNode.needsParentScope())) {
|
||||
functionNode.compilerConstant(SCOPE).setNeedsSlot(false);
|
||||
}
|
||||
if (!functionNode.usesReturnSymbol()) {
|
||||
functionNode.compilerConstant(RETURN).setNeedsSlot(false);
|
||||
}
|
||||
// Named function expressions that end up not referencing themselves won't need a local slot for the self symbol.
|
||||
if(!functionNode.isDeclared() && !functionNode.usesSelfSymbol() && !functionNode.isAnonymous()) {
|
||||
final Symbol selfSymbol = functionNode.getBody().getExistingSymbol(functionNode.getIdent().getName());
|
||||
if(selfSymbol != null) {
|
||||
if(selfSymbol.isFunctionSelf()) {
|
||||
selfSymbol.setNeedsSlot(false);
|
||||
selfSymbol.clearFlag(Symbol.IS_VAR);
|
||||
}
|
||||
} else {
|
||||
assert functionNode.isProgram();
|
||||
}
|
||||
}
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
private final Deque<Set<String>> thisProperties = new ArrayDeque<>();
|
||||
private final Map<String, Symbol> globalSymbols = new HashMap<>(); //reuse the same global symbol
|
||||
private final Compiler compiler;
|
||||
|
||||
public AssignSymbols(final Compiler compiler) {
|
||||
super(new LexicalContext());
|
||||
this.compiler = compiler;
|
||||
this.log = initLogger(compiler.getContext());
|
||||
this.debug = log.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugLogger getLogger() {
|
||||
return log;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugLogger initLogger(final Context context) {
|
||||
return context.getLogger(this.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Define symbols for all variable declarations at the top of the function scope. This way we can get around
|
||||
* problems like
|
||||
*
|
||||
* while (true) {
|
||||
* break;
|
||||
* if (true) {
|
||||
* var s;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* to an arbitrary nesting depth.
|
||||
*
|
||||
* see NASHORN-73
|
||||
*
|
||||
* @param functionNode the FunctionNode we are entering
|
||||
* @param body the body of the FunctionNode we are entering
|
||||
*/
|
||||
private void acceptDeclarations(final FunctionNode functionNode, final Block body) {
|
||||
// This visitor will assign symbol to all declared variables, except function declarations (which are taken care
|
||||
// in a separate step above) and "var" declarations in for loop initializers.
|
||||
//
|
||||
body.accept(new NodeVisitor<LexicalContext>(new LexicalContext()) {
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode nestedFn) {
|
||||
// Don't descend into nested functions
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveVarNode(final VarNode varNode) {
|
||||
if (varNode.isStatement()) {
|
||||
final IdentNode ident = varNode.getName();
|
||||
final Symbol symbol = defineSymbol(body, ident.getName(), IS_VAR);
|
||||
functionNode.addDeclaredSymbol(symbol);
|
||||
if (varNode.isFunctionDeclaration()) {
|
||||
symbol.setIsFunctionDeclaration();
|
||||
}
|
||||
return varNode.setName((IdentNode)ident.setSymbol(symbol));
|
||||
}
|
||||
return varNode;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private IdentNode compilerConstantIdentifier(final CompilerConstants cc) {
|
||||
return (IdentNode)createImplicitIdentifier(cc.symbolName()).setSymbol(lc.getCurrentFunction().compilerConstant(cc));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ident node for an implicit identifier within the function (one not declared in the script source
|
||||
* code). These identifiers are defined with function's token and finish.
|
||||
* @param name the name of the identifier
|
||||
* @return an ident node representing the implicit identifier.
|
||||
*/
|
||||
private IdentNode createImplicitIdentifier(final String name) {
|
||||
final FunctionNode fn = lc.getCurrentFunction();
|
||||
return new IdentNode(fn.getToken(), fn.getFinish(), name);
|
||||
}
|
||||
|
||||
private Symbol createSymbol(final String name, final int flags) {
|
||||
if ((flags & Symbol.KINDMASK) == IS_GLOBAL) {
|
||||
//reuse global symbols so they can be hashed
|
||||
Symbol global = globalSymbols.get(name);
|
||||
if (global == null) {
|
||||
global = new Symbol(name, flags);
|
||||
globalSymbols.put(name, global);
|
||||
}
|
||||
return global;
|
||||
}
|
||||
return new Symbol(name, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a synthetic initializer for a variable (a var statement that doesn't occur in the source code). Typically
|
||||
* used to create assignmnent of {@code :callee} to the function name symbol in self-referential function
|
||||
* expressions as well as for assignment of {@code :arguments} to {@code arguments}.
|
||||
*
|
||||
* @param name the ident node identifying the variable to initialize
|
||||
* @param initConstant the compiler constant it is initialized to
|
||||
* @param fn the function node the assignment is for
|
||||
* @return a var node with the appropriate assignment
|
||||
*/
|
||||
private VarNode createSyntheticInitializer(final IdentNode name, final CompilerConstants initConstant, final FunctionNode fn) {
|
||||
final IdentNode init = compilerConstantIdentifier(initConstant);
|
||||
assert init.getSymbol() != null && init.getSymbol().isBytecodeLocal();
|
||||
|
||||
final VarNode synthVar = new VarNode(fn.getLineNumber(), fn.getToken(), fn.getFinish(), name, init);
|
||||
|
||||
final Symbol nameSymbol = fn.getBody().getExistingSymbol(name.getName());
|
||||
assert nameSymbol != null;
|
||||
|
||||
return (VarNode)synthVar.setName((IdentNode)name.setSymbol(nameSymbol)).accept(this);
|
||||
}
|
||||
|
||||
private FunctionNode createSyntheticInitializers(final FunctionNode functionNode) {
|
||||
final List<VarNode> syntheticInitializers = new ArrayList<>(2);
|
||||
|
||||
// Must visit the new var nodes in the context of the body. We could also just set the new statements into the
|
||||
// block and then revisit the entire block, but that seems to be too much double work.
|
||||
final Block body = functionNode.getBody();
|
||||
lc.push(body);
|
||||
try {
|
||||
if (functionNode.usesSelfSymbol()) {
|
||||
// "var fn = :callee"
|
||||
syntheticInitializers.add(createSyntheticInitializer(functionNode.getIdent(), CALLEE, functionNode));
|
||||
}
|
||||
|
||||
if (functionNode.needsArguments()) {
|
||||
// "var arguments = :arguments"
|
||||
syntheticInitializers.add(createSyntheticInitializer(createImplicitIdentifier(ARGUMENTS_VAR.symbolName()),
|
||||
ARGUMENTS, functionNode));
|
||||
}
|
||||
|
||||
if (syntheticInitializers.isEmpty()) {
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
for(final ListIterator<VarNode> it = syntheticInitializers.listIterator(); it.hasNext();) {
|
||||
it.set((VarNode)it.next().accept(this));
|
||||
}
|
||||
} finally {
|
||||
lc.pop(body);
|
||||
}
|
||||
|
||||
final List<Statement> stmts = body.getStatements();
|
||||
final List<Statement> newStatements = new ArrayList<>(stmts.size() + syntheticInitializers.size());
|
||||
newStatements.addAll(syntheticInitializers);
|
||||
newStatements.addAll(stmts);
|
||||
return functionNode.setBody(lc, body.setStatements(lc, newStatements));
|
||||
}
|
||||
|
||||
private Symbol defineGlobalSymbol(final Block block, final String name) {
|
||||
return defineSymbol(block, name, IS_GLOBAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a new symbol in the given block.
|
||||
*
|
||||
* @param block the block in which to define the symbol
|
||||
* @param name name of symbol.
|
||||
* @param symbolFlags Symbol flags.
|
||||
*
|
||||
* @return Symbol for given name or null for redefinition.
|
||||
*/
|
||||
private Symbol defineSymbol(final Block block, final String name, final int symbolFlags) {
|
||||
int flags = symbolFlags;
|
||||
Symbol symbol = findSymbol(block, name); // Locate symbol.
|
||||
final boolean isGlobal = (flags & KINDMASK) == IS_GLOBAL;
|
||||
|
||||
// Global variables are implicitly always scope variables too.
|
||||
if (isGlobal) {
|
||||
flags |= IS_SCOPE;
|
||||
}
|
||||
|
||||
if (lc.getCurrentFunction().isProgram()) {
|
||||
flags |= IS_PROGRAM_LEVEL;
|
||||
}
|
||||
|
||||
final boolean isParam = (flags & KINDMASK) == IS_PARAM;
|
||||
final boolean isVar = (flags & KINDMASK) == IS_VAR;
|
||||
|
||||
final FunctionNode function = lc.getFunction(block);
|
||||
if (symbol != null) {
|
||||
// Symbol was already defined. Check if it needs to be redefined.
|
||||
if (isParam) {
|
||||
if (!isLocal(function, symbol)) {
|
||||
// Not defined in this function. Create a new definition.
|
||||
symbol = null;
|
||||
} else if (symbol.isParam()) {
|
||||
// Duplicate parameter. Null return will force an error.
|
||||
throw new AssertionError("duplicate parameter");
|
||||
}
|
||||
} else if (isVar) {
|
||||
if ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET) {
|
||||
// Always create a new definition.
|
||||
symbol = null;
|
||||
} else {
|
||||
// Not defined in this function. Create a new definition.
|
||||
if (!isLocal(function, symbol) || symbol.less(IS_VAR)) {
|
||||
symbol = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol == null) {
|
||||
// If not found, then create a new one.
|
||||
Block symbolBlock;
|
||||
|
||||
// Determine where to create it.
|
||||
if (isVar && ((flags & IS_INTERNAL) == IS_INTERNAL || (flags & IS_LET) == IS_LET)) {
|
||||
symbolBlock = block; //internal vars are always defined in the block closest to them
|
||||
} else if (isGlobal) {
|
||||
symbolBlock = lc.getOutermostFunction().getBody();
|
||||
} else {
|
||||
symbolBlock = lc.getFunctionBody(function);
|
||||
}
|
||||
|
||||
// Create and add to appropriate block.
|
||||
symbol = createSymbol(name, flags);
|
||||
symbolBlock.putSymbol(lc, symbol);
|
||||
|
||||
if ((flags & IS_SCOPE) == 0) {
|
||||
// Initial assumption; symbol can lose its slot later
|
||||
symbol.setNeedsSlot(true);
|
||||
}
|
||||
} else if (symbol.less(flags)) {
|
||||
symbol.setFlags(flags);
|
||||
}
|
||||
|
||||
if((isVar || isParam) && compiler.useOptimisticTypes() && compiler.isOnDemandCompilation()) {
|
||||
compiler.declareLocalSymbol(name);
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
private <T extends Node> T end(final T node) {
|
||||
return end(node, true);
|
||||
}
|
||||
|
||||
private <T extends Node> T end(final T node, final boolean printNode) {
|
||||
if (debug) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("[LEAVE ").
|
||||
append(name(node)).
|
||||
append("] ").
|
||||
append(printNode ? node.toString() : "").
|
||||
append(" in '").
|
||||
append(lc.getCurrentFunction().getName()).
|
||||
append('\'');
|
||||
|
||||
if (node instanceof IdentNode) {
|
||||
final Symbol symbol = ((IdentNode)node).getSymbol();
|
||||
if (symbol == null) {
|
||||
sb.append(" <NO SYMBOL>");
|
||||
} else {
|
||||
sb.append(" <symbol=").append(symbol).append('>');
|
||||
}
|
||||
}
|
||||
|
||||
log.unindent();
|
||||
log.info(sb);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterBlock(final Block block) {
|
||||
start(block);
|
||||
block.clearSymbols();
|
||||
|
||||
if (lc.isFunctionBody()) {
|
||||
enterFunctionBody();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterCatchNode(final CatchNode catchNode) {
|
||||
final IdentNode exception = catchNode.getException();
|
||||
final Block block = lc.getCurrentBlock();
|
||||
|
||||
start(catchNode);
|
||||
|
||||
// define block-local exception variable
|
||||
final String exname = exception.getName();
|
||||
// If the name of the exception starts with ":e", this is a synthetic catch block, likely a catch-all. Its
|
||||
// symbol is naturally internal, and should be treated as such.
|
||||
final boolean isInternal = exname.startsWith(EXCEPTION_PREFIX.symbolName());
|
||||
defineSymbol(block, exname, IS_VAR | IS_LET | (isInternal ? IS_INTERNAL : 0) | HAS_OBJECT_VALUE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void enterFunctionBody() {
|
||||
final FunctionNode functionNode = lc.getCurrentFunction();
|
||||
final Block body = lc.getCurrentBlock();
|
||||
|
||||
initFunctionWideVariables(functionNode, body);
|
||||
|
||||
if (functionNode.isProgram()) {
|
||||
initGlobalSymbols(body);
|
||||
} else if (!functionNode.isDeclared() && !functionNode.isAnonymous()) {
|
||||
// It's neither declared nor program - it's a function expression then; assign it a self-symbol unless it's
|
||||
// anonymous.
|
||||
final String name = functionNode.getIdent().getName();
|
||||
assert name != null;
|
||||
assert body.getExistingSymbol(name) == null;
|
||||
defineSymbol(body, name, IS_VAR | IS_FUNCTION_SELF | HAS_OBJECT_VALUE);
|
||||
if(functionNode.allVarsInScope()) { // basically, has deep eval
|
||||
lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
|
||||
}
|
||||
}
|
||||
|
||||
acceptDeclarations(functionNode, body);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterFunctionNode(final FunctionNode functionNode) {
|
||||
// TODO: once we have information on symbols used by nested functions, we can stop descending into nested
|
||||
// functions with on-demand compilation, e.g. add
|
||||
// if(!thisProperties.isEmpty() && env.isOnDemandCompilation()) {
|
||||
// return false;
|
||||
// }
|
||||
start(functionNode, false);
|
||||
|
||||
thisProperties.push(new HashSet<String>());
|
||||
|
||||
//an outermost function in our lexical context that is not a program
|
||||
//is possible - it is a function being compiled lazily
|
||||
if (functionNode.isDeclared()) {
|
||||
final Iterator<Block> blocks = lc.getBlocks();
|
||||
if (blocks.hasNext()) {
|
||||
defineSymbol(blocks.next(), functionNode.getIdent().getName(), IS_VAR);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean enterVarNode(final VarNode varNode) {
|
||||
start(varNode);
|
||||
defineSymbol(lc.getCurrentBlock(), varNode.getName().getName(), IS_VAR | (lc.getCurrentFunction().isProgram() ? IS_SCOPE : 0));
|
||||
return true;
|
||||
}
|
||||
|
||||
private Symbol exceptionSymbol() {
|
||||
return newObjectInternal(EXCEPTION_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* This has to run before fix assignment types, store any type specializations for
|
||||
* paramters, then turn then to objects for the generic version of this method
|
||||
*
|
||||
* @param functionNode functionNode
|
||||
*/
|
||||
private FunctionNode finalizeParameters(final FunctionNode functionNode) {
|
||||
final List<IdentNode> newParams = new ArrayList<>();
|
||||
final boolean isVarArg = functionNode.isVarArg();
|
||||
|
||||
final Block body = functionNode.getBody();
|
||||
for (final IdentNode param : functionNode.getParameters()) {
|
||||
final Symbol paramSymbol = body.getExistingSymbol(param.getName());
|
||||
assert paramSymbol != null;
|
||||
assert paramSymbol.isParam() : paramSymbol + " " + paramSymbol.getFlags();
|
||||
newParams.add((IdentNode)param.setSymbol(paramSymbol));
|
||||
|
||||
// parameters should not be slots for a function that uses variable arity signature
|
||||
if (isVarArg) {
|
||||
paramSymbol.setNeedsSlot(false);
|
||||
}
|
||||
}
|
||||
|
||||
return functionNode.setParameters(lc, newParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for symbol in the lexical context starting from the given block.
|
||||
* @param name Symbol name.
|
||||
* @return Found symbol or null if not found.
|
||||
*/
|
||||
private Symbol findSymbol(final Block block, final String name) {
|
||||
for (final Iterator<Block> blocks = lc.getBlocks(block); blocks.hasNext();) {
|
||||
final Symbol symbol = blocks.next().getExistingSymbol(name);
|
||||
if (symbol != null) {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the current function as one using any global symbol. The function and all its parent functions will all be
|
||||
* marked as needing parent scope.
|
||||
* @see FunctionNode#needsParentScope()
|
||||
*/
|
||||
private void functionUsesGlobalSymbol() {
|
||||
for (final Iterator<FunctionNode> fns = lc.getFunctions(); fns.hasNext();) {
|
||||
lc.setFlag(fns.next(), FunctionNode.USES_ANCESTOR_SCOPE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the current function as one using a scoped symbol. The block defining the symbol will be marked as needing
|
||||
* its own scope to hold the variable. If the symbol is defined outside of the current function, it and all
|
||||
* functions up to (but not including) the function containing the defining block will be marked as needing parent
|
||||
* function scope.
|
||||
* @see FunctionNode#needsParentScope()
|
||||
*/
|
||||
private void functionUsesScopeSymbol(final Symbol symbol) {
|
||||
final String name = symbol.getName();
|
||||
for (final Iterator<LexicalContextNode> contextNodeIter = lc.getAllNodes(); contextNodeIter.hasNext(); ) {
|
||||
final LexicalContextNode node = contextNodeIter.next();
|
||||
if (node instanceof Block) {
|
||||
final Block block = (Block)node;
|
||||
if (block.getExistingSymbol(name) != null) {
|
||||
assert lc.contains(block);
|
||||
lc.setBlockNeedsScope(block);
|
||||
break;
|
||||
}
|
||||
} else if (node instanceof FunctionNode) {
|
||||
lc.setFlag(node, FunctionNode.USES_ANCESTOR_SCOPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares that the current function is using the symbol.
|
||||
* @param symbol the symbol used by the current function.
|
||||
*/
|
||||
private void functionUsesSymbol(final Symbol symbol) {
|
||||
assert symbol != null;
|
||||
if (symbol.isScope()) {
|
||||
if (symbol.isGlobal()) {
|
||||
functionUsesGlobalSymbol();
|
||||
} else {
|
||||
functionUsesScopeSymbol(symbol);
|
||||
}
|
||||
} else {
|
||||
assert !symbol.isGlobal(); // Every global is also scope
|
||||
}
|
||||
}
|
||||
|
||||
private void initCompileConstant(final CompilerConstants cc, final Block block, final int flags) {
|
||||
defineSymbol(block, cc.symbolName(), flags).setNeedsSlot(true);
|
||||
}
|
||||
|
||||
private void initFunctionWideVariables(final FunctionNode functionNode, final Block body) {
|
||||
initCompileConstant(CALLEE, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
|
||||
initCompileConstant(THIS, body, IS_PARAM | IS_THIS | HAS_OBJECT_VALUE);
|
||||
|
||||
if (functionNode.isVarArg()) {
|
||||
initCompileConstant(VARARGS, body, IS_PARAM | IS_INTERNAL | HAS_OBJECT_VALUE);
|
||||
if (functionNode.needsArguments()) {
|
||||
initCompileConstant(ARGUMENTS, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
|
||||
defineSymbol(body, ARGUMENTS_VAR.symbolName(), IS_VAR | HAS_OBJECT_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
initParameters(functionNode, body);
|
||||
initCompileConstant(SCOPE, body, IS_VAR | IS_INTERNAL | HAS_OBJECT_VALUE);
|
||||
initCompileConstant(RETURN, body, IS_VAR | IS_INTERNAL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Move any properties from the global map into the scope of this function (which must be a program function).
|
||||
* @param block the function node body for which to init scope vars
|
||||
*/
|
||||
private void initGlobalSymbols(final Block block) {
|
||||
final PropertyMap map = Context.getGlobalMap();
|
||||
|
||||
for (final Property property : map.getProperties()) {
|
||||
final Symbol symbol = defineGlobalSymbol(block, property.getKey());
|
||||
log.info("Added global symbol from property map ", symbol);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize parameters for function node.
|
||||
* @param functionNode the function node
|
||||
*/
|
||||
private void initParameters(final FunctionNode functionNode, final Block body) {
|
||||
final boolean isVarArg = functionNode.isVarArg();
|
||||
final boolean scopeParams = functionNode.allVarsInScope() || isVarArg;
|
||||
for (final IdentNode param : functionNode.getParameters()) {
|
||||
final Symbol symbol = defineSymbol(body, param.getName(), IS_PARAM);
|
||||
if(scopeParams) {
|
||||
// NOTE: this "set is scope" is a poor substitute for clear expression of where the symbol is stored.
|
||||
// It will force creation of scopes where they would otherwise not necessarily be needed (functions
|
||||
// using arguments object and other variable arity functions). Tracked by JDK-8038942.
|
||||
symbol.setIsScope();
|
||||
assert symbol.hasSlot();
|
||||
if(isVarArg) {
|
||||
symbol.setNeedsSlot(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the symbol local to (that is, defined in) the specified function?
|
||||
* @param function the function
|
||||
* @param symbol the symbol
|
||||
* @return true if the symbol is defined in the specified function
|
||||
*/
|
||||
private boolean isLocal(final FunctionNode function, final Symbol symbol) {
|
||||
final FunctionNode definingFn = lc.getDefiningFunction(symbol);
|
||||
assert definingFn != null;
|
||||
return definingFn == function;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveASSIGN(final BinaryNode binaryNode) {
|
||||
// If we're assigning a property of the this object ("this.foo = ..."), record it.
|
||||
|
||||
final Expression lhs = binaryNode.lhs();
|
||||
if (lhs instanceof AccessNode) {
|
||||
final AccessNode accessNode = (AccessNode) lhs;
|
||||
final Expression base = accessNode.getBase();
|
||||
if (base instanceof IdentNode) {
|
||||
final Symbol symbol = ((IdentNode)base).getSymbol();
|
||||
if(symbol.isThis()) {
|
||||
thisProperties.peek().add(accessNode.getProperty());
|
||||
}
|
||||
}
|
||||
}
|
||||
return binaryNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveDELETE(final UnaryNode unaryNode) {
|
||||
final FunctionNode currentFunctionNode = lc.getCurrentFunction();
|
||||
final boolean strictMode = currentFunctionNode.isStrict();
|
||||
final Expression rhs = unaryNode.getExpression();
|
||||
final Expression strictFlagNode = (Expression)LiteralNode.newInstance(unaryNode, strictMode).accept(this);
|
||||
|
||||
Request request = Request.DELETE;
|
||||
final List<Expression> args = new ArrayList<>();
|
||||
|
||||
if (rhs instanceof IdentNode) {
|
||||
final IdentNode ident = (IdentNode)rhs;
|
||||
// If this is a declared variable or a function parameter, delete always fails (except for globals).
|
||||
final String name = ident.getName();
|
||||
final Symbol symbol = ident.getSymbol();
|
||||
final boolean failDelete = strictMode || symbol.isParam() || (symbol.isVar() && !symbol.isProgramLevel());
|
||||
|
||||
if (failDelete && symbol.isThis()) {
|
||||
return LiteralNode.newInstance(unaryNode, true).accept(this);
|
||||
}
|
||||
final Expression literalNode = (Expression)LiteralNode.newInstance(unaryNode, name).accept(this);
|
||||
|
||||
if (!failDelete) {
|
||||
args.add(compilerConstantIdentifier(SCOPE));
|
||||
}
|
||||
args.add(literalNode);
|
||||
args.add(strictFlagNode);
|
||||
|
||||
if (failDelete) {
|
||||
request = Request.FAIL_DELETE;
|
||||
}
|
||||
} else if (rhs instanceof AccessNode) {
|
||||
final Expression base = ((AccessNode)rhs).getBase();
|
||||
final String property = ((AccessNode)rhs).getProperty();
|
||||
|
||||
args.add(base);
|
||||
args.add((Expression)LiteralNode.newInstance(unaryNode, property).accept(this));
|
||||
args.add(strictFlagNode);
|
||||
|
||||
} else if (rhs instanceof IndexNode) {
|
||||
final IndexNode indexNode = (IndexNode)rhs;
|
||||
final Expression base = indexNode.getBase();
|
||||
final Expression index = indexNode.getIndex();
|
||||
|
||||
args.add(base);
|
||||
args.add(index);
|
||||
args.add(strictFlagNode);
|
||||
|
||||
} else {
|
||||
return LiteralNode.newInstance(unaryNode, true).accept(this);
|
||||
}
|
||||
return new RuntimeNode(unaryNode, request, args).accept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveForNode(final ForNode forNode) {
|
||||
if (forNode.isForIn()) {
|
||||
forNode.setIterator(newObjectInternal(ITERATOR_PREFIX)); //NASHORN-73
|
||||
}
|
||||
|
||||
return end(forNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveFunctionNode(final FunctionNode functionNode) {
|
||||
|
||||
return markProgramBlock(
|
||||
removeUnusedSlots(
|
||||
createSyntheticInitializers(
|
||||
finalizeParameters(
|
||||
lc.applyTopFlags(functionNode))))
|
||||
.setThisProperties(lc, thisProperties.pop().size())
|
||||
.setState(lc, CompilationState.SYMBOLS_ASSIGNED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveIdentNode(final IdentNode identNode) {
|
||||
final String name = identNode.getName();
|
||||
|
||||
if (identNode.isPropertyName()) {
|
||||
return identNode;
|
||||
}
|
||||
|
||||
final Block block = lc.getCurrentBlock();
|
||||
|
||||
Symbol symbol = findSymbol(block, name);
|
||||
|
||||
//If an existing symbol with the name is found, use that otherwise, declare a new one
|
||||
if (symbol != null) {
|
||||
log.info("Existing symbol = ", symbol);
|
||||
if (symbol.isFunctionSelf()) {
|
||||
final FunctionNode functionNode = lc.getDefiningFunction(symbol);
|
||||
assert functionNode != null;
|
||||
assert lc.getFunctionBody(functionNode).getExistingSymbol(CALLEE.symbolName()) != null;
|
||||
lc.setFlag(functionNode, FunctionNode.USES_SELF_SYMBOL);
|
||||
}
|
||||
|
||||
// if symbol is non-local or we're in a with block, we need to put symbol in scope (if it isn't already)
|
||||
maybeForceScope(symbol);
|
||||
} else {
|
||||
log.info("No symbol exists. Declare as global: ", symbol);
|
||||
symbol = defineGlobalSymbol(block, name);
|
||||
Symbol.setSymbolIsScope(lc, symbol);
|
||||
}
|
||||
|
||||
functionUsesSymbol(symbol);
|
||||
|
||||
if (!identNode.isInitializedHere()) {
|
||||
symbol.increaseUseCount();
|
||||
}
|
||||
|
||||
return end(identNode.setSymbol(symbol));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveSwitchNode(final SwitchNode switchNode) {
|
||||
// We only need a symbol for the tag if it's not an integer switch node
|
||||
if(!switchNode.isInteger()) {
|
||||
switchNode.setTag(newObjectInternal(SWITCH_TAG_PREFIX));
|
||||
}
|
||||
return switchNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveTryNode(final TryNode tryNode) {
|
||||
tryNode.setException(exceptionSymbol());
|
||||
if (tryNode.getFinallyBody() != null) {
|
||||
tryNode.setFinallyCatchAll(exceptionSymbol());
|
||||
}
|
||||
|
||||
end(tryNode);
|
||||
|
||||
return tryNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Node leaveTYPEOF(final UnaryNode unaryNode) {
|
||||
final Expression rhs = unaryNode.getExpression();
|
||||
|
||||
final List<Expression> args = new ArrayList<>();
|
||||
if (rhs instanceof IdentNode && !isParamOrVar((IdentNode)rhs)) {
|
||||
args.add(compilerConstantIdentifier(SCOPE));
|
||||
args.add((Expression)LiteralNode.newInstance(rhs, ((IdentNode)rhs).getName()).accept(this)); //null
|
||||
} else {
|
||||
args.add(rhs);
|
||||
args.add((Expression)LiteralNode.newInstance(unaryNode).accept(this)); //null, do not reuse token of identifier rhs, it can be e.g. 'this'
|
||||
}
|
||||
|
||||
final Node runtimeNode = new RuntimeNode(unaryNode, Request.TYPEOF, args).accept(this);
|
||||
|
||||
end(unaryNode);
|
||||
|
||||
return runtimeNode;
|
||||
}
|
||||
|
||||
private FunctionNode markProgramBlock(final FunctionNode functionNode) {
|
||||
if (compiler.isOnDemandCompilation() || !functionNode.isProgram()) {
|
||||
return functionNode;
|
||||
}
|
||||
|
||||
assert functionNode.getId() == 1;
|
||||
return functionNode.setBody(lc, functionNode.getBody().setFlag(lc, Block.IS_GLOBAL_SCOPE));
|
||||
}
|
||||
|
||||
/**
|
||||
* If the symbol isn't already a scope symbol, but it needs to be (see {@link #symbolNeedsToBeScope(Symbol)}, it is
|
||||
* promoted to a scope symbol and its block marked as needing a scope.
|
||||
* @param symbol the symbol that might be scoped
|
||||
*/
|
||||
private void maybeForceScope(final Symbol symbol) {
|
||||
if (!symbol.isScope() && symbolNeedsToBeScope(symbol)) {
|
||||
Symbol.setSymbolIsScope(lc, symbol);
|
||||
}
|
||||
}
|
||||
|
||||
private Symbol newInternal(final CompilerConstants cc, final int flags) {
|
||||
return defineSymbol(lc.getCurrentBlock(), lc.getCurrentFunction().uniqueName(cc.symbolName()), IS_VAR | IS_INTERNAL | flags); //NASHORN-73
|
||||
}
|
||||
|
||||
private Symbol newObjectInternal(final CompilerConstants cc) {
|
||||
return newInternal(cc, HAS_OBJECT_VALUE);
|
||||
}
|
||||
|
||||
private boolean start(final Node node) {
|
||||
return start(node, true);
|
||||
}
|
||||
|
||||
private boolean start(final Node node, final boolean printNode) {
|
||||
if (debug) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append("[ENTER ").
|
||||
append(name(node)).
|
||||
append("] ").
|
||||
append(printNode ? node.toString() : "").
|
||||
append(" in '").
|
||||
append(lc.getCurrentFunction().getName()).
|
||||
append("'");
|
||||
log.info(sb);
|
||||
log.indent();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the symbol has to be a scope symbol. In general terms, it has to be a scope symbol if it can only
|
||||
* be reached from the current block by traversing a function node, a split node, or a with node.
|
||||
* @param symbol the symbol checked for needing to be a scope symbol
|
||||
* @return true if the symbol has to be a scope symbol.
|
||||
*/
|
||||
private boolean symbolNeedsToBeScope(final Symbol symbol) {
|
||||
if (symbol.isThis() || symbol.isInternal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lc.getCurrentFunction().allVarsInScope()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean previousWasBlock = false;
|
||||
for (final Iterator<LexicalContextNode> it = lc.getAllNodes(); it.hasNext();) {
|
||||
final LexicalContextNode node = it.next();
|
||||
if (node instanceof FunctionNode || node instanceof SplitNode || isSplitArray(node)) {
|
||||
// We reached the function boundary or a splitting boundary without seeing a definition for the symbol.
|
||||
// It needs to be in scope.
|
||||
return true;
|
||||
} else if (node instanceof WithNode) {
|
||||
if (previousWasBlock) {
|
||||
// We reached a WithNode; the symbol must be scoped. Note that if the WithNode was not immediately
|
||||
// preceded by a block, this means we're currently processing its expression, not its body,
|
||||
// therefore it doesn't count.
|
||||
return true;
|
||||
}
|
||||
previousWasBlock = false;
|
||||
} else if (node instanceof Block) {
|
||||
if (((Block)node).getExistingSymbol(symbol.getName()) == symbol) {
|
||||
// We reached the block that defines the symbol without reaching either the function boundary, or a
|
||||
// WithNode. The symbol need not be scoped.
|
||||
return false;
|
||||
}
|
||||
previousWasBlock = true;
|
||||
} else {
|
||||
previousWasBlock = false;
|
||||
}
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
private static boolean isSplitArray(final LexicalContextNode expr) {
|
||||
if(!(expr instanceof ArrayLiteralNode)) {
|
||||
return false;
|
||||
}
|
||||
final List<ArrayUnit> units = ((ArrayLiteralNode)expr).getUnits();
|
||||
return !(units == null || units.isEmpty());
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -32,10 +32,10 @@ import static jdk.nashorn.internal.codegen.Condition.LE;
|
||||
import static jdk.nashorn.internal.codegen.Condition.LT;
|
||||
import static jdk.nashorn.internal.codegen.Condition.NE;
|
||||
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.BinaryNode;
|
||||
import jdk.nashorn.internal.ir.Expression;
|
||||
import jdk.nashorn.internal.ir.TernaryNode;
|
||||
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
|
||||
import jdk.nashorn.internal.ir.LocalVariableConversion;
|
||||
import jdk.nashorn.internal.ir.UnaryNode;
|
||||
|
||||
/**
|
||||
@ -57,7 +57,7 @@ final class BranchOptimizer {
|
||||
}
|
||||
|
||||
private void branchOptimizer(final UnaryNode unaryNode, final Label label, final boolean state) {
|
||||
final Expression rhs = unaryNode.rhs();
|
||||
final Expression rhs = unaryNode.getExpression();
|
||||
|
||||
switch (unaryNode.tokenType()) {
|
||||
case NOT:
|
||||
@ -71,13 +71,7 @@ final class BranchOptimizer {
|
||||
break;
|
||||
}
|
||||
|
||||
// convert to boolean
|
||||
codegen.load(unaryNode, Type.BOOLEAN);
|
||||
if (state) {
|
||||
method.ifne(label);
|
||||
} else {
|
||||
method.ifeq(label);
|
||||
}
|
||||
loadTestAndJump(unaryNode, label, state);
|
||||
}
|
||||
|
||||
private void branchOptimizer(final BinaryNode binaryNode, final Label label, final boolean state) {
|
||||
@ -88,86 +82,97 @@ final class BranchOptimizer {
|
||||
case AND:
|
||||
if (state) {
|
||||
final Label skip = new Label("skip");
|
||||
branchOptimizer(lhs, skip, false);
|
||||
branchOptimizer(rhs, label, true);
|
||||
optimizeLogicalOperand(lhs, skip, false, false);
|
||||
optimizeLogicalOperand(rhs, label, true, true);
|
||||
method.label(skip);
|
||||
} else {
|
||||
branchOptimizer(lhs, label, false);
|
||||
branchOptimizer(rhs, label, false);
|
||||
optimizeLogicalOperand(lhs, label, false, false);
|
||||
optimizeLogicalOperand(rhs, label, false, true);
|
||||
}
|
||||
return;
|
||||
|
||||
case OR:
|
||||
if (state) {
|
||||
branchOptimizer(lhs, label, true);
|
||||
branchOptimizer(rhs, label, true);
|
||||
optimizeLogicalOperand(lhs, label, true, false);
|
||||
optimizeLogicalOperand(rhs, label, true, true);
|
||||
} else {
|
||||
final Label skip = new Label("skip");
|
||||
branchOptimizer(lhs, skip, true);
|
||||
branchOptimizer(rhs, label, false);
|
||||
optimizeLogicalOperand(lhs, skip, true, false);
|
||||
optimizeLogicalOperand(rhs, label, false, true);
|
||||
method.label(skip);
|
||||
}
|
||||
return;
|
||||
|
||||
case EQ:
|
||||
case EQ_STRICT:
|
||||
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
|
||||
codegen.loadBinaryOperands(binaryNode);
|
||||
method.conditionalJump(state ? EQ : NE, true, label);
|
||||
return;
|
||||
|
||||
case NE:
|
||||
case NE_STRICT:
|
||||
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
|
||||
codegen.loadBinaryOperands(binaryNode);
|
||||
method.conditionalJump(state ? NE : EQ, true, label);
|
||||
return;
|
||||
|
||||
case GE:
|
||||
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
|
||||
method.conditionalJump(state ? GE : LT, !state, label);
|
||||
codegen.loadBinaryOperands(binaryNode);
|
||||
method.conditionalJump(state ? GE : LT, false, label);
|
||||
return;
|
||||
|
||||
case GT:
|
||||
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
|
||||
method.conditionalJump(state ? GT : LE, !state, label);
|
||||
codegen.loadBinaryOperands(binaryNode);
|
||||
method.conditionalJump(state ? GT : LE, false, label);
|
||||
return;
|
||||
|
||||
case LE:
|
||||
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
|
||||
method.conditionalJump(state ? LE : GT, state, label);
|
||||
codegen.loadBinaryOperands(binaryNode);
|
||||
method.conditionalJump(state ? LE : GT, true, label);
|
||||
return;
|
||||
|
||||
case LT:
|
||||
codegen.loadBinaryOperands(lhs, rhs, Type.widest(lhs.getType(), rhs.getType()));
|
||||
method.conditionalJump(state ? LT : GE, state, label);
|
||||
codegen.loadBinaryOperands(binaryNode);
|
||||
method.conditionalJump(state ? LT : GE, true, label);
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
codegen.load(binaryNode, Type.BOOLEAN);
|
||||
if (state) {
|
||||
method.ifne(label);
|
||||
} else {
|
||||
method.ifeq(label);
|
||||
}
|
||||
loadTestAndJump(binaryNode, label, state);
|
||||
}
|
||||
|
||||
private void optimizeLogicalOperand(final Expression expr, final Label label, final boolean state, final boolean isRhs) {
|
||||
final JoinPredecessorExpression jpexpr = (JoinPredecessorExpression)expr;
|
||||
if(LocalVariableConversion.hasLiveConversion(jpexpr)) {
|
||||
final Label after = new Label("after");
|
||||
branchOptimizer(jpexpr.getExpression(), after, !state);
|
||||
method.beforeJoinPoint(jpexpr);
|
||||
method._goto(label);
|
||||
method.label(after);
|
||||
if(isRhs) {
|
||||
method.beforeJoinPoint(jpexpr);
|
||||
}
|
||||
} else {
|
||||
branchOptimizer(jpexpr.getExpression(), label, state);
|
||||
}
|
||||
}
|
||||
private void branchOptimizer(final Expression 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;
|
||||
}
|
||||
if (node instanceof BinaryNode) {
|
||||
branchOptimizer((BinaryNode)node, label, state);
|
||||
return;
|
||||
}
|
||||
|
||||
codegen.load(node, Type.BOOLEAN);
|
||||
if (node instanceof UnaryNode) {
|
||||
branchOptimizer((UnaryNode)node, label, state);
|
||||
return;
|
||||
}
|
||||
|
||||
loadTestAndJump(node, label, state);
|
||||
}
|
||||
|
||||
private void loadTestAndJump(final Expression node, final Label label, final boolean state) {
|
||||
codegen.loadExpressionAsBoolean(node);
|
||||
if (state) {
|
||||
method.ifne(label);
|
||||
} else {
|
||||
|
@ -49,25 +49,27 @@ 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 static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
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.ir.SplitNode;
|
||||
import jdk.nashorn.internal.ir.debug.NashornClassReader;
|
||||
import jdk.nashorn.internal.ir.debug.NashornTextifier;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||
import jdk.nashorn.internal.runtime.RewriteException;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.Source;
|
||||
|
||||
@ -106,6 +108,8 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
* @see Compiler
|
||||
*/
|
||||
public class ClassEmitter implements Emitter {
|
||||
/** Default flags for class generation - public class */
|
||||
private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
|
||||
|
||||
/** Sanity check flag - have we started on a class? */
|
||||
private boolean classStarted;
|
||||
@ -123,10 +127,7 @@ public class ClassEmitter implements Emitter {
|
||||
protected final ClassWriter cw;
|
||||
|
||||
/** The script environment */
|
||||
protected final ScriptEnvironment env;
|
||||
|
||||
/** Default flags for class generation - oublic class */
|
||||
private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
|
||||
protected final Context context;
|
||||
|
||||
/** Compile unit class name. */
|
||||
private String unitClassName;
|
||||
@ -141,10 +142,8 @@ public class ClassEmitter implements Emitter {
|
||||
* @param env script environment
|
||||
* @param cw ASM classwriter
|
||||
*/
|
||||
private ClassEmitter(final ScriptEnvironment env, final ClassWriter cw) {
|
||||
assert env != null;
|
||||
|
||||
this.env = env;
|
||||
private ClassEmitter(final Context context, final ClassWriter cw) {
|
||||
this.context = context;
|
||||
this.cw = cw;
|
||||
this.methodsStarted = new HashSet<>();
|
||||
}
|
||||
@ -157,8 +156,8 @@ public class ClassEmitter implements Emitter {
|
||||
* @param superClassName super class name for class
|
||||
* @param interfaceNames names of interfaces implemented by this class, or null if none
|
||||
*/
|
||||
ClassEmitter(final ScriptEnvironment env, final String className, final String superClassName, final String... interfaceNames) {
|
||||
this(env, new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS));
|
||||
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);
|
||||
}
|
||||
|
||||
@ -170,8 +169,8 @@ public class ClassEmitter implements Emitter {
|
||||
* @param unitClassName Compile unit class name.
|
||||
* @param strictMode Should we generate this method in strict mode
|
||||
*/
|
||||
ClassEmitter(final ScriptEnvironment env, final String sourceName, final String unitClassName, final boolean strictMode) {
|
||||
this(env,
|
||||
ClassEmitter(final Context context, final String sourceName, final String unitClassName, final boolean strictMode) {
|
||||
this(context,
|
||||
new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
|
||||
private static final String OBJECT_CLASS = "java/lang/Object";
|
||||
|
||||
@ -197,6 +196,10 @@ public class ClassEmitter implements Emitter {
|
||||
defineCommonStatics(strictMode);
|
||||
}
|
||||
|
||||
Context getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the compile unit class name.
|
||||
* @return the name of the compile unit class name.
|
||||
@ -274,51 +277,51 @@ public class ClassEmitter implements Emitter {
|
||||
}
|
||||
|
||||
// $getXXXX$array - get the ith entry from the constants table and cast to XXXX[].
|
||||
for (final Class<?> cls : constantMethodNeeded) {
|
||||
if (cls.isArray()) {
|
||||
defineGetArrayMethod(cls);
|
||||
for (final Class<?> clazz : constantMethodNeeded) {
|
||||
if (clazz.isArray()) {
|
||||
defineGetArrayMethod(clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a primitive specific method for getting the ith entry from the constants table and cast.
|
||||
* @param cls Array class.
|
||||
* Constructs a primitive specific method for getting the ith entry from the constants table as an array.
|
||||
* @param clazz Array class.
|
||||
*/
|
||||
private void defineGetArrayMethod(final Class<?> cls) {
|
||||
private void defineGetArrayMethod(final Class<?> clazz) {
|
||||
assert unitClassName != null;
|
||||
|
||||
final String methodName = getArrayMethodName(cls);
|
||||
final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, cls, int.class);
|
||||
final String methodName = getArrayMethodName(clazz);
|
||||
final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, clazz, int.class);
|
||||
|
||||
getArrayMethod.begin();
|
||||
getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
|
||||
.load(Type.INT, 0)
|
||||
.arrayload()
|
||||
.checkcast(cls)
|
||||
.dup()
|
||||
.arraylength()
|
||||
.invoke(staticCallNoLookup(Arrays.class, "copyOf", cls, cls, int.class))
|
||||
.checkcast(clazz)
|
||||
.invoke(virtualCallNoLookup(clazz, "clone", Object.class))
|
||||
.checkcast(clazz)
|
||||
._return();
|
||||
getArrayMethod.end();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate the name of a get array from constant pool method.
|
||||
* @param cls Name of array class.
|
||||
* @param clazz Name of array class.
|
||||
* @return Method name.
|
||||
*/
|
||||
static String getArrayMethodName(final Class<?> cls) {
|
||||
assert cls.isArray();
|
||||
return GET_ARRAY_PREFIX.symbolName() + cls.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
|
||||
static String getArrayMethodName(final Class<?> clazz) {
|
||||
assert clazz.isArray();
|
||||
return GET_ARRAY_PREFIX.symbolName() + clazz.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a get constant method is issued for the class.
|
||||
* @param cls Class of constant.
|
||||
* @param clazz Class of constant.
|
||||
*/
|
||||
void needGetConstantMethod(final Class<?> cls) {
|
||||
constantMethodNeeded.add(cls);
|
||||
void needGetConstantMethod(final Class<?> clazz) {
|
||||
constantMethodNeeded.add(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -376,16 +379,19 @@ public class ClassEmitter implements Emitter {
|
||||
static String disassemble(final byte[] bytecode) {
|
||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try (final PrintWriter pw = new PrintWriter(baos)) {
|
||||
new ClassReader(bytecode).accept(new TraceClassVisitor(pw), 0);
|
||||
final NashornClassReader cr = new NashornClassReader(bytecode);
|
||||
final Context ctx = AccessController.doPrivileged(new PrivilegedAction<Context>() {
|
||||
@Override
|
||||
public Context run() {
|
||||
return Context.getContext();
|
||||
}
|
||||
});
|
||||
final TraceClassVisitor tcv = new TraceClassVisitor(null, new NashornTextifier(ctx.getEnv(), cr), pw);
|
||||
cr.accept(tcv, 0);
|
||||
}
|
||||
return new String(baos.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return env used for class emission
|
||||
*/
|
||||
ScriptEnvironment getEnv() {
|
||||
return env;
|
||||
final String str = new String(baos.toByteArray());
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -475,16 +481,35 @@ public class ClassEmitter implements Emitter {
|
||||
* @return method emitter to use for weaving this method
|
||||
*/
|
||||
MethodEmitter method(final FunctionNode functionNode) {
|
||||
final FunctionSignature signature = new FunctionSignature(functionNode);
|
||||
final MethodVisitor mv = cw.visitMethod(
|
||||
ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
|
||||
functionNode.getName(),
|
||||
new FunctionSignature(functionNode).toString(),
|
||||
signature.toString(),
|
||||
null,
|
||||
null);
|
||||
|
||||
return new MethodEmitter(this, mv, functionNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new method to the class, representing a rest-of version of the function node
|
||||
*
|
||||
* @param functionNode the function node to generate a method for
|
||||
* @return method emitter to use for weaving this method
|
||||
*/
|
||||
MethodEmitter restOfMethod(final FunctionNode functionNode) {
|
||||
final MethodVisitor mv = cw.visitMethod(
|
||||
ACC_PUBLIC | ACC_STATIC,
|
||||
functionNode.getName(),
|
||||
Type.getMethodDescriptor(functionNode.getReturnType().getTypeClass(), RewriteException.class),
|
||||
null,
|
||||
null);
|
||||
|
||||
return new MethodEmitter(this, mv, functionNode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start generating the <clinit> method in the class
|
||||
*
|
||||
@ -636,7 +661,7 @@ public class ClassEmitter implements Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
private MethodVisitor methodVisitor(EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
|
||||
private MethodVisitor methodVisitor(final EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
|
||||
return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -31,7 +31,7 @@ import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.nashorn.internal.IntDeque;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.Block;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
@ -63,6 +63,10 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
||||
* i.e. should we keep it or throw it away */
|
||||
private final Deque<Node> discard = new ArrayDeque<>();
|
||||
|
||||
private final Deque<Map<String, Collection<Label>>> unwarrantedOptimismHandlers = new ArrayDeque<>();
|
||||
private final Deque<StringBuilder> slotTypesDescriptors = new ArrayDeque<>();
|
||||
private final IntDeque splitNodes = new IntDeque();
|
||||
|
||||
/** A stack tracking the next free local variable slot in the blocks. There's one entry for every block
|
||||
* currently on the lexical context stack. */
|
||||
private int[] nextFreeSlots = new int[16];
|
||||
@ -70,46 +74,56 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
||||
/** size of next free slot vector */
|
||||
private int nextFreeSlotsSize;
|
||||
|
||||
private boolean isWithBoundary(final LexicalContextNode node) {
|
||||
return node instanceof Block && !isEmpty() && peek() instanceof WithNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends LexicalContextNode> T push(final T node) {
|
||||
if (isDynamicScopeBoundary(node)) {
|
||||
++dynamicScopeCount;
|
||||
if (isWithBoundary(node)) {
|
||||
dynamicScopeCount++;
|
||||
} else if (node instanceof FunctionNode) {
|
||||
if (((FunctionNode)node).inDynamicContext()) {
|
||||
dynamicScopeCount++;
|
||||
}
|
||||
splitNodes.push(0);
|
||||
}
|
||||
return super.push(node);
|
||||
}
|
||||
|
||||
void enterSplitNode() {
|
||||
splitNodes.getAndIncrement();
|
||||
pushFreeSlots(methodEmitters.peek().getUsedSlotsWithLiveTemporaries());
|
||||
}
|
||||
|
||||
void exitSplitNode() {
|
||||
final int count = splitNodes.decrementAndGet();
|
||||
assert count >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends LexicalContextNode> T pop(final T node) {
|
||||
final T popped = super.pop(node);
|
||||
if (isDynamicScopeBoundary(popped)) {
|
||||
--dynamicScopeCount;
|
||||
}
|
||||
if (node instanceof Block) {
|
||||
--nextFreeSlotsSize;
|
||||
if (isWithBoundary(node)) {
|
||||
dynamicScopeCount--;
|
||||
assert dynamicScopeCount >= 0;
|
||||
} else if (node instanceof FunctionNode) {
|
||||
if (((FunctionNode)node).inDynamicContext()) {
|
||||
dynamicScopeCount--;
|
||||
assert dynamicScopeCount >= 0;
|
||||
}
|
||||
assert splitNodes.peek() == 0;
|
||||
splitNodes.pop();
|
||||
}
|
||||
return popped;
|
||||
}
|
||||
|
||||
private boolean isDynamicScopeBoundary(final LexicalContextNode node) {
|
||||
if (node instanceof Block) {
|
||||
// Block's immediate parent is a with node. Note we aren't testing for a WithNode, as that'd capture
|
||||
// processing of WithNode.expression too, but it should be unaffected.
|
||||
return !isEmpty() && peek() instanceof WithNode;
|
||||
} else if (node instanceof FunctionNode) {
|
||||
// Function has a direct eval in it (so a top-level "var ..." in the eval code can introduce a new
|
||||
// variable into the function's scope), and it isn't strict (as evals in strict functions get an
|
||||
// isolated scope).
|
||||
return isFunctionDynamicScope((FunctionNode)node);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean inDynamicScope() {
|
||||
return dynamicScopeCount > 0;
|
||||
}
|
||||
|
||||
static boolean isFunctionDynamicScope(FunctionNode fn) {
|
||||
return fn.hasEval() && !fn.isStrict();
|
||||
boolean inSplitNode() {
|
||||
return !splitNodes.isEmpty() && splitNodes.peek() > 0;
|
||||
}
|
||||
|
||||
MethodEmitter pushMethodEmitter(final MethodEmitter newMethod) {
|
||||
@ -123,6 +137,20 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
||||
return methodEmitters.isEmpty() ? null : methodEmitters.peek();
|
||||
}
|
||||
|
||||
void pushUnwarrantedOptimismHandlers() {
|
||||
unwarrantedOptimismHandlers.push(new HashMap<String, Collection<Label>>());
|
||||
slotTypesDescriptors.push(new StringBuilder());
|
||||
}
|
||||
|
||||
Map<String, Collection<Label>> getUnwarrantedOptimismHandlers() {
|
||||
return unwarrantedOptimismHandlers.peek();
|
||||
}
|
||||
|
||||
Map<String, Collection<Label>> popUnwarrantedOptimismHandlers() {
|
||||
slotTypesDescriptors.pop();
|
||||
return unwarrantedOptimismHandlers.pop();
|
||||
}
|
||||
|
||||
CompileUnit pushCompileUnit(final CompileUnit newUnit) {
|
||||
compileUnits.push(newUnit);
|
||||
return newUnit;
|
||||
@ -167,50 +195,77 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
||||
* Get a shared static method representing a dynamic scope get access.
|
||||
*
|
||||
* @param unit current compile unit
|
||||
* @param type the type of the variable
|
||||
* @param symbol the symbol
|
||||
* @param valueType the type of the variable
|
||||
* @param flags the callsite flags
|
||||
* @return an object representing a shared scope call
|
||||
*/
|
||||
SharedScopeCall getScopeGet(final CompileUnit unit, final Type type, final Symbol symbol, final int flags) {
|
||||
final SharedScopeCall scopeCall = new SharedScopeCall(symbol, type, type, null, flags);
|
||||
if (scopeCalls.containsKey(scopeCall)) {
|
||||
return scopeCalls.get(scopeCall);
|
||||
}
|
||||
scopeCall.setClassAndName(unit, getCurrentFunction().uniqueName(":scopeCall"));
|
||||
scopeCalls.put(scopeCall, scopeCall);
|
||||
return scopeCall;
|
||||
SharedScopeCall getScopeGet(final CompileUnit unit, final Symbol symbol, final Type valueType, final int flags) {
|
||||
return getScopeCall(unit, symbol, valueType, valueType, null, flags);
|
||||
}
|
||||
|
||||
void onEnterBlock(final Block block) {
|
||||
pushFreeSlots(assignSlots(block, isFunctionBody() ? 0 : getUsedSlotCount()));
|
||||
}
|
||||
|
||||
void nextFreeSlot(final Block block) {
|
||||
final boolean isFunctionBody = isFunctionBody();
|
||||
|
||||
final int nextFreeSlot;
|
||||
if (isFunctionBody) {
|
||||
// On entry to function, start with slot 0
|
||||
nextFreeSlot = 0;
|
||||
} else {
|
||||
// Otherwise, continue from previous block's first free slot
|
||||
nextFreeSlot = nextFreeSlots[nextFreeSlotsSize - 1];
|
||||
}
|
||||
private void pushFreeSlots(final int freeSlots) {
|
||||
if (nextFreeSlotsSize == nextFreeSlots.length) {
|
||||
final int[] newNextFreeSlots = new int[nextFreeSlotsSize * 2];
|
||||
System.arraycopy(nextFreeSlots, 0, newNextFreeSlots, 0, nextFreeSlotsSize);
|
||||
nextFreeSlots = newNextFreeSlots;
|
||||
}
|
||||
nextFreeSlots[nextFreeSlotsSize++] = assignSlots(block, nextFreeSlot);
|
||||
nextFreeSlots[nextFreeSlotsSize++] = freeSlots;
|
||||
}
|
||||
|
||||
private static int assignSlots(final Block block, final int firstSlot) {
|
||||
int nextSlot = firstSlot;
|
||||
int getUsedSlotCount() {
|
||||
return nextFreeSlots[nextFreeSlotsSize - 1];
|
||||
}
|
||||
|
||||
void releaseSlots() {
|
||||
--nextFreeSlotsSize;
|
||||
final int undefinedFromSlot = nextFreeSlotsSize == 0 ? 0 : nextFreeSlots[nextFreeSlotsSize - 1];
|
||||
if(!slotTypesDescriptors.isEmpty()) {
|
||||
slotTypesDescriptors.peek().setLength(undefinedFromSlot);
|
||||
}
|
||||
methodEmitters.peek().undefineLocalVariables(undefinedFromSlot, false);
|
||||
}
|
||||
|
||||
private int assignSlots(final Block block, final int firstSlot) {
|
||||
int fromSlot = firstSlot;
|
||||
final MethodEmitter method = methodEmitters.peek();
|
||||
for (final Symbol symbol : block.getSymbols()) {
|
||||
if (symbol.hasSlot()) {
|
||||
symbol.setSlot(nextSlot);
|
||||
nextSlot += symbol.slotCount();
|
||||
symbol.setFirstSlot(fromSlot);
|
||||
final int toSlot = fromSlot + symbol.slotCount();
|
||||
method.defineBlockLocalVariable(fromSlot, toSlot);
|
||||
fromSlot = toSlot;
|
||||
}
|
||||
}
|
||||
return nextSlot;
|
||||
return fromSlot;
|
||||
}
|
||||
|
||||
static Type getTypeForSlotDescriptor(final char typeDesc) {
|
||||
// Recognizing both lowercase and uppercase as we're using both to signify symbol boundaries; see
|
||||
// MethodEmitter.markSymbolBoundariesInLvarTypesDescriptor().
|
||||
switch(typeDesc) {
|
||||
case 'I':
|
||||
case 'i':
|
||||
return Type.INT;
|
||||
case 'J':
|
||||
case 'j':
|
||||
return Type.LONG;
|
||||
case 'D':
|
||||
case 'd':
|
||||
return Type.NUMBER;
|
||||
case 'A':
|
||||
case 'a':
|
||||
return Type.OBJECT;
|
||||
case 'U':
|
||||
case 'u':
|
||||
return Type.UNKNOWN;
|
||||
default:
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
void pushDiscard(final Node node) {
|
||||
@ -225,11 +280,8 @@ final class CodeGeneratorLexicalContext extends LexicalContext {
|
||||
return discard.peek();
|
||||
}
|
||||
|
||||
int quickSlot(final Symbol symbol) {
|
||||
final int quickSlot = nextFreeSlots[nextFreeSlotsSize - 1];
|
||||
nextFreeSlots[nextFreeSlotsSize - 1] = quickSlot + symbol.slotCount();
|
||||
return quickSlot;
|
||||
int quickSlot(final Type type) {
|
||||
return methodEmitters.peek().defineTemporaryLocalVariable(type.getSlots());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,10 +25,17 @@
|
||||
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import jdk.nashorn.internal.ir.FunctionNode;
|
||||
import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
|
||||
|
||||
/**
|
||||
* Used to track split class compilation.
|
||||
*/
|
||||
public class CompileUnit implements Comparable<CompileUnit> {
|
||||
public final class CompileUnit implements Comparable<CompileUnit> {
|
||||
/** Current class name */
|
||||
private final String className;
|
||||
|
||||
@ -39,14 +46,44 @@ public class CompileUnit implements Comparable<CompileUnit> {
|
||||
|
||||
private Class<?> clazz;
|
||||
|
||||
CompileUnit(final String className, final ClassEmitter classEmitter) {
|
||||
this(className, classEmitter, 0L);
|
||||
private Set<FunctionInitializer> functionInitializers = new LinkedHashSet<>();
|
||||
|
||||
private static class FunctionInitializer {
|
||||
final RecompilableScriptFunctionData data;
|
||||
final FunctionNode functionNode;
|
||||
|
||||
FunctionInitializer(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
|
||||
this.data = data;
|
||||
this.functionNode = functionNode;
|
||||
}
|
||||
|
||||
void initializeCode() {
|
||||
data.initializeCode(functionNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return data.hashCode() + 31 * functionNode.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj == null || obj.getClass() != FunctionInitializer.class) {
|
||||
return false;
|
||||
}
|
||||
final FunctionInitializer other = (FunctionInitializer)obj;
|
||||
return data == other.data && functionNode == other.functionNode;
|
||||
}
|
||||
}
|
||||
|
||||
CompileUnit(final String className, final ClassEmitter classEmitter, final long initialWeight) {
|
||||
this.className = className;
|
||||
this.classEmitter = classEmitter;
|
||||
this.weight = initialWeight;
|
||||
this.classEmitter = classEmitter;
|
||||
}
|
||||
|
||||
static Set<CompileUnit> createCompileUnitSet() {
|
||||
return new TreeSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -71,6 +108,29 @@ public class CompileUnit implements Comparable<CompileUnit> {
|
||||
this.classEmitter = null;
|
||||
}
|
||||
|
||||
void addFunctionInitializer(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
|
||||
functionInitializers.add(new FunctionInitializer(data, functionNode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this compile unit is responsible for initializing the specified function data with specified
|
||||
* function node.
|
||||
* @param data the function data to check
|
||||
* @param functionNode the function node to check
|
||||
* @return true if this unit is responsible for initializing the function data with the function node, otherwise
|
||||
* false
|
||||
*/
|
||||
public boolean isInitializing(final RecompilableScriptFunctionData data, final FunctionNode functionNode) {
|
||||
return functionInitializers.contains(new FunctionInitializer(data, functionNode));
|
||||
}
|
||||
|
||||
void initializeFunctionsCode() {
|
||||
for(final FunctionInitializer init : functionInitializers) {
|
||||
init.initializeCode();
|
||||
}
|
||||
functionInitializers = Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add weight to this compile unit
|
||||
* @param w weight to add
|
||||
@ -112,13 +172,17 @@ public class CompileUnit implements Comparable<CompileUnit> {
|
||||
return className;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[classname=" + className + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']';
|
||||
private static String shortName(final String name) {
|
||||
return name.lastIndexOf('/') == -1 ? name : name.substring(name.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CompileUnit o) {
|
||||
public String toString() {
|
||||
return "[CompileUnit className=" + shortName(className) + " weight=" + weight + '/' + Splitter.SPLIT_THRESHOLD + ']';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final CompileUnit o) {
|
||||
return className.compareTo(o.className);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -29,7 +29,11 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import jdk.internal.org.objectweb.asm.MethodVisitor;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
@ -41,7 +45,6 @@ import jdk.nashorn.internal.runtime.Source;
|
||||
*/
|
||||
|
||||
public enum CompilerConstants {
|
||||
|
||||
/** the __FILE__ variable */
|
||||
__FILE__,
|
||||
|
||||
@ -51,9 +54,6 @@ public enum CompilerConstants {
|
||||
/** the __LINE__ variable */
|
||||
__LINE__,
|
||||
|
||||
/** lazy prefix for classes of jitted methods */
|
||||
LAZY("Lazy"),
|
||||
|
||||
/** constructor name */
|
||||
INIT("<init>"),
|
||||
|
||||
@ -78,15 +78,18 @@ public enum CompilerConstants {
|
||||
/** function prefix for anonymous functions */
|
||||
ANON_FUNCTION_PREFIX("L:"),
|
||||
|
||||
/** method name for Java method that is script entry point */
|
||||
RUN_SCRIPT("runScript"),
|
||||
/** method name for Java method that is the program entry point */
|
||||
PROGRAM(":program"),
|
||||
|
||||
/** method name for Java method that creates the script function for the program */
|
||||
CREATE_PROGRAM_FUNCTION(":createProgramFunction"),
|
||||
|
||||
/**
|
||||
* "this" name symbol for a parameter representing ECMAScript "this" in static methods that are compiled
|
||||
* representations of ECMAScript functions. It is not assigned a slot, as its position in the method signature is
|
||||
* dependent on other factors (most notably, callee can precede it).
|
||||
*/
|
||||
THIS("this"),
|
||||
THIS("this", Object.class),
|
||||
|
||||
/** this debugger symbol */
|
||||
THIS_DEBUGGER(":this"),
|
||||
@ -110,13 +113,16 @@ public enum CompilerConstants {
|
||||
/** the internal arguments object, when necessary (not visible to scripts, can't be reassigned). */
|
||||
ARGUMENTS(":arguments", ScriptObject.class),
|
||||
|
||||
/** prefix for apply-to-call exploded arguments */
|
||||
EXPLODED_ARGUMENT_PREFIX(":xarg"),
|
||||
|
||||
/** prefix for iterators for for (x in ...) */
|
||||
ITERATOR_PREFIX(":i", Iterator.class),
|
||||
|
||||
/** prefix for tag variable used for switch evaluation */
|
||||
SWITCH_TAG_PREFIX(":s"),
|
||||
|
||||
/** prefix for all exceptions */
|
||||
/** prefix for JVM exceptions */
|
||||
EXCEPTION_PREFIX(":e", Throwable.class),
|
||||
|
||||
/** prefix for quick slots generated in Store */
|
||||
@ -161,7 +167,7 @@ public enum CompilerConstants {
|
||||
/** get map */
|
||||
GET_MAP(":getMap"),
|
||||
|
||||
/** get map */
|
||||
/** set map */
|
||||
SET_MAP(":setMap"),
|
||||
|
||||
/** get array prefix */
|
||||
@ -170,10 +176,22 @@ public enum CompilerConstants {
|
||||
/** get array suffix */
|
||||
GET_ARRAY_SUFFIX("$array");
|
||||
|
||||
/** To save memory - intern the compiler constant symbol names, as they are frequently reused */
|
||||
static {
|
||||
for (final CompilerConstants c : values()) {
|
||||
final String symbolName = c.symbolName();
|
||||
if (symbolName != null) {
|
||||
symbolName.intern();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<String> symbolNames;
|
||||
|
||||
/**
|
||||
* Prefix used for internal methods generated in script clases.
|
||||
*/
|
||||
public static final String INTERNAL_METHOD_PREFIX = ":";
|
||||
private static final String INTERNAL_METHOD_PREFIX = ":";
|
||||
|
||||
private final String symbolName;
|
||||
private final Class<?> type;
|
||||
@ -198,9 +216,28 @@ public enum CompilerConstants {
|
||||
}
|
||||
|
||||
private CompilerConstants(final String symbolName, final Class<?> type, final int slot) {
|
||||
this.symbolName = symbolName;
|
||||
this.type = type;
|
||||
this.slot = slot;
|
||||
this.symbolName = symbolName;
|
||||
this.type = type;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a name is that of a reserved compiler constnat
|
||||
* @param name name
|
||||
* @return true if compiler constant name
|
||||
*/
|
||||
public static boolean isCompilerConstant(final String name) {
|
||||
ensureSymbolNames();
|
||||
return symbolNames.contains(name);
|
||||
}
|
||||
|
||||
private static void ensureSymbolNames() {
|
||||
if(symbolNames == null) {
|
||||
symbolNames = new HashSet<>();
|
||||
for(final CompilerConstants cc: CompilerConstants.values()) {
|
||||
symbolNames.add(cc.symbolName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -327,9 +364,14 @@ public enum CompilerConstants {
|
||||
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) {
|
||||
MethodEmitter invoke(final MethodEmitter method) {
|
||||
return method.invokespecial(className, name, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(final MethodVisitor mv) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, className, name, desc, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -361,9 +403,14 @@ public enum CompilerConstants {
|
||||
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) {
|
||||
MethodEmitter invoke(final MethodEmitter method) {
|
||||
return method.invokestatic(className, name, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(final MethodVisitor mv) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, className, name, desc, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -396,9 +443,14 @@ public enum CompilerConstants {
|
||||
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) {
|
||||
MethodEmitter invoke(final MethodEmitter method) {
|
||||
return method.invokevirtual(className, name, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(final MethodVisitor mv) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, name, descriptor, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -416,9 +468,14 @@ public enum CompilerConstants {
|
||||
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) {
|
||||
MethodEmitter invoke(final MethodEmitter method) {
|
||||
return method.invokeinterface(className, name, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(final MethodVisitor mv) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, className, name, descriptor, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -512,9 +569,14 @@ public enum CompilerConstants {
|
||||
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) {
|
||||
MethodEmitter invoke(final MethodEmitter method) {
|
||||
return method.invokestatic(className, name, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(final MethodVisitor mv) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKESTATIC, className, name, descriptor, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -532,12 +594,55 @@ public enum CompilerConstants {
|
||||
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) {
|
||||
MethodEmitter invoke(final MethodEmitter method) {
|
||||
return method.invokevirtual(className, name, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(final MethodVisitor mv) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, name, descriptor, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a special call, given an explicit lookup, looking up the method handle for it at the same time.
|
||||
* clazz is used as this class
|
||||
*
|
||||
* @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 specialCall(final MethodHandles.Lookup lookup, final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
|
||||
return new Call(MH.findSpecial(lookup, clazz, name, MH.type(rtype, ptypes), clazz), className(clazz), name, methodDescriptor(rtype, ptypes)) {
|
||||
@Override
|
||||
MethodEmitter invoke(final MethodEmitter method) {
|
||||
return method.invokespecial(className, name, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(final MethodVisitor mv) {
|
||||
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, className, name, descriptor, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the passed string looks like a method name of an internally generated Nashorn method. Basically,
|
||||
* if it starts with a colon character {@code :} but is not the name of the program method {@code :program}.
|
||||
* Program function is not considered internal as we want it to show up in exception stack traces.
|
||||
* @param methodName the name of a method
|
||||
* @return true if it looks like an internal Nashorn method name.
|
||||
* @throws NullPointerException if passed null
|
||||
*/
|
||||
public static boolean isInternalMethodName(final String methodName) {
|
||||
return methodName.startsWith(INTERNAL_METHOD_PREFIX) && !methodName.equals(PROGRAM.symbolName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private class representing an access. This can generate code into a method code or
|
||||
* a field access.
|
||||
@ -668,7 +773,14 @@ public enum CompilerConstants {
|
||||
*
|
||||
* @return the method emitter
|
||||
*/
|
||||
protected abstract MethodEmitter invoke(final MethodEmitter emitter);
|
||||
abstract MethodEmitter invoke(final MethodEmitter emitter);
|
||||
|
||||
/**
|
||||
* Generate invocation code for the method
|
||||
*
|
||||
* @param mv a method visitor
|
||||
*/
|
||||
public abstract void invoke(final MethodVisitor mv);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -66,8 +66,7 @@ enum Condition {
|
||||
case GT:
|
||||
return IFGT;
|
||||
default:
|
||||
assert false;
|
||||
return -1;
|
||||
throw new UnsupportedOperationException("toUnary:" + c.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,8 +85,7 @@ enum Condition {
|
||||
case GT:
|
||||
return IF_ICMPGT;
|
||||
default:
|
||||
assert false;
|
||||
return -1;
|
||||
throw new UnsupportedOperationException("toBinary:" + c.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,20 +25,19 @@
|
||||
|
||||
package jdk.nashorn.internal.codegen;
|
||||
|
||||
import jdk.nashorn.internal.runtime.Property;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import jdk.nashorn.internal.runtime.Property;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
|
||||
/**
|
||||
* Manages constants needed by code generation. Objects are maintained in an
|
||||
* interning maps to remove duplicates.
|
||||
*/
|
||||
class ConstantData {
|
||||
final class ConstantData {
|
||||
/** Constant table. */
|
||||
final List<Object> constants;
|
||||
|
||||
@ -64,7 +63,7 @@ class ConstantData {
|
||||
private int calcHashCode() {
|
||||
final Class<?> cls = array.getClass();
|
||||
|
||||
if (cls == Object[].class) {
|
||||
if (!cls.getComponentType().isPrimitive()) {
|
||||
return Arrays.hashCode((Object[])array);
|
||||
} else if (cls == double[].class) {
|
||||
return Arrays.hashCode((double[])array);
|
||||
@ -92,7 +91,7 @@ class ConstantData {
|
||||
final Class<?> cls = array.getClass();
|
||||
|
||||
if (cls == otherArray.getClass()) {
|
||||
if (cls == Object[].class) {
|
||||
if (!cls.getComponentType().isPrimitive()) {
|
||||
return Arrays.equals((Object[])array, (Object[])otherArray);
|
||||
} else if (cls == double[].class) {
|
||||
return Arrays.equals((double[])array, (double[])otherArray);
|
||||
@ -185,6 +184,7 @@ class ConstantData {
|
||||
* @return the index in the constant pool that the object was given
|
||||
*/
|
||||
public int add(final Object object) {
|
||||
assert object != null;
|
||||
final Object entry;
|
||||
if (object.getClass().isArray()) {
|
||||
entry = new ArrayWrapper(object);
|
||||
|
120
nashorn/src/jdk/nashorn/internal/codegen/DumpBytecode.java
Normal file
120
nashorn/src/jdk/nashorn/internal/codegen/DumpBytecode.java
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2013, 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.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import jdk.nashorn.internal.runtime.ECMAErrors;
|
||||
import jdk.nashorn.internal.runtime.ScriptEnvironment;
|
||||
import jdk.nashorn.internal.runtime.logging.DebugLogger;
|
||||
|
||||
/**
|
||||
* Class that facilitates printing bytecode and dumping it to disk.
|
||||
*/
|
||||
public final class DumpBytecode {
|
||||
/**
|
||||
* Dump bytecode to console and potentially disk.
|
||||
* @param env the script environment defining options for printing bytecode
|
||||
* @param logger a logger used to write diagnostics about bytecode dumping
|
||||
* @param bytecode the actual code to dump
|
||||
* @param className the name of the class being dumped
|
||||
*/
|
||||
public static void dumpBytecode(final ScriptEnvironment env, final DebugLogger logger, final byte[] bytecode, final String className) {
|
||||
File dir = null;
|
||||
try {
|
||||
// should could be printed to stderr for generate class?
|
||||
if (env._print_code) {
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("class: " + className).
|
||||
append('\n').
|
||||
append(ClassEmitter.disassemble(bytecode)).
|
||||
append("=====");
|
||||
|
||||
if (env._print_code_dir != null) {
|
||||
|
||||
String name = className;
|
||||
final int dollar = name.lastIndexOf('$');
|
||||
if (dollar != -1) {
|
||||
name = name.substring(dollar + 1);
|
||||
}
|
||||
|
||||
dir = new File(env._print_code_dir);
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw new IOException(dir.toString());
|
||||
}
|
||||
|
||||
File file;
|
||||
String fileName;
|
||||
int uniqueId = 0;
|
||||
do {
|
||||
fileName = name + (uniqueId == 0 ? "" : "_" + uniqueId) + ".bytecode";
|
||||
file = new File(env._print_code_dir, fileName);
|
||||
uniqueId++;
|
||||
} while (file.exists());
|
||||
|
||||
try (final PrintWriter pw = new PrintWriter(new FileOutputStream(file))) {
|
||||
pw.print(sb.toString());
|
||||
pw.flush();
|
||||
}
|
||||
} else {
|
||||
env.getErr().println(sb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// should code be dumped to disk - only valid in compile_only mode?
|
||||
if (env._dest_dir != null && env._compile_only) {
|
||||
final String fileName = className.replace('.', File.separatorChar) + ".class";
|
||||
final int index = fileName.lastIndexOf(File.separatorChar);
|
||||
|
||||
if (index != -1) {
|
||||
dir = new File(env._dest_dir, fileName.substring(0, index));
|
||||
} else {
|
||||
dir = new File(env._dest_dir);
|
||||
}
|
||||
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw new IOException(dir.toString());
|
||||
}
|
||||
final File file = new File(env._dest_dir, fileName);
|
||||
try (final FileOutputStream fos = new FileOutputStream(file)) {
|
||||
fos.write(bytecode);
|
||||
}
|
||||
logger.info("Wrote class to '" + file.getAbsolutePath() + '\'');
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
logger.warning("Skipping class dump for ",
|
||||
className,
|
||||
": ",
|
||||
ECMAErrors.getMessage(
|
||||
"io.error.cant.write",
|
||||
dir.toString()));
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user