8035836: Array performance improvements
Implement typed arrays with native byte buffers and do fast linking for all continuous arrays Reviewed-by: attila, jlaskey, sundar, hannesw
This commit is contained in:
parent
e9e7dd2ec1
commit
8d4fc394e0
nashorn
bin
runnormal.shrunnormaldual.shrunopt.shrunoptdual.shrunoptdualcatch.shverbose_octane.batverbose_octane.sh
buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen
make
src/jdk
internal/dynalink
nashorn/internal
codegen
CodeGenerator.javaCompilationEnvironment.javaCompiler.javaCompilerConstants.javaLabel.javaMapCreator.javaNamespace.javaObjectClassGenerator.java
ir
lookup
objects
ArrayBufferView.javaGlobal.javaNativeArray.javaNativeArrayBuffer.javaNativeDebug.javaNativeError.javaNativeFloat32Array.javaNativeFloat64Array.javaNativeInt16Array.javaNativeInt32Array.javaNativeInt8Array.javaNativeMath.javaNativeNumber.javaNativeObject.javaNativeRegExp.javaNativeString.javaNativeUint16Array.javaNativeUint32Array.javaNativeUint8Array.javaNativeUint8ClampedArray.java
runtime
AccessorProperty.javaGlobalFunctions.javaNativeJavaPackage.javaProperty.javaPropertyHashMap.javaRecompilableScriptFunctionData.javaScriptFunction.javaScriptFunctionData.javaScriptLoader.javaScriptObject.javaSetMethodCreator.javaSpillProperty.javaWithObject.java
arrays
ArrayData.javaContinuousArray.javaContinuousArrayData.javaIntArrayData.javaLongArrayData.javaNumberArrayData.javaObjectArrayData.javaTypedArrayData.java
linker
resources
test
@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
FILENAME="./pessimistic_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||
$JAVA_HOME/bin/java -ea -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -Djava.ext.dirs=dist jdk.nashorn.tools.Shell ${@}
|
@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
FILENAME="./optimistic_dual_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||
$JAVA_HOME/bin/java -ea -Dnashorn.fields.dual -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -XX:TypeProfileLevel=222 -XX:+UnlockExperimentalVMOptions -XX:+UseTypeSpeculation -XX:+UseMathExactIntrinsics ${@}
|
@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
FILENAME="./optimistic_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||
$JAVA_HOME/bin/java -ea -Dnashorn.optimistic -Dnashorn.fastrewrite -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -XX:TypeProfileLevel=222 -XX:+UnlockExperimentalVMOptions -XX:+UseTypeSpeculation -XX:-UseMathExactIntrinsics -Xbootclasspath/p:dist/nashorn.jar jdk.nashorn.tools.Shell ${@}
|
@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
FILENAME="./optimistic_dual_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||
$JAVA_HOME/bin/java -ea -Dnashorn.fields.dual -Dnashorn.optimistic -Xms2G -Xmx2G -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$FILENAME,stackdepth=128 -XX:TypeProfileLevel=222 -XX:+UnlockExperimentalVMOptions -XX:+UseTypeSpeculation -XX:-UseMathExactIntrinsics ${@}
|
@ -2,15 +2,18 @@
|
||||
|
||||
#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_dual_catch$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||
FILENAME="./optimistic_dual_catch_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr"
|
||||
|
||||
DIR=/Users/marcus/src/tip/
|
||||
FAST_CATCH_COMBINATOR=$DIR/bin/fastCatchCombinator.jar
|
||||
NASHORN_JAR=$DIR/dist/nashorn.jar
|
||||
|
||||
$JAVA_HOME/bin/java \
|
||||
$FLAGS \
|
||||
-ea \
|
||||
-esa \
|
||||
$FLAGS \
|
||||
-Dnashorn.fastrewrite \
|
||||
-Dnashorn.optimistic \
|
||||
-Xbootclasspath/p:/Users/marcus/src/tip/dist/nashorn.jar \
|
||||
-Xbootclasspath/p:$FAST_CATCH_COMBINATOR:$NASHORN_JAR \
|
||||
-Xms2G -Xmx2G \
|
||||
-XX:+UnlockCommercialFeatures \
|
||||
-XX:+FlightRecorder \
|
||||
@ -23,3 +26,8 @@ $FLAGS \
|
||||
-cp $CLASSPATH:../build/test/classes/ \
|
||||
jdk.nashorn.tools.Shell ${@}
|
||||
|
||||
#-XX:+UnlockDiagnosticVMOptions \
|
||||
#-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"
|
@ -248,17 +248,17 @@ public final class MemberInfo implements Cloneable {
|
||||
if (kind == Kind.CONSTRUCTOR) {
|
||||
final Type returnType = Type.getReturnType(javaDesc);
|
||||
if (! returnType.toString().equals(OBJECT_DESC)) {
|
||||
error("return value should be of Object type, found" + returnType);
|
||||
error("return value should be of Object type, found " + returnType);
|
||||
}
|
||||
final Type[] argTypes = Type.getArgumentTypes(javaDesc);
|
||||
if (argTypes.length < 2) {
|
||||
error("constructor methods should have at least 2 args");
|
||||
}
|
||||
if (! argTypes[0].equals(Type.BOOLEAN_TYPE)) {
|
||||
error("first argument should be of boolean type, found" + argTypes[0]);
|
||||
error("first argument should be of boolean type, found " + argTypes[0]);
|
||||
}
|
||||
if (! argTypes[1].toString().equals(OBJECT_DESC)) {
|
||||
error("second argument should be of Object type, found" + argTypes[0]);
|
||||
error("second argument should be of Object type, found " + argTypes[0]);
|
||||
}
|
||||
|
||||
if (argTypes.length > 2) {
|
||||
@ -284,7 +284,7 @@ public final class MemberInfo implements Cloneable {
|
||||
error("function methods should have at least 1 arg");
|
||||
}
|
||||
if (! argTypes[0].toString().equals(OBJECT_DESC)) {
|
||||
error("first argument should be of Object type, found" + argTypes[0]);
|
||||
error("first argument should be of Object type, found " + argTypes[0]);
|
||||
}
|
||||
|
||||
if (argTypes.length > 1) {
|
||||
|
@ -400,6 +400,8 @@
|
||||
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"/>
|
||||
|
@ -88,6 +88,7 @@ import java.lang.invoke.MethodHandles;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.support.AbstractRelinkableCallSite;
|
||||
import jdk.internal.dynalink.support.Lookup;
|
||||
@ -103,8 +104,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<>();
|
||||
|
||||
@ -127,23 +147,25 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
|
||||
|
||||
@Override
|
||||
public void relink(GuardedInvocation guardedInvocation, MethodHandle fallback) {
|
||||
relinkInternal(guardedInvocation, fallback, false);
|
||||
relinkInternal(guardedInvocation, fallback, false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAndRelink(GuardedInvocation guardedInvocation, MethodHandle fallback) {
|
||||
relinkInternal(guardedInvocation, fallback, true);
|
||||
relinkInternal(guardedInvocation, fallback, true, false);
|
||||
}
|
||||
|
||||
private MethodHandle relinkInternal(GuardedInvocation invocation, MethodHandle relink, boolean reset) {
|
||||
private MethodHandle relinkInternal(GuardedInvocation invocation, MethodHandle relink, boolean reset, 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.
|
||||
// First, prune the chain of invalidated switchpoints, we always do this
|
||||
// We also remove any catches if the remove catches flag is set
|
||||
for(Iterator<GuardedInvocation> it = newInvocations.iterator(); it.hasNext();) {
|
||||
if(it.next().hasBeenInvalidated()) {
|
||||
final GuardedInvocation inv = it.next();
|
||||
if(inv.hasBeenInvalidated() || (removeCatches && inv.getException() != null)) {
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
@ -160,12 +182,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);
|
||||
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 +200,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(MethodHandle relink, 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 +232,7 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private MethodHandle prune(MethodHandle relink) {
|
||||
return relinkInternal(null, relink, false);
|
||||
private MethodHandle prune(MethodHandle relink, final boolean catches) {
|
||||
return relinkInternal(null, relink, false, catches);
|
||||
}
|
||||
}
|
||||
|
@ -227,8 +227,8 @@ public class DynamicLinker {
|
||||
final boolean callSiteUnstable = unstableDetectionEnabled && relinkCount >= unstableRelinkThreshold;
|
||||
final LinkRequest linkRequest =
|
||||
runtimeContextArgCount == 0 ?
|
||||
new LinkRequestImpl(callSiteDescriptor, callSite, callSiteUnstable, arguments) :
|
||||
new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSite, callSiteUnstable, arguments, runtimeContextArgCount);
|
||||
new LinkRequestImpl(callSiteDescriptor, callSite, relinkCount, callSiteUnstable, arguments) :
|
||||
new RuntimeContextLinkRequestImpl(callSiteDescriptor, callSite, relinkCount, callSiteUnstable, arguments, runtimeContextArgCount);
|
||||
|
||||
GuardedInvocation guardedInvocation = linkerServices.getGuardedInvocation(linkRequest);
|
||||
|
||||
|
@ -89,6 +89,7 @@ import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.SwitchPoint;
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.support.CatchExceptionCombinator;
|
||||
import jdk.internal.dynalink.support.Guards;
|
||||
@ -344,21 +345,44 @@ public class GuardedInvocation {
|
||||
* @return a composite method handle.
|
||||
*/
|
||||
public MethodHandle compose(MethodHandle fallback) {
|
||||
return compose(fallback, 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(MethodHandle guardFallback, MethodHandle switchpointFallback, MethodHandle catchFallback) {
|
||||
final MethodHandle guarded =
|
||||
guard == null ? invocation : MethodHandles.guardWithTest(guard, invocation, guardFallback);
|
||||
final MethodHandle catchGuarded = exception == null ? guarded : catchException(guarded, exception,
|
||||
MethodHandles.dropArguments(guardFallback, 0, exception));
|
||||
return switchPoint == null ? catchGuarded : switchPoint.guardWithTest(catchGuarded, switchpointFallback);
|
||||
guard == null ?
|
||||
invocation :
|
||||
MethodHandles.guardWithTest(
|
||||
guard,
|
||||
invocation,
|
||||
guardFallback);
|
||||
|
||||
final MethodHandle catchGuarded =
|
||||
exception == null ?
|
||||
guarded :
|
||||
catchException(
|
||||
guarded,
|
||||
exception,
|
||||
MethodHandles.dropArguments(
|
||||
catchFallback,
|
||||
0,
|
||||
exception));
|
||||
|
||||
final MethodHandle spGuarded =
|
||||
switchPoint == null ?
|
||||
catchGuarded :
|
||||
switchPoint.guardWithTest(
|
||||
catchGuarded,
|
||||
switchpointFallback);
|
||||
|
||||
return spGuarded;
|
||||
}
|
||||
|
||||
private static MethodHandle catchException(final MethodHandle target, final Class<? extends Throwable> exType, final MethodHandle handler) {
|
||||
@ -367,6 +391,7 @@ public class GuardedInvocation {
|
||||
}
|
||||
return MethodHandles.catchException(target, exType, handler);
|
||||
}
|
||||
|
||||
private static void assertType(MethodHandle mh, 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;
|
||||
}
|
||||
|
@ -126,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
|
||||
|
@ -98,18 +98,21 @@ public class LinkRequestImpl implements LinkRequest {
|
||||
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, Object callSiteToken, boolean callSiteUnstable, Object... arguments) {
|
||||
public LinkRequestImpl(CallSiteDescriptor callSiteDescriptor, Object callSiteToken, int linkCount, boolean callSiteUnstable, Object... arguments) {
|
||||
this.callSiteDescriptor = callSiteDescriptor;
|
||||
this.callSiteToken = callSiteToken;
|
||||
this.linkCount = linkCount;
|
||||
this.callSiteUnstable = callSiteUnstable;
|
||||
this.arguments = arguments;
|
||||
}
|
||||
@ -139,6 +142,11 @@ public class LinkRequestImpl implements LinkRequest {
|
||||
return callSiteUnstable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLinkCount() {
|
||||
return linkCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkRequest withoutRuntimeContext() {
|
||||
return this;
|
||||
@ -146,6 +154,6 @@ public class LinkRequestImpl implements LinkRequest {
|
||||
|
||||
@Override
|
||||
public LinkRequest replaceArguments(CallSiteDescriptor newCallSiteDescriptor, Object[] newArguments) {
|
||||
return new LinkRequestImpl(newCallSiteDescriptor, callSiteToken, callSiteUnstable, newArguments);
|
||||
return new LinkRequestImpl(newCallSiteDescriptor, callSiteToken, linkCount, callSiteUnstable, newArguments);
|
||||
}
|
||||
}
|
||||
|
@ -103,14 +103,15 @@ public class RuntimeContextLinkRequestImpl extends LinkRequestImpl {
|
||||
* @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, Object callSiteToken,
|
||||
boolean callSiteUnstable, Object[] arguments, int runtimeContextArgCount) {
|
||||
super(callSiteDescriptor, callSiteToken, callSiteUnstable, arguments);
|
||||
int linkCount, boolean callSiteUnstable, Object[] arguments, int runtimeContextArgCount) {
|
||||
super(callSiteDescriptor, callSiteToken, linkCount, callSiteUnstable, arguments);
|
||||
if(runtimeContextArgCount < 1) {
|
||||
throw new IllegalArgumentException("runtimeContextArgCount < 1");
|
||||
}
|
||||
@ -122,14 +123,14 @@ public class RuntimeContextLinkRequestImpl extends LinkRequestImpl {
|
||||
if(contextStrippedRequest == null) {
|
||||
contextStrippedRequest =
|
||||
new LinkRequestImpl(CallSiteDescriptorFactory.dropParameterTypes(getCallSiteDescriptor(), 1,
|
||||
runtimeContextArgCount + 1), getCallSiteToken(), isCallSiteUnstable(), getTruncatedArguments());
|
||||
runtimeContextArgCount + 1), getCallSiteToken(), getLinkCount(), isCallSiteUnstable(), getTruncatedArguments());
|
||||
}
|
||||
return contextStrippedRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkRequest replaceArguments(CallSiteDescriptor callSiteDescriptor, Object[] arguments) {
|
||||
return new RuntimeContextLinkRequestImpl(callSiteDescriptor, getCallSiteToken(), isCallSiteUnstable(), arguments,
|
||||
return new RuntimeContextLinkRequestImpl(callSiteDescriptor, getCallSiteToken(), getLinkCount(), isCallSiteUnstable(), arguments,
|
||||
runtimeContextArgCount);
|
||||
}
|
||||
|
||||
|
@ -91,6 +91,7 @@ import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.internal.dynalink.linker.ConversionComparator;
|
||||
import jdk.internal.dynalink.linker.ConversionComparator.Comparison;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
@ -372,6 +373,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;
|
||||
|
||||
|
@ -394,7 +394,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
previousWasBlock = true;
|
||||
} else {
|
||||
if ((node instanceof WithNode && previousWasBlock) || (node instanceof FunctionNode && CodeGeneratorLexicalContext.isFunctionDynamicScope((FunctionNode)node))) {
|
||||
if (node instanceof WithNode && previousWasBlock || node instanceof FunctionNode && CodeGeneratorLexicalContext.isFunctionDynamicScope((FunctionNode)node)) {
|
||||
// If we hit a scope that can have symbols introduced into it at run time before finding the defining
|
||||
// block, the symbol can't be fast scoped. A WithNode only counts if we've immediately seen a block
|
||||
// before - its block. Otherwise, we are currently processing the WithNode's expression, and that's
|
||||
@ -963,7 +963,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
if (callNode.isEval()) {
|
||||
evalCall(node, flags);
|
||||
} else if (useCount <= SharedScopeCall.FAST_SCOPE_CALL_THRESHOLD
|
||||
|| (!isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD)
|
||||
|| !isFastScope(symbol) && useCount <= SharedScopeCall.SLOW_SCOPE_CALL_THRESHOLD
|
||||
|| CodeGenerator.this.lc.inDynamicScope()
|
||||
|| isOptimisticOrRestOf()) {
|
||||
scopeCall(node, flags);
|
||||
@ -1186,7 +1186,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
* @return flags without optimism
|
||||
*/
|
||||
static int nonOptimisticFlags(int flags) {
|
||||
return flags & ~(CALLSITE_OPTIMISTIC | (-1 << CALLSITE_PROGRAM_POINT_SHIFT));
|
||||
return flags & ~(CALLSITE_OPTIMISTIC | -1 << CALLSITE_PROGRAM_POINT_SHIFT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1398,7 +1398,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
//this symbol will be put fielded, we can't initialize it as undefined with a known type
|
||||
@Override
|
||||
public Class<?> getValueType() {
|
||||
return (OBJECT_FIELDS_ONLY || value == null || value.getSymbolType().isBoolean()) ? Object.class : value.getSymbolType().getTypeClass();
|
||||
return OBJECT_FIELDS_ONLY || value == null || value.getSymbolType().isBoolean() ? Object.class : value.getSymbolType().getTypeClass();
|
||||
//return OBJECT_FIELDS_ONLY ? Object.class : symbol.getSymbolType().getTypeClass();
|
||||
}
|
||||
});
|
||||
@ -1482,7 +1482,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
// Nested functions are not visited when we either recompile or lazily compile, only the outermost function is.
|
||||
if (compileOutermostOnly() && (lc.getOutermostFunction() != functionNode)) {
|
||||
if (compileOutermostOnly() && lc.getOutermostFunction() != functionNode) {
|
||||
// In case we are not generating code for the function, we must create or retrieve the function object and
|
||||
// load it on the stack here.
|
||||
newFunctionObject(functionNode, false);
|
||||
@ -1790,7 +1790,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
} else if (value instanceof String) {
|
||||
final String string = (String)value;
|
||||
|
||||
if (string.length() > (MethodEmitter.LARGE_STRING_THRESHOLD / 3)) { // 3 == max bytes per encoded char
|
||||
if (string.length() > MethodEmitter.LARGE_STRING_THRESHOLD / 3) { // 3 == max bytes per encoded char
|
||||
loadConstant(string);
|
||||
} else {
|
||||
method.load(string);
|
||||
@ -1907,7 +1907,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
tuples.add(new MapTuple<Expression>(key, symbol, value) {
|
||||
@Override
|
||||
public Class<?> getValueType() {
|
||||
return (OBJECT_FIELDS_ONLY || value == null || value.getType().isBoolean()) ? Object.class : value.getType().getTypeClass();
|
||||
return OBJECT_FIELDS_ONLY || value == null || value.getType().isBoolean() ? Object.class : value.getType().getTypeClass();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -2397,7 +2397,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
final Label entry = caseNode.getEntry();
|
||||
|
||||
// Take first duplicate.
|
||||
if (!(tree.containsKey(value))) {
|
||||
if (!tree.containsKey(value)) {
|
||||
tree.put(value, entry);
|
||||
}
|
||||
}
|
||||
@ -2435,7 +2435,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
// If reasonable size and not too sparse (80%), use table otherwise use lookup.
|
||||
if (range > 0 && range < 4096 && range <= (size * 5 / 4)) {
|
||||
if (range > 0 && range < 4096 && range <= size * 5 / 4) {
|
||||
final Label[] table = new Label[range];
|
||||
Arrays.fill(table, defaultLabel);
|
||||
for (int i = 0; i < size; i++) {
|
||||
@ -2650,7 +2650,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
if (needsScope) {
|
||||
load(init);
|
||||
int flags = CALLSITE_SCOPE | getCallSiteFlags();
|
||||
final int flags = CALLSITE_SCOPE | getCallSiteFlags();
|
||||
if (isFastScope(identSymbol)) {
|
||||
storeFastScopeVar(identSymbol, flags);
|
||||
} else {
|
||||
@ -2984,7 +2984,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
new Store<BinaryNode>(binaryNode, lhs) {
|
||||
@Override
|
||||
protected void evaluate() {
|
||||
if ((lhs instanceof IdentNode) && !lhs.getSymbol().isScope()) {
|
||||
if (lhs instanceof IdentNode && !lhs.getSymbol().isScope()) {
|
||||
load(rhs, lhsType);
|
||||
} else {
|
||||
load(rhs);
|
||||
@ -4064,10 +4064,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
// There are at least as many as are declared by the current blocks.
|
||||
int usedSlots = lc.getUsedSlotCount();
|
||||
// Look at every load on the stack, and bump the number of used slots up by the temporaries seen there.
|
||||
for(int i = 0; i < localLoadsOnStack.length; ++i) {
|
||||
final int slot = localLoadsOnStack[i];
|
||||
for (final int slot : localLoadsOnStack) {
|
||||
if(slot != Label.Stack.NON_LOAD) {
|
||||
int afterSlot = slot + localVariableTypes.get(slot).getSlots();
|
||||
final int afterSlot = slot + localVariableTypes.get(slot).getSlots();
|
||||
if(afterSlot > usedSlots) {
|
||||
usedSlots = afterSlot;
|
||||
}
|
||||
@ -4085,8 +4084,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
private static boolean everyLocalLoadIsValid(final int[] loads, int localCount) {
|
||||
for(int i = 0; i < loads.length; ++i) {
|
||||
if(loads[i] < 0 || loads[i] >= localCount) {
|
||||
for (final int load : loads) {
|
||||
if(load < 0 || load >= localCount) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -4106,8 +4105,8 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
}
|
||||
|
||||
private static boolean everyStackValueIsLocalLoad(final int[] loads) {
|
||||
for(int i = 0; i < loads.length; ++i) {
|
||||
if(loads[i] == Label.Stack.NON_LOAD) {
|
||||
for (final int load : loads) {
|
||||
if(load == Label.Stack.NON_LOAD) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -4330,7 +4329,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder b = new StringBuilder(64).append("[HandlerSpec ").append(lvarSpec);
|
||||
final StringBuilder b = new StringBuilder(64).append("[HandlerSpec ").append(lvarSpec);
|
||||
if(catchTarget) {
|
||||
b.append(", catchTarget");
|
||||
}
|
||||
|
@ -338,6 +338,7 @@ public final class CompilationEnvironment {
|
||||
* @return most optimistic type in current environment
|
||||
*/
|
||||
Type getOptimisticType(final Optimistic node) {
|
||||
|
||||
assert useOptimisticTypes();
|
||||
final Type invalidType = invalidatedProgramPoints.get(node.getProgramPoint());
|
||||
if (invalidType != null) {
|
||||
|
@ -136,11 +136,10 @@ public final class Compiler {
|
||||
compilationEnv.setIsStrict(true);
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append(functionNode.uniqueName(className)).
|
||||
append('$').
|
||||
append(safeSourceName(functionNode.getSource()));
|
||||
this.scriptName = sb.toString();
|
||||
final String name = className + '$' + safeSourceName(functionNode.getSource());
|
||||
final String uniqueName = functionNode.uniqueName(name);
|
||||
|
||||
this.scriptName = uniqueName;
|
||||
}
|
||||
|
||||
private Compiler(final CompilationEnvironment compilationEnv, final ScriptEnvironment scriptEnv, final CodeInstaller<ScriptEnvironment> installer) {
|
||||
@ -437,11 +436,11 @@ public final class Compiler {
|
||||
}
|
||||
|
||||
baseName = baseName.replace('.', '_').replace('-', '_');
|
||||
if (! scriptEnv._loader_per_compile) {
|
||||
if (!scriptEnv._loader_per_compile) {
|
||||
baseName = baseName + installer.getUniqueScriptId();
|
||||
}
|
||||
final String mangled = NameCodec.encode(baseName);
|
||||
|
||||
final String mangled = NameCodec.encode(baseName);
|
||||
return mangled != null ? mangled : baseName;
|
||||
}
|
||||
|
||||
|
@ -552,6 +552,48 @@ public enum CompilerConstants {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a special call, given an explicit lookup, looking up the method handle for it at the same time
|
||||
*
|
||||
* @param lookup the lookup
|
||||
* @param thisClass this class
|
||||
* @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 specialCall0(final MethodHandles.Lookup lookup, final Class<?> thisClass, final Class<?> clazz, final String name, final Class<?> rtype, final Class<?>... ptypes) {
|
||||
return new Call(MH.findSpecial(lookup, clazz, name, MH.type(rtype, ptypes), thisClass), className(clazz), name, methodDescriptor(rtype, ptypes)) {
|
||||
@Override
|
||||
public MethodEmitter invoke(final MethodEmitter method) {
|
||||
return method.invokespecial(className, name, descriptor);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
public MethodEmitter invoke(final MethodEmitter method) {
|
||||
return method.invokespecial(className, name, descriptor);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
|
@ -40,9 +40,9 @@ public final class Label {
|
||||
static final class Stack {
|
||||
static final int NON_LOAD = -1;
|
||||
|
||||
Type[] data = new Type[8];
|
||||
Type[] data = new Type[8];
|
||||
int[] localLoads = new int[8];
|
||||
int sp = 0;
|
||||
int sp;
|
||||
|
||||
Stack() {
|
||||
}
|
||||
|
@ -77,8 +77,16 @@ public class MapCreator<T> {
|
||||
final String key = tuple.key;
|
||||
final Symbol symbol = tuple.symbol;
|
||||
final Class<?> initialType = tuple.getValueType();
|
||||
|
||||
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
|
||||
properties.add(new AccessorProperty(key, getPropertyFlags(symbol, hasArguments), structure, symbol.getFieldIndex(), initialType));
|
||||
final int flags = getPropertyFlags(symbol, hasArguments);
|
||||
final Property property = new AccessorProperty(
|
||||
key,
|
||||
flags,
|
||||
structure,
|
||||
symbol.getFieldIndex(),
|
||||
initialType);
|
||||
properties.add(property);
|
||||
}
|
||||
}
|
||||
|
||||
@ -94,8 +102,14 @@ public class MapCreator<T> {
|
||||
final String key = tuple.key;
|
||||
final Symbol symbol = tuple.symbol;
|
||||
|
||||
//TODO initial type is object here no matter what. Is that right?
|
||||
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
|
||||
properties.add(new SpillProperty(key, getPropertyFlags(symbol, hasArguments), spillIndex++));
|
||||
final int flags = getPropertyFlags(symbol, hasArguments);
|
||||
properties.add(
|
||||
new SpillProperty(
|
||||
key,
|
||||
flags,
|
||||
spillIndex++));
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,11 +124,11 @@ public class MapCreator<T> {
|
||||
*
|
||||
* @return flags to use for fields
|
||||
*/
|
||||
protected int getPropertyFlags(final Symbol symbol, final boolean hasArguments) {
|
||||
static int getPropertyFlags(final Symbol symbol, final boolean hasArguments) {
|
||||
int flags = 0;
|
||||
|
||||
if (symbol.isParam()) {
|
||||
flags |= Property.IS_ALWAYS_OBJECT | Property.IS_PARAMETER;
|
||||
flags |= Property.IS_PARAMETER;
|
||||
}
|
||||
|
||||
if (hasArguments) {
|
||||
|
@ -80,7 +80,6 @@ public class Namespace {
|
||||
if (counter != null) {
|
||||
final int count = counter + 1;
|
||||
namespaceDirectory.put(base, count);
|
||||
|
||||
return base + '-' + count;
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ public final class ObjectClassGenerator {
|
||||
* @param clazz the JavaScript scope class.
|
||||
* @return the number of fields in the scope class.
|
||||
*/
|
||||
public static int getFieldCount(Class<?> clazz) {
|
||||
public static int getFieldCount(final Class<?> clazz) {
|
||||
final String name = clazz.getSimpleName();
|
||||
final String prefix = JS_OBJECT_PREFIX.symbolName();
|
||||
if (prefix.equals(name)) {
|
||||
@ -247,13 +247,16 @@ public final class ObjectClassGenerator {
|
||||
* @param fieldNames fields to initialize to undefined, where applicable
|
||||
*/
|
||||
private static void initializeToUndefined(final MethodEmitter init, final String className, final List<String> fieldNames) {
|
||||
if (!OBJECT_FIELDS_ONLY) {
|
||||
// no need to initialize anything to undefined in the dual field world
|
||||
// - then we have a constant getter for undefined for any unknown type
|
||||
return;
|
||||
}
|
||||
|
||||
if (fieldNames.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// always initialize fields to undefined, even with --dual-fields. Then it's ok to
|
||||
// remember things like "widest set type" in properties, and if it's object, don't
|
||||
// add any special "return undefined" getters, saving an invalidation
|
||||
init.load(Type.OBJECT, JAVA_THIS.slot());
|
||||
init.loadUndefined(Type.OBJECT);
|
||||
|
||||
@ -565,7 +568,7 @@ public final class ObjectClassGenerator {
|
||||
final boolean isPrimitiveStorage = forType != null && forType.isPrimitive();
|
||||
|
||||
//which is the primordial getter
|
||||
final MethodHandle getter = OBJECT_FIELDS_ONLY ? objectGetter : (isPrimitiveStorage ? primitiveGetter : objectGetter);
|
||||
final MethodHandle getter = OBJECT_FIELDS_ONLY ? objectGetter : isPrimitiveStorage ? primitiveGetter : objectGetter;
|
||||
|
||||
if (forType == null) {
|
||||
if (isOptimistic) {
|
||||
|
@ -25,9 +25,6 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
@ -120,9 +117,6 @@ public final class AccessNode extends BaseNode {
|
||||
if (this.optimisticType == optimisticType) {
|
||||
return this;
|
||||
}
|
||||
if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), optimisticType)) {
|
||||
ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", optimisticType, " instead of ", getType());
|
||||
}
|
||||
return new AccessNode(this, base, property, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||
}
|
||||
|
||||
|
@ -436,7 +436,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
|
||||
return namespace.uniqueName(base);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void toString(final StringBuilder sb) {
|
||||
sb.append('[').
|
||||
|
@ -147,7 +147,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
||||
* @return true if this is a property name
|
||||
*/
|
||||
public boolean isPropertyName() {
|
||||
return (flags & PROPERTY_NAME) != 0;
|
||||
return (flags & PROPERTY_NAME) == PROPERTY_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -166,7 +166,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
||||
* @return true if this is a future strict name
|
||||
*/
|
||||
public boolean isFutureStrictName() {
|
||||
return (flags & FUTURESTRICT_NAME) != 0;
|
||||
return (flags & FUTURESTRICT_NAME) == FUTURESTRICT_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -185,7 +185,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
||||
* @return true if IdentNode is initialized on creation
|
||||
*/
|
||||
public boolean isInitializedHere() {
|
||||
return (flags & INITIALIZED_HERE) != 0;
|
||||
return (flags & INITIALIZED_HERE) == INITIALIZED_HERE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -211,7 +211,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
||||
|
||||
@Override
|
||||
public boolean isFunction() {
|
||||
return (flags & FUNCTION) != 0;
|
||||
return (flags & FUNCTION) == FUNCTION;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -219,7 +219,7 @@ public final class IdentNode extends Expression implements PropertyKey, Function
|
||||
if (this.optimisticType == callSiteType) {
|
||||
return this;
|
||||
}
|
||||
if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), callSiteType)) {
|
||||
if (DEBUG_FIELDS && ObjectClassGenerator.shouldInstrument(getName()) && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), callSiteType)) {
|
||||
ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", callSiteType, " instead of ", getType());
|
||||
}
|
||||
return new IdentNode(this, name, callSiteType, flags, programPoint);
|
||||
|
@ -25,9 +25,6 @@
|
||||
|
||||
package jdk.nashorn.internal.ir;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.DEBUG_FIELDS;
|
||||
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.ir.annotations.Immutable;
|
||||
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
|
||||
@ -120,9 +117,6 @@ public final class IndexNode extends BaseNode {
|
||||
if (this.optimisticType == optimisticType) {
|
||||
return this;
|
||||
}
|
||||
if (DEBUG_FIELDS && getSymbol() != null && !Type.areEquivalent(getSymbol().getSymbolType(), optimisticType)) {
|
||||
ObjectClassGenerator.LOG.info(getClass().getName(), " ", this, " => ", optimisticType, " instead of ", getType());
|
||||
}
|
||||
return new IndexNode(this, base, index, isFunction(), optimisticType, isOptimistic, programPoint);
|
||||
}
|
||||
|
||||
|
@ -128,6 +128,50 @@ public final class Lookup {
|
||||
throw typeError("strict.getter.setter.poison", ScriptRuntime.safeToString(self));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method filters primitive argument types using JavaScript semantics. For example,
|
||||
* an (int) cast of a double in Java land is not the same thing as invoking toInt32 on it.
|
||||
* If you are returning values to JavaScript that have to be of a specific type, this is
|
||||
* the correct return value filter to use, as the explicitCastArguments just uses the
|
||||
* Java boxing equivalents
|
||||
*
|
||||
* @param mh method handle for which to filter argument value
|
||||
* @param n argument index
|
||||
* @param from old argument type, the new one is given by the sent method handle
|
||||
* @return method handle for appropriate argument type conversion
|
||||
*/
|
||||
public static MethodHandle filterArgumentType(final MethodHandle mh, final int n, final Class<?> from) {
|
||||
final Class<?> to = mh.type().parameterType(n);
|
||||
|
||||
if (from == int.class) {
|
||||
//fallthru
|
||||
} else if (from == long.class) {
|
||||
//fallthru
|
||||
} else if (from == double.class) {
|
||||
if (to == int.class) {
|
||||
return MH.filterArguments(mh, n, JSType.TO_INT32_D.methodHandle());
|
||||
} else if (to == long.class) {
|
||||
return MH.filterArguments(mh, n, JSType.TO_UINT32_D.methodHandle());
|
||||
}
|
||||
//fallthru
|
||||
} else if (!from.isPrimitive()) {
|
||||
if (to == int.class) {
|
||||
return MH.filterArguments(mh, n, JSType.TO_INT32.methodHandle());
|
||||
} else if (to == long.class) {
|
||||
return MH.filterArguments(mh, n, JSType.TO_UINT32.methodHandle());
|
||||
} else if (to == double.class) {
|
||||
return MH.filterArguments(mh, n, JSType.TO_NUMBER.methodHandle());
|
||||
} else if (!to.isPrimitive()) {
|
||||
return mh;
|
||||
}
|
||||
|
||||
assert false : "unsupported Lookup.filterReturnType type " + from + " -> " + to;
|
||||
}
|
||||
|
||||
//use a standard cast - we don't need to check JavaScript special cases
|
||||
return MH.explicitCastArguments(mh, mh.type().changeParameterType(2, to));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method filters primitive return types using JavaScript semantics. For example,
|
||||
* an (int) cast of a double in Java land is not the same thing as invoking toInt32 on it.
|
||||
|
@ -142,7 +142,7 @@ public final class MethodHandleFactory {
|
||||
final String str = "\treturn" +
|
||||
(VOID_TAG.equals(value) ?
|
||||
";" :
|
||||
(" " + stripName(value) + "; // [type=" + (value == null ? "null" : stripName(value.getClass()) + ']')));
|
||||
" " + stripName(value) + "; // [type=" + (value == null ? "null" : stripName(value.getClass()) + ']'));
|
||||
logger.log(TRACE_LEVEL, str);
|
||||
logger.log(TRACE_LEVEL, Debug.firstJSFrame());
|
||||
return value;
|
||||
@ -214,7 +214,7 @@ public final class MethodHandleFactory {
|
||||
|
||||
if (arg instanceof ScriptObject) {
|
||||
return arg.toString() +
|
||||
" (map=" + Debug.id((((ScriptObject)arg).getMap())) +
|
||||
" (map=" + Debug.id(((ScriptObject)arg).getMap()) +
|
||||
")";
|
||||
}
|
||||
|
||||
@ -429,6 +429,15 @@ public final class MethodHandleFactory {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle findSpecial(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type, final Class<?> thisClass) {
|
||||
try {
|
||||
return explicitLookup.findSpecial(clazz, name, type, thisClass);
|
||||
} catch (final NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new LookupException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle findVirtual(final MethodHandles.Lookup explicitLookup, final Class<?> clazz, final String name, final MethodType type) {
|
||||
try {
|
||||
|
@ -296,6 +296,19 @@ public interface MethodHandleFunctionality {
|
||||
*/
|
||||
public MethodHandle findVirtual(MethodHandles.Lookup explicitLookup, Class<?> clazz, String name, MethodType type);
|
||||
|
||||
/**
|
||||
* Wrapper for {@link java.lang.invoke.MethodHandles.Lookup#findSpecial(Class, String, MethodType, Class)}
|
||||
*
|
||||
* @param explicitLookup explicit lookup to be used
|
||||
* @param clazz class to look in
|
||||
* @param name name of method
|
||||
* @param type method type
|
||||
* @param thisClass thisClass
|
||||
*
|
||||
* @return method handle for virtual method
|
||||
*/
|
||||
public MethodHandle findSpecial(MethodHandles.Lookup explicitLookup, Class<?> clazz, String name, MethodType type, final Class<?> thisClass);
|
||||
|
||||
/**
|
||||
* Wrapper for SwitchPoint creation. Just like {@code new SwitchPoint()} but potentially
|
||||
* tracked
|
||||
|
@ -26,8 +26,15 @@
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Getter;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
@ -36,9 +43,12 @@ import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
|
||||
|
||||
@ScriptClass("ArrayBufferView")
|
||||
abstract class ArrayBufferView extends ScriptObject {
|
||||
private final NativeArrayBuffer buffer;
|
||||
private final int byteOffset;
|
||||
|
||||
// initialized by nasgen
|
||||
private static PropertyMap $nasgenmap$;
|
||||
@ -49,24 +59,34 @@ abstract class ArrayBufferView extends ScriptObject {
|
||||
|
||||
private ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength, final Global global) {
|
||||
super(global.getArrayBufferViewMap());
|
||||
checkConstructorArgs(buffer, byteOffset, elementLength);
|
||||
this.setProto(getPrototype(global));
|
||||
this.setArray(factory().createArrayData(buffer, byteOffset, elementLength));
|
||||
|
||||
final int bytesPerElement = bytesPerElement();
|
||||
|
||||
checkConstructorArgs(buffer.getByteLength(), bytesPerElement, byteOffset, elementLength);
|
||||
setProto(getPrototype(global));
|
||||
|
||||
this.buffer = buffer;
|
||||
this.byteOffset = byteOffset;
|
||||
|
||||
assert byteOffset % bytesPerElement == 0;
|
||||
final int start = byteOffset / bytesPerElement;
|
||||
final ByteBuffer newNioBuffer = buffer.getNioBuffer().duplicate().order(ByteOrder.nativeOrder());
|
||||
final ArrayData data = factory().createArrayData(newNioBuffer, start, start + elementLength);
|
||||
|
||||
setArray(data);
|
||||
}
|
||||
|
||||
ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
protected ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
this(buffer, byteOffset, elementLength, Global.instance());
|
||||
}
|
||||
|
||||
private void checkConstructorArgs(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
private static void checkConstructorArgs(final int byteLength, final int bytesPerElement, final int byteOffset, final int elementLength) {
|
||||
if (byteOffset < 0 || elementLength < 0) {
|
||||
throw new RuntimeException("byteOffset or length must not be negative");
|
||||
}
|
||||
if (byteOffset + elementLength * bytesPerElement() > buffer.getByteLength()) {
|
||||
throw new RuntimeException("byteOffset + byteLength out of range");
|
||||
}
|
||||
if (byteOffset % bytesPerElement() != 0) {
|
||||
throw new RuntimeException("byteOffset must be a multiple of the element size");
|
||||
throw new RuntimeException("byteOffset or length must not be negative, byteOffset=" + byteOffset + ", elementLength=" + elementLength + ", bytesPerElement=" + bytesPerElement);
|
||||
} else if (byteOffset + elementLength * bytesPerElement > byteLength) {
|
||||
throw new RuntimeException("byteOffset + byteLength out of range, byteOffset=" + byteOffset + ", elementLength=" + elementLength + ", bytesPerElement=" + bytesPerElement);
|
||||
} else if (byteOffset % bytesPerElement != 0) {
|
||||
throw new RuntimeException("byteOffset must be a multiple of the element size, byteOffset=" + byteOffset + " bytesPerElement=" + bytesPerElement);
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,22 +96,22 @@ abstract class ArrayBufferView extends ScriptObject {
|
||||
|
||||
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
|
||||
public static Object buffer(final Object self) {
|
||||
return ((ArrayDataImpl)((ArrayBufferView)self).getArray()).buffer;
|
||||
return ((ArrayBufferView)self).buffer;
|
||||
}
|
||||
|
||||
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
|
||||
public static Object byteOffset(final Object self) {
|
||||
return ((ArrayDataImpl)((ArrayBufferView)self).getArray()).byteOffset;
|
||||
return ((ArrayBufferView)self).byteOffset;
|
||||
}
|
||||
|
||||
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
|
||||
public static Object byteLength(final Object self) {
|
||||
public static int byteLength(final Object self) {
|
||||
final ArrayBufferView view = (ArrayBufferView)self;
|
||||
return ((ArrayDataImpl)view.getArray()).elementLength * view.bytesPerElement();
|
||||
return ((TypedArrayData<?>)view.getArray()).getElementLength() * view.bytesPerElement();
|
||||
}
|
||||
|
||||
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
|
||||
public static Object length(final Object self) {
|
||||
public static int length(final Object self) {
|
||||
return ((ArrayBufferView)self).elementLength();
|
||||
}
|
||||
|
||||
@ -101,182 +121,7 @@ abstract class ArrayBufferView extends ScriptObject {
|
||||
}
|
||||
|
||||
private int elementLength() {
|
||||
return ((ArrayDataImpl)getArray()).elementLength;
|
||||
}
|
||||
|
||||
protected static abstract class ArrayDataImpl extends ArrayData {
|
||||
protected final NativeArrayBuffer buffer;
|
||||
protected final int byteOffset;
|
||||
private final int elementLength;
|
||||
|
||||
protected ArrayDataImpl(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
super(elementLength);
|
||||
this.buffer = buffer;
|
||||
this.byteOffset = byteOffset;
|
||||
this.elementLength = elementLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData copy() {
|
||||
throw new UnsupportedOperationException(); // Not used for ArrayBuffers
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] asObjectArray() {
|
||||
final Object[] array = new Object[elementLength];
|
||||
for (int i = 0; i < elementLength; i++) {
|
||||
array[i] = getObjectImpl(i);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData ensure(final long safeIndex) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLength(final long length) {
|
||||
//empty?
|
||||
//TODO is this right?
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData shrink(final long newLength) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(final int index, final Object value, final boolean strict) {
|
||||
if (has(index)) {
|
||||
setImpl(index, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(final int index, final int value, final boolean strict) {
|
||||
if (has(index)) {
|
||||
setImpl(index, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(final int index, final long value, final boolean strict) {
|
||||
if (has(index)) {
|
||||
setImpl(index, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(final int index, final double value, final boolean strict) {
|
||||
if (has(index)) {
|
||||
setImpl(index, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(final int index) {
|
||||
return getIntImpl(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(final int index) {
|
||||
return getLongImpl(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(final int index) {
|
||||
return getDoubleImpl(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(final int index) {
|
||||
return getObjectImpl(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(final int index) {
|
||||
return index >= 0 && index < elementLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDelete(final int index, final boolean strict) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData delete(final int index) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData delete(final long fromIndex, final long toIndex) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ArrayData convert(final Class<?> type) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shiftLeft(final int by) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData shiftRight(final int by) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object pop() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData slice(final long from, final long to) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected abstract int getIntImpl(int key);
|
||||
|
||||
protected long getLongImpl(final int key) {
|
||||
return getIntImpl(key);
|
||||
}
|
||||
|
||||
protected double getDoubleImpl(final int key) {
|
||||
return getIntImpl(key);
|
||||
}
|
||||
|
||||
protected Object getObjectImpl(final int key) {
|
||||
return getIntImpl(key);
|
||||
}
|
||||
|
||||
protected abstract void setImpl(int key, int value);
|
||||
|
||||
protected void setImpl(final int key, final long value) {
|
||||
setImpl(key, (int)value);
|
||||
}
|
||||
|
||||
protected void setImpl(final int key, final double value) {
|
||||
setImpl(key, JSType.toInt32(value));
|
||||
}
|
||||
|
||||
protected void setImpl(final int key, final Object value) {
|
||||
setImpl(key, JSType.toInt32(value));
|
||||
}
|
||||
|
||||
protected abstract int byteIndex(int index);
|
||||
return ((TypedArrayData<?>)getArray()).getElementLength();
|
||||
}
|
||||
|
||||
protected static abstract class Factory {
|
||||
@ -284,12 +129,12 @@ abstract class ArrayBufferView extends ScriptObject {
|
||||
final int maxElementLength;
|
||||
|
||||
public Factory(final int bytesPerElement) {
|
||||
this.bytesPerElement = bytesPerElement;
|
||||
this.bytesPerElement = bytesPerElement;
|
||||
this.maxElementLength = Integer.MAX_VALUE / bytesPerElement;
|
||||
}
|
||||
|
||||
public final ArrayBufferView construct(final int elementLength) {
|
||||
if(elementLength > maxElementLength) {
|
||||
if (elementLength > maxElementLength) {
|
||||
throw rangeError("inappropriate.array.buffer.length", JSType.toString(elementLength));
|
||||
}
|
||||
return construct(new NativeArrayBuffer(elementLength * bytesPerElement), 0, elementLength);
|
||||
@ -297,25 +142,39 @@ abstract class ArrayBufferView extends ScriptObject {
|
||||
|
||||
public abstract ArrayBufferView construct(NativeArrayBuffer buffer, int byteOffset, int elementLength);
|
||||
|
||||
public abstract ArrayData createArrayData(NativeArrayBuffer buffer, int byteOffset, int elementLength);
|
||||
public abstract TypedArrayData<?> createArrayData(ByteBuffer nb, int start, int end);
|
||||
|
||||
public abstract String getClassName();
|
||||
}
|
||||
|
||||
protected abstract Factory factory();
|
||||
|
||||
protected abstract ScriptObject getPrototype(final Global global);
|
||||
|
||||
@Override
|
||||
public final String getClassName() {
|
||||
return factory().getClassName();
|
||||
}
|
||||
|
||||
protected boolean isFloatArray() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static ArrayBufferView constructorImpl(final Object[] args, final Factory factory) {
|
||||
final Object arg0 = args.length != 0 ? args[0] : 0;
|
||||
final ArrayBufferView dst;
|
||||
final int length;
|
||||
protected static ArrayBufferView constructorImpl(final boolean newObj, final Object[] args, final Factory factory) {
|
||||
final Object arg0 = args.length != 0 ? args[0] : 0;
|
||||
final ArrayBufferView dest;
|
||||
final int length;
|
||||
|
||||
if (!newObj) {
|
||||
throw typeError("constructor.requires.new", factory.getClassName());
|
||||
}
|
||||
|
||||
|
||||
if (arg0 instanceof NativeArrayBuffer) {
|
||||
// Constructor(ArrayBuffer buffer, optional unsigned long byteOffset, optional unsigned long length)
|
||||
final NativeArrayBuffer buffer = (NativeArrayBuffer) arg0;
|
||||
final int byteOffset = args.length > 1 ? JSType.toInt32(args[1]) : 0;
|
||||
final NativeArrayBuffer buffer = (NativeArrayBuffer)arg0;
|
||||
final int byteOffset = args.length > 1 ? JSType.toInt32(args[1]) : 0;
|
||||
|
||||
if (args.length > 2) {
|
||||
length = JSType.toInt32(args[2]);
|
||||
} else {
|
||||
@ -324,15 +183,16 @@ abstract class ArrayBufferView extends ScriptObject {
|
||||
}
|
||||
length = (buffer.getByteLength() - byteOffset) / factory.bytesPerElement;
|
||||
}
|
||||
|
||||
return factory.construct(buffer, byteOffset, length);
|
||||
} else if (arg0 instanceof ArrayBufferView) {
|
||||
// Constructor(TypedArray array)
|
||||
length = ((ArrayBufferView)arg0).elementLength();
|
||||
dst = factory.construct(length);
|
||||
dest = factory.construct(length);
|
||||
} else if (arg0 instanceof NativeArray) {
|
||||
// Constructor(type[] array)
|
||||
length = lengthToInt(((NativeArray) arg0).getArray().length());
|
||||
dst = factory.construct(length);
|
||||
dest = factory.construct(length);
|
||||
} else {
|
||||
// Constructor(unsigned long length). Treating infinity as 0 is a special case for ArrayBufferView.
|
||||
final double dlen = JSType.toNumber(arg0);
|
||||
@ -340,8 +200,9 @@ abstract class ArrayBufferView extends ScriptObject {
|
||||
return factory.construct(length);
|
||||
}
|
||||
|
||||
copyElements(dst, length, (ScriptObject)arg0, 0);
|
||||
return dst;
|
||||
copyElements(dest, length, (ScriptObject)arg0, 0);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
protected static Object setImpl(final Object self, final Object array, final Object offset0) {
|
||||
@ -357,7 +218,7 @@ abstract class ArrayBufferView extends ScriptObject {
|
||||
throw new RuntimeException("argument is not of array type");
|
||||
}
|
||||
|
||||
final ScriptObject source = (ScriptObject) array;
|
||||
final ScriptObject source = (ScriptObject)array;
|
||||
final int offset = JSType.toInt32(offset0); // default=0
|
||||
|
||||
if (dest.elementLength() < length + offset || offset < 0) {
|
||||
@ -385,15 +246,39 @@ abstract class ArrayBufferView extends ScriptObject {
|
||||
if (length > Integer.MAX_VALUE || length < 0) {
|
||||
throw rangeError("inappropriate.array.buffer.length", JSType.toString(length));
|
||||
}
|
||||
return (int) (length & Integer.MAX_VALUE);
|
||||
return (int)(length & Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
protected static Object subarrayImpl(final Object self, final Object begin0, final Object end0) {
|
||||
final ArrayBufferView arrayView = ((ArrayBufferView)self);
|
||||
final int elementLength = arrayView.elementLength();
|
||||
final int begin = NativeArrayBuffer.adjustIndex(JSType.toInt32(begin0), elementLength);
|
||||
final int end = NativeArrayBuffer.adjustIndex(end0 != ScriptRuntime.UNDEFINED ? JSType.toInt32(end0) : elementLength, elementLength);
|
||||
final ArrayDataImpl arrayData = (ArrayDataImpl)arrayView.getArray();
|
||||
return arrayView.factory().construct(arrayData.buffer, arrayData.byteIndex(begin), Math.max(end - begin, 0));
|
||||
final ArrayBufferView arrayView = ((ArrayBufferView)self);
|
||||
final int byteOffset = arrayView.byteOffset;
|
||||
final int bytesPerElement = arrayView.bytesPerElement();
|
||||
final int elementLength = arrayView.elementLength();
|
||||
final int begin = NativeArrayBuffer.adjustIndex(JSType.toInt32(begin0), elementLength);
|
||||
final int end = NativeArrayBuffer.adjustIndex(end0 != ScriptRuntime.UNDEFINED ? JSType.toInt32(end0) : elementLength, elementLength);
|
||||
final int length = Math.max(end - begin, 0);
|
||||
|
||||
assert byteOffset % bytesPerElement == 0;
|
||||
|
||||
//second is byteoffset
|
||||
return arrayView.factory().construct(arrayView.buffer, begin * bytesPerElement + byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
final GuardedInvocation inv = getArray().findFastGetIndexMethod(getArray().getClass(), desc, request);
|
||||
if (inv != null) {
|
||||
return inv;
|
||||
}
|
||||
return super.findGetIndexMethod(desc, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
final GuardedInvocation inv = getArray().findFastSetIndexMethod(getArray().getClass(), desc, request);
|
||||
if (inv != null) {
|
||||
return inv;
|
||||
}
|
||||
return super.findSetIndexMethod(desc, request);
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,6 @@ import java.lang.ref.SoftReference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -472,7 +471,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
||||
* @return the global singleton
|
||||
*/
|
||||
public static Global instance() {
|
||||
ScriptObject global = Context.getGlobal();
|
||||
final ScriptObject global = Context.getGlobal();
|
||||
if (! (global instanceof Global)) {
|
||||
throw new IllegalStateException("no current global instance");
|
||||
}
|
||||
@ -705,7 +704,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
||||
private final int size;
|
||||
private final ReferenceQueue<Class<?>> queue;
|
||||
|
||||
ClassCache(int size) {
|
||||
ClassCache(final int size) {
|
||||
super(size, 0.75f, true);
|
||||
this.size = size;
|
||||
this.queue = new ReferenceQueue<>();
|
||||
@ -721,7 +720,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassReference get(Object key) {
|
||||
public ClassReference get(final Object key) {
|
||||
for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) {
|
||||
remove(ref.source);
|
||||
}
|
||||
@ -743,7 +742,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
||||
@Override
|
||||
public Class<?> findCachedClass(final Source source) {
|
||||
assert classCache != null : "Class cache used without being initialized";
|
||||
ClassReference ref = classCache.get(source);
|
||||
final ClassReference ref = classCache.get(source);
|
||||
return ref != null ? ref.get() : null;
|
||||
}
|
||||
|
||||
@ -815,7 +814,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
||||
return str;
|
||||
}
|
||||
final Global global = Global.instance();
|
||||
final ScriptObject scope = (self instanceof ScriptObject) ? (ScriptObject)self : global;
|
||||
final ScriptObject scope = self instanceof ScriptObject ? (ScriptObject)self : global;
|
||||
|
||||
return global.getContext().eval(scope, str.toString(), callThis, location, Boolean.TRUE.equals(strict));
|
||||
}
|
||||
@ -856,7 +855,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
||||
*/
|
||||
public static Object load(final Object self, final Object source) throws IOException {
|
||||
final Global global = Global.instance();
|
||||
final ScriptObject scope = (self instanceof ScriptObject) ? (ScriptObject)self : global;
|
||||
final ScriptObject scope = self instanceof ScriptObject ? (ScriptObject)self : global;
|
||||
return global.getContext().load(scope, source);
|
||||
}
|
||||
|
||||
@ -1610,11 +1609,13 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
||||
* not the case
|
||||
*
|
||||
* @param obj and object to check
|
||||
* @return the script object
|
||||
*/
|
||||
public static void checkObject(final Object obj) {
|
||||
public static ScriptObject checkObject(final Object obj) {
|
||||
if (!(obj instanceof ScriptObject)) {
|
||||
throw typeError("not.an.object", ScriptRuntime.safeToString(obj));
|
||||
}
|
||||
return (ScriptObject)obj;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1665,7 +1666,8 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
||||
// initialize global function properties
|
||||
this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL);
|
||||
|
||||
this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT);
|
||||
this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT,
|
||||
new MethodHandle[] { GlobalFunctions.PARSEINT_OI, GlobalFunctions.PARSEINT_O });
|
||||
this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT);
|
||||
this.isNaN = ScriptFunctionImpl.makeFunction("isNaN", GlobalFunctions.IS_NAN);
|
||||
this.isFinite = ScriptFunctionImpl.makeFunction("isFinite", GlobalFunctions.IS_FINITE);
|
||||
@ -1751,13 +1753,12 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
||||
copyBuiltins();
|
||||
|
||||
// expose script (command line) arguments as "arguments" property of global
|
||||
final List<String> arguments = env.getArguments();
|
||||
final Object argsObj = wrapAsObject(arguments.toArray());
|
||||
final int flags = jdk.nashorn.internal.runtime.Property.IS_ALWAYS_OBJECT | Attribute.NOT_ENUMERABLE;
|
||||
addOwnProperty("arguments", flags, argsObj);
|
||||
final Object argumentsObject = wrapAsObject(env.getArguments().toArray());
|
||||
final int argumentsFlags = Attribute.NOT_ENUMERABLE;
|
||||
addOwnProperty("arguments", argumentsFlags, argumentsObject);
|
||||
if (env._scripting) {
|
||||
// synonym for "arguments" in scripting mode
|
||||
addOwnProperty("$ARG", flags, argsObj);
|
||||
addOwnProperty("$ARG", argumentsFlags, argumentsObject);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1857,7 +1858,7 @@ public final class Global extends ScriptObject implements GlobalObject, Scope {
|
||||
}
|
||||
|
||||
private static void copyOptions(final ScriptObject options, final ScriptEnvironment scriptEnv) {
|
||||
for (Field f : scriptEnv.getClass().getFields()) {
|
||||
for (final Field f : scriptEnv.getClass().getFields()) {
|
||||
try {
|
||||
options.set(f.getName(), f.get(scriptEnv), false);
|
||||
} catch (final IllegalArgumentException | IllegalAccessException exp) {
|
||||
|
@ -29,12 +29,10 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
import static jdk.nashorn.internal.runtime.PropertyDescriptor.VALUE;
|
||||
import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator;
|
||||
import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -42,11 +40,11 @@ import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.nashorn.api.scripting.JSObject;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
@ -54,8 +52,10 @@ import jdk.nashorn.internal.objects.annotations.Getter;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Setter;
|
||||
import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
|
||||
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.Context;
|
||||
import jdk.nashorn.internal.runtime.Debug;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyDescriptor;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
@ -66,11 +66,9 @@ import jdk.nashorn.internal.runtime.Undefined;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayIndex;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
|
||||
import jdk.nashorn.internal.runtime.arrays.ContinuousArray;
|
||||
import jdk.nashorn.internal.runtime.arrays.IteratorAction;
|
||||
import jdk.nashorn.internal.runtime.linker.Bootstrap;
|
||||
import jdk.nashorn.internal.runtime.linker.InvokeByName;
|
||||
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
|
||||
|
||||
/**
|
||||
* Runtime representation of a JavaScript array. NativeArray only holds numeric
|
||||
@ -97,7 +95,7 @@ public final class NativeArray extends ScriptObject {
|
||||
|
||||
NativeArray(final long length) {
|
||||
// TODO assert valid index in long before casting
|
||||
this(ArrayData.allocate((int) length));
|
||||
this(ArrayData.allocate((int)length));
|
||||
}
|
||||
|
||||
NativeArray(final int[] array) {
|
||||
@ -142,65 +140,30 @@ public final class NativeArray extends ScriptObject {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
final ArrayData data = getArray();
|
||||
final MethodType callType = desc.getMethodType();
|
||||
final Class<?> indexType = callType.parameterType(1);
|
||||
final Class<?> returnType = callType.returnType();
|
||||
|
||||
if (data instanceof ContinuousArray && indexType == int.class) {
|
||||
final Object[] args = request.getArguments();
|
||||
final int index = (int)args[args.length - 1];
|
||||
|
||||
if (data.has(index)) {
|
||||
final MethodHandle getArray = ScriptObject.GET_ARRAY.methodHandle();
|
||||
final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT;
|
||||
MethodHandle getElement = ((ContinuousArray)data).getElementGetter(returnType, programPoint);
|
||||
if (getElement != null) {
|
||||
getElement = MH.filterArguments(getElement, 0, MH.asType(getArray, getArray.type().changeReturnType(ContinuousArray.class)));
|
||||
//System.err.println("Relink fast GET "+ desc+ " "+ DynamicLinker.getLinkedCallSiteLocation());
|
||||
return new GuardedInvocation(getElement, null, null, ClassCastException.class);
|
||||
}
|
||||
}
|
||||
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
|
||||
final GuardedInvocation inv = getArray().findFastGetMethod(getArray().getClass(), desc, request, operator);
|
||||
if (inv != null) {
|
||||
return inv;
|
||||
}
|
||||
//System.err.println("Relink slow GET "+ desc+ " "+ DynamicLinker.getLinkedCallSiteLocation());
|
||||
return super.findGetMethod(desc, request, operator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
final GuardedInvocation inv = getArray().findFastGetIndexMethod(getArray().getClass(), desc, request);
|
||||
if (inv != null) {
|
||||
return inv;
|
||||
}
|
||||
return super.findGetIndexMethod(desc, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
|
||||
|
||||
final ArrayData data = getArray();
|
||||
final MethodType callType = desc.getMethodType();
|
||||
final Class<?> indexType = callType.parameterType(1);
|
||||
final Class<?> elementType = callType.parameterType(2);
|
||||
|
||||
if (data instanceof ContinuousArray && indexType == int.class) {
|
||||
final ContinuousArray cdata = (ContinuousArray)data;
|
||||
final Object[] args = request.getArguments();
|
||||
final int index = (int)args[args.length - 2];
|
||||
|
||||
if (data.has(index)) {
|
||||
MethodHandle hasGuard = cdata.getSetGuard();
|
||||
hasGuard = MH.asType(hasGuard, hasGuard.type().changeParameterType(0, ContinuousArray.class));
|
||||
|
||||
MethodHandle setElement = cdata.getElementSetter(elementType); //Z(continuousarraydata, int, int), return true if successful
|
||||
if (setElement != null) {
|
||||
//else we are dealing with a wider type than supported by this callsite
|
||||
MethodHandle getArray = ScriptObject.GET_ARRAY.methodHandle();
|
||||
getArray = MH.asType(getArray, getArray.type().changeReturnType(ContinuousArray.class));
|
||||
setElement = MH.filterArguments(setElement, 0, getArray);
|
||||
hasGuard = MH.filterArguments(hasGuard, 0, getArray);
|
||||
//if this is the first invocation we have to execute the setter ourselves, we know it will work
|
||||
|
||||
//System.err.println("Relink fast SET "+ desc + " "+ DynamicLinker.getLinkedCallSiteLocation());
|
||||
return new GuardedInvocation(setElement, hasGuard, null, ClassCastException.class); //CCE if not a scriptObject anymore
|
||||
}
|
||||
}
|
||||
protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
final GuardedInvocation inv = getArray().findFastSetIndexMethod(getArray().getClass(), desc, request);
|
||||
if (inv != null) {
|
||||
return inv;
|
||||
}
|
||||
|
||||
//System.err.println("Relink slow SET "+ desc + " "+ DynamicLinker.getLinkedCallSiteLocation());
|
||||
return super.findSetIndexMethod(desc, request);
|
||||
}
|
||||
|
||||
@ -856,13 +819,10 @@ public final class NativeArray extends ScriptObject {
|
||||
try {
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
|
||||
if (bulkable(sobj)) {
|
||||
if (sobj.getArray().length() + args.length <= JSType.MAX_UINT) {
|
||||
final ArrayData newData = sobj.getArray().push(true, args);
|
||||
sobj.setArray(newData);
|
||||
return newData.length();
|
||||
}
|
||||
//fallthru
|
||||
if (bulkable(sobj) && sobj.getArray().length() + args.length <= JSType.MAX_UINT) {
|
||||
final ArrayData newData = sobj.getArray().push(true, args);
|
||||
sobj.setArray(newData);
|
||||
return newData.length();
|
||||
}
|
||||
|
||||
long len = JSType.toUint32(sobj.getLength());
|
||||
@ -877,6 +837,88 @@ public final class NativeArray extends ScriptObject {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single int argument
|
||||
*
|
||||
* @param self self reference
|
||||
* @param arg argument to push
|
||||
* @return array after pushes
|
||||
*/
|
||||
/* @SpecializedFunction
|
||||
public static long push(final Object self, final int arg) {
|
||||
try {
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ArrayData arrayData = sobj.getArray();
|
||||
final long length = arrayData.length();
|
||||
|
||||
if (bulkable(sobj) && length + 1 <= JSType.MAX_UINT) {
|
||||
sobj.setArray(arrayData.ensure(length).set(ArrayIndex.getArrayIndex(length), arg, true));
|
||||
return length + 1;
|
||||
}
|
||||
|
||||
long len = JSType.toUint32(sobj.getLength());
|
||||
sobj.set(len++, arg, true);
|
||||
sobj.set("length", len, true);
|
||||
return len;
|
||||
} catch (final ClassCastException | NullPointerException e) {
|
||||
throw typeError("not.an.object", ScriptRuntime.safeToString(self));
|
||||
}
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single number argument
|
||||
*
|
||||
* @param self self reference
|
||||
* @param arg argument to push
|
||||
* @return array after pushes
|
||||
*/
|
||||
/* @SpecializedFunction
|
||||
public static long push(final Object self, final double arg) {
|
||||
try {
|
||||
final ScriptObject sobj = (ScriptObject)self; final ArrayData arrayData = sobj.getArray();
|
||||
final long length = arrayData.length();
|
||||
|
||||
if (bulkable(sobj) && length + 1 <= JSType.MAX_UINT) {
|
||||
sobj.setArray(arrayData.ensure(length).set(ArrayIndex.getArrayIndex(length), arg, true));
|
||||
return length + 1;
|
||||
}
|
||||
|
||||
long len = JSType.toUint32(sobj.getLength());
|
||||
sobj.set(len++, arg, true);
|
||||
sobj.set("length", len, true);
|
||||
return len;
|
||||
} catch (final ClassCastException | NullPointerException e) {
|
||||
throw typeError("not.an.object", ScriptRuntime.safeToString(self));
|
||||
}
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* ECMA 15.4.4.7 Array.prototype.push (args...) specialized for single object argument
|
||||
*
|
||||
* @param self self reference
|
||||
* @param arg argument to push
|
||||
* @return array after pushes
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static long push(final Object self, final Object arg) {
|
||||
try {
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ArrayData arrayData = sobj.getArray();
|
||||
final long length = arrayData.length();
|
||||
if (bulkable(sobj) && length < JSType.MAX_UINT) {
|
||||
sobj.setArray(arrayData.push(true, arg)); //ensure(length).set(ArrayIndex.getArrayIndex(length), arg, true));
|
||||
return length + 1;
|
||||
}
|
||||
|
||||
long len = JSType.toUint32(sobj.getLength());
|
||||
sobj.set(len++, arg, true);
|
||||
sobj.set("length", len, true);
|
||||
return len;
|
||||
} catch (final ClassCastException | NullPointerException e) {
|
||||
throw typeError("not.an.object", ScriptRuntime.safeToString(self));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.4.4.8 Array.prototype.reverse ()
|
||||
*
|
||||
@ -1086,7 +1128,7 @@ public final class NativeArray extends ScriptObject {
|
||||
}
|
||||
|
||||
sobj.setArray(array);
|
||||
}
|
||||
}
|
||||
|
||||
return sobj;
|
||||
} catch (final ClassCastException | NullPointerException e) {
|
||||
@ -1243,7 +1285,7 @@ public final class NativeArray extends ScriptObject {
|
||||
}
|
||||
|
||||
for (int j = 0; j < items.length; j++) {
|
||||
sobj.set(j, items[j], true);
|
||||
sobj.set(j, items[j], true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1520,4 +1562,9 @@ public final class NativeArray extends ScriptObject {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NativeArray@" + Debug.id(this) + '@' + getArray().getClass().getSimpleName();
|
||||
}
|
||||
}
|
||||
|
@ -25,20 +25,28 @@
|
||||
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import java.util.Arrays;
|
||||
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Getter;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
|
||||
/**
|
||||
* NativeArrayBuffer - ArrayBuffer as described in the JS typed
|
||||
* array spec
|
||||
*/
|
||||
@ScriptClass("ArrayBuffer")
|
||||
final class NativeArrayBuffer extends ScriptObject {
|
||||
private final byte[] buffer;
|
||||
public final class NativeArrayBuffer extends ScriptObject {
|
||||
private final ByteBuffer nb;
|
||||
|
||||
// initialized by nasgen
|
||||
private static PropertyMap $nasgenmap$;
|
||||
@ -47,8 +55,19 @@ final class NativeArrayBuffer extends ScriptObject {
|
||||
return $nasgenmap$;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param newObj is this a new call
|
||||
* @param self self
|
||||
* @param args arguments
|
||||
* @return new native array buffer
|
||||
*/
|
||||
@Constructor(arity = 1)
|
||||
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
|
||||
if (!newObj) {
|
||||
throw typeError("constructor.requires.new", "ArrayBuffer");
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
throw new RuntimeException("missing length argument");
|
||||
}
|
||||
@ -56,21 +75,56 @@ final class NativeArrayBuffer extends ScriptObject {
|
||||
return new NativeArrayBuffer(JSType.toInt32(args[0]));
|
||||
}
|
||||
|
||||
protected NativeArrayBuffer(final byte[] byteArray, final Global global) {
|
||||
/**
|
||||
* Constructor
|
||||
* @param nb native byte buffer to wrap
|
||||
* @param global global instance
|
||||
*/
|
||||
protected NativeArrayBuffer(final ByteBuffer nb, final Global global) {
|
||||
super(global.getArrayBufferPrototype(), global.getArrayBufferMap());
|
||||
this.buffer = byteArray;
|
||||
this.nb = nb;
|
||||
}
|
||||
|
||||
protected NativeArrayBuffer(final byte[] byteArray) {
|
||||
this(byteArray, Global.instance());
|
||||
/**
|
||||
* Constructor
|
||||
* @param nb native byte buffer to wrap
|
||||
*/
|
||||
protected NativeArrayBuffer(final ByteBuffer nb) {
|
||||
this(nb, Global.instance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param byteLength byteLength for buffer
|
||||
*/
|
||||
protected NativeArrayBuffer(final int byteLength) {
|
||||
this(new byte[byteLength]);
|
||||
this(ByteBuffer.allocateDirect(byteLength));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone constructor
|
||||
* Used only for slice
|
||||
* @param other original buffer
|
||||
* @param begin begin byte index
|
||||
* @param end end byte index
|
||||
*/
|
||||
protected NativeArrayBuffer(final NativeArrayBuffer other, final int begin, final int end) {
|
||||
this(Arrays.copyOfRange(other.buffer, begin, end));
|
||||
this(cloneBuffer(other.getNioBuffer(), begin, end));
|
||||
}
|
||||
|
||||
private static ByteBuffer cloneBuffer(ByteBuffer original, final int begin, final int end) {
|
||||
final ByteBuffer clone = ByteBuffer.allocateDirect(original.capacity());
|
||||
original.rewind();//copy from the beginning
|
||||
clone.put(original);
|
||||
original.rewind();
|
||||
clone.flip();
|
||||
clone.position(begin);
|
||||
clone.limit(end);
|
||||
return clone.slice();
|
||||
}
|
||||
|
||||
ByteBuffer getNioBuffer() {
|
||||
return nb;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -78,19 +132,55 @@ final class NativeArrayBuffer extends ScriptObject {
|
||||
return "ArrayBuffer";
|
||||
}
|
||||
|
||||
/**
|
||||
* Byte length for native array buffer
|
||||
* @param self native array buffer
|
||||
* @return byte length
|
||||
*/
|
||||
@Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE)
|
||||
public static Object byteLength(final Object self) {
|
||||
return ((NativeArrayBuffer)self).buffer.length;
|
||||
public static int byteLength(final Object self) {
|
||||
return ((NativeArrayBuffer)self).getByteLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Slice function
|
||||
* @param self native array buffer
|
||||
* @param begin0 start byte index
|
||||
* @param end0 end byte index
|
||||
* @return new array buffer, sliced
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object slice(final Object self, final Object begin0, final Object end0) {
|
||||
final NativeArrayBuffer arrayBuffer = (NativeArrayBuffer)self;
|
||||
int begin = JSType.toInt32(begin0);
|
||||
int end = end0 != ScriptRuntime.UNDEFINED ? JSType.toInt32(end0) : arrayBuffer.getByteLength();
|
||||
begin = adjustIndex(begin, arrayBuffer.getByteLength());
|
||||
end = adjustIndex(end, arrayBuffer.getByteLength());
|
||||
return new NativeArrayBuffer((NativeArrayBuffer) self, begin, Math.max(end, begin));
|
||||
final int byteLength = arrayBuffer.getByteLength();
|
||||
final int begin = adjustIndex(JSType.toInt32(begin0), byteLength);
|
||||
final int end = adjustIndex(end0 != ScriptRuntime.UNDEFINED ? JSType.toInt32(end0) : byteLength, byteLength);
|
||||
return new NativeArrayBuffer(arrayBuffer, begin, Math.max(end, begin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized slice function
|
||||
* @param self native array buffer
|
||||
* @param begin start byte index
|
||||
* @param end end byte index
|
||||
* @return new array buffer, sliced
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static Object slice(final Object self, final int begin, final int end) {
|
||||
final NativeArrayBuffer arrayBuffer = (NativeArrayBuffer)self;
|
||||
final int byteLength = arrayBuffer.getByteLength();
|
||||
return new NativeArrayBuffer(arrayBuffer, adjustIndex(begin, byteLength), Math.max(adjustIndex(end, byteLength), begin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized slice function
|
||||
* @param self native array buffer
|
||||
* @param begin start byte index
|
||||
* @return new array buffer, sliced
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static Object slice(final Object self, final int begin) {
|
||||
return slice(self, begin, ((NativeArrayBuffer)self).getByteLength());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -103,10 +193,7 @@ final class NativeArrayBuffer extends ScriptObject {
|
||||
* @return valid index index in the range [0, length).
|
||||
*/
|
||||
static int adjustIndex(final int index, final int length) {
|
||||
if (index < 0) {
|
||||
return clamp(index + length, length);
|
||||
}
|
||||
return clamp(index, length);
|
||||
return index < 0 ? clamp(index + length, length) : clamp(index, length);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,11 +208,7 @@ final class NativeArrayBuffer extends ScriptObject {
|
||||
return index;
|
||||
}
|
||||
|
||||
public byte[] getByteArray() {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public int getByteLength() {
|
||||
return buffer.length;
|
||||
int getByteLength() {
|
||||
return nb.limit();
|
||||
}
|
||||
}
|
||||
|
@ -108,6 +108,7 @@ public final class NativeDebug extends ScriptObject {
|
||||
/**
|
||||
* Returns true if if the two objects are both property maps, and they have identical properties in the same order,
|
||||
* but allows the properties to differ in their types.
|
||||
* @param self self
|
||||
* @param m1 first property map
|
||||
* @param m2 second property map
|
||||
* @return true if they have identical properties in same order, with possibly different types.
|
||||
@ -119,6 +120,7 @@ public final class NativeDebug extends ScriptObject {
|
||||
|
||||
/**
|
||||
* Returns a diagnostic string representing the difference of two property maps.
|
||||
* @param self self
|
||||
* @param m1 first property map
|
||||
* @param m2 second property map
|
||||
* @return a diagnostic string representing the difference of two property maps.
|
||||
@ -128,7 +130,6 @@ public final class NativeDebug extends ScriptObject {
|
||||
return PropertyMap.diff((PropertyMap)m1, (PropertyMap)m2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Object util - getClass
|
||||
*
|
||||
|
@ -151,8 +151,7 @@ public final class NativeError extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
|
||||
public static Object captureStackTrace(final Object self, final Object errorObj) {
|
||||
Global.checkObject(errorObj);
|
||||
final ScriptObject sobj = (ScriptObject)errorObj;
|
||||
final ScriptObject sobj = Global.checkObject(errorObj);
|
||||
initException(sobj);
|
||||
sobj.delete(STACK, false);
|
||||
if (! sobj.has("stack")) {
|
||||
@ -188,8 +187,7 @@ public final class NativeError extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object printStackTrace(final Object self) {
|
||||
Global.checkObject(self);
|
||||
return ECMAException.printStackTrace((ScriptObject)self);
|
||||
return ECMAException.printStackTrace(Global.checkObject(self));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -203,8 +201,7 @@ public final class NativeError extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object getStackTrace(final Object self) {
|
||||
Global.checkObject(self);
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ScriptObject sobj = Global.checkObject(self);
|
||||
final Object exception = ECMAException.getException(sobj);
|
||||
Object[] res;
|
||||
if (exception instanceof Throwable) {
|
||||
@ -224,8 +221,7 @@ public final class NativeError extends ScriptObject {
|
||||
* @return line number from which error was thrown
|
||||
*/
|
||||
public static Object getLineNumber(final Object self) {
|
||||
Global.checkObject(self);
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ScriptObject sobj = Global.checkObject(self);
|
||||
return sobj.has(LINENUMBER) ? sobj.get(LINENUMBER) : ECMAException.getLineNumber(sobj);
|
||||
}
|
||||
|
||||
@ -238,8 +234,7 @@ public final class NativeError extends ScriptObject {
|
||||
* @return value that was set
|
||||
*/
|
||||
public static Object setLineNumber(final Object self, final Object value) {
|
||||
Global.checkObject(self);
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ScriptObject sobj = Global.checkObject(self);
|
||||
if (sobj.hasOwnProperty(LINENUMBER)) {
|
||||
sobj.put(LINENUMBER, value, false);
|
||||
} else {
|
||||
@ -256,8 +251,7 @@ public final class NativeError extends ScriptObject {
|
||||
* @return column number from which error was thrown
|
||||
*/
|
||||
public static Object getColumnNumber(final Object self) {
|
||||
Global.checkObject(self);
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ScriptObject sobj = Global.checkObject(self);
|
||||
return sobj.has(COLUMNNUMBER) ? sobj.get(COLUMNNUMBER) : ECMAException.getColumnNumber((ScriptObject)self);
|
||||
}
|
||||
|
||||
@ -270,8 +264,7 @@ public final class NativeError extends ScriptObject {
|
||||
* @return value that was set
|
||||
*/
|
||||
public static Object setColumnNumber(final Object self, final Object value) {
|
||||
Global.checkObject(self);
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ScriptObject sobj = Global.checkObject(self);
|
||||
if (sobj.hasOwnProperty(COLUMNNUMBER)) {
|
||||
sobj.put(COLUMNNUMBER, value, false);
|
||||
} else {
|
||||
@ -288,8 +281,7 @@ public final class NativeError extends ScriptObject {
|
||||
* @return file name from which error was thrown
|
||||
*/
|
||||
public static Object getFileName(final Object self) {
|
||||
Global.checkObject(self);
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ScriptObject sobj = Global.checkObject(self);
|
||||
return sobj.has(FILENAME) ? sobj.get(FILENAME) : ECMAException.getFileName((ScriptObject)self);
|
||||
}
|
||||
|
||||
@ -302,8 +294,7 @@ public final class NativeError extends ScriptObject {
|
||||
* @return value that was set
|
||||
*/
|
||||
public static Object setFileName(final Object self, final Object value) {
|
||||
Global.checkObject(self);
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ScriptObject sobj = Global.checkObject(self);
|
||||
if (sobj.hasOwnProperty(FILENAME)) {
|
||||
sobj.put(FILENAME, value, false);
|
||||
} else {
|
||||
@ -322,8 +313,7 @@ public final class NativeError extends ScriptObject {
|
||||
* @return value of "stack" property
|
||||
*/
|
||||
public static Object getStack(final Object self) {
|
||||
Global.checkObject(self);
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ScriptObject sobj = Global.checkObject(self);
|
||||
if (sobj.has(STACK)) {
|
||||
return sobj.get(STACK);
|
||||
}
|
||||
@ -348,8 +338,7 @@ public final class NativeError extends ScriptObject {
|
||||
* @return value that was set
|
||||
*/
|
||||
public static Object setStack(final Object self, final Object value) {
|
||||
Global.checkObject(self);
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ScriptObject sobj = Global.checkObject(self);
|
||||
sobj.set(STACK, value, false);
|
||||
return value;
|
||||
}
|
||||
@ -364,9 +353,7 @@ public final class NativeError extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object toString(final Object self) {
|
||||
// Step 1 and 2 : check if 'self' is object it not throw TypeError
|
||||
Global.checkObject(self);
|
||||
|
||||
final ScriptObject sobj = (ScriptObject)self;
|
||||
final ScriptObject sobj = Global.checkObject(self);
|
||||
|
||||
// Step 3 & 4 : get "name" and convert to String.
|
||||
// But if message is undefined make it "Error".
|
||||
|
@ -25,6 +25,13 @@
|
||||
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
@ -35,6 +42,7 @@ import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
|
||||
|
||||
/**
|
||||
* Float32 array for the TypedArray extension
|
||||
@ -56,73 +64,103 @@ public final class NativeFloat32Array extends ArrayBufferView {
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new NativeFloat32Array(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData createArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new Float32ArrayData(buffer, byteOffset, length);
|
||||
public Float32ArrayData createArrayData(final ByteBuffer nb, final int start, final int end) {
|
||||
return new Float32ArrayData(nb.asFloatBuffer(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Float32Array";
|
||||
}
|
||||
};
|
||||
|
||||
private static final class Float32ArrayData extends ArrayDataImpl {
|
||||
private Float32ArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
super(buffer, byteOffset, elementLength);
|
||||
private static final class Float32ArrayData extends TypedArrayData<FloatBuffer> {
|
||||
|
||||
private static final MethodHandle GET_ELEM = specialCall(MethodHandles.lookup(), Float32ArrayData.class, "getElem", double.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), Float32ArrayData.class, "setElem", void.class, int.class, double.class).methodHandle();
|
||||
|
||||
private Float32ArrayData(final FloatBuffer nb, final int start, final int end) {
|
||||
super(((FloatBuffer)nb.position(start).limit(end)).slice(), end - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int byteIndex(final int index) {
|
||||
return index * BYTES_PER_ELEMENT + byteOffset;
|
||||
protected MethodHandle getGetElem() {
|
||||
return GET_ELEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double getDoubleImpl(final int index) {
|
||||
final int byteIndex = byteIndex(index);
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
final int bits = byteArray[byteIndex ] & 0x0000_00ff |
|
||||
byteArray[byteIndex+1] << 8 & 0x0000_ff00 |
|
||||
byteArray[byteIndex+2] << 16 & 0x00ff_0000 |
|
||||
byteArray[byteIndex+3] << 24 & 0xff00_0000 ;
|
||||
return Float.intBitsToFloat(bits);
|
||||
protected MethodHandle getSetElem() {
|
||||
return SET_ELEM;
|
||||
}
|
||||
|
||||
private double getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new ClassCastException(); //force relink - this works for unoptimistic too
|
||||
}
|
||||
}
|
||||
|
||||
private void setElem(final int index, final double elem) {
|
||||
try {
|
||||
nb.put(index, (float)elem);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
//swallow valid array indexes. it's ok.
|
||||
if (index < 0) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getIntImpl(final int index) {
|
||||
return (int)getDoubleImpl(index);
|
||||
public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
|
||||
if (returnType == int.class || returnType == long.class) {
|
||||
return null;
|
||||
}
|
||||
return getContinuousElementGetter(getClass(), GET_ELEM, returnType, programPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getLongImpl(final int key) {
|
||||
return (long)getDoubleImpl(key);
|
||||
public int getInt(int index) {
|
||||
return (int)getDouble(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getObjectImpl(final int key) {
|
||||
return getDoubleImpl(key);
|
||||
public long getLong(int index) {
|
||||
return (long)getDouble(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int index, final double value) {
|
||||
final int bits = Float.floatToRawIntBits((float)value);
|
||||
final int byteIndex = byteIndex(index);
|
||||
@SuppressWarnings("MismatchedReadAndWriteOfArray")
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
byteArray[byteIndex ] = (byte)(bits & 0xff);
|
||||
byteArray[byteIndex+1] = (byte)(bits >>> 8 & 0xff);
|
||||
byteArray[byteIndex+2] = (byte)(bits >>> 16 & 0xff);
|
||||
byteArray[byteIndex+3] = (byte)(bits >>> 24 & 0xff);
|
||||
public double getDouble(int index) {
|
||||
return getElem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int key, final int value) {
|
||||
setImpl(key, (double)value);
|
||||
public Object getObject(int index) {
|
||||
return getDouble(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int key, final long value) {
|
||||
setImpl(key, (double)value);
|
||||
public ArrayData set(int index, Object value, boolean strict) {
|
||||
return set(index, JSType.toNumber(value), strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int key, final Object value) {
|
||||
setImpl(key, JSType.toNumber(value));
|
||||
public ArrayData set(int index, int value, boolean strict) {
|
||||
return set(index, (double)value, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, long value, boolean strict) {
|
||||
return set(index, (double)value, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, double value, boolean strict) {
|
||||
setElem(index, value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,18 +175,13 @@ public final class NativeFloat32Array extends ArrayBufferView {
|
||||
*/
|
||||
@Constructor(arity = 1)
|
||||
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
|
||||
return constructorImpl(args, FACTORY);
|
||||
return constructorImpl(newObj, args, FACTORY);
|
||||
}
|
||||
|
||||
NativeFloat32Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
super(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Float32Array";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Factory factory() {
|
||||
return FACTORY;
|
||||
|
@ -25,6 +25,13 @@
|
||||
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.DoubleBuffer;
|
||||
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
@ -35,6 +42,7 @@ import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
|
||||
|
||||
/**
|
||||
* Float64 array for the TypedArray extension
|
||||
@ -56,83 +64,103 @@ public final class NativeFloat64Array extends ArrayBufferView {
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new NativeFloat64Array(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData createArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new Float64ArrayData(buffer, byteOffset, length);
|
||||
public Float64ArrayData createArrayData(final ByteBuffer nb, final int start, final int length) {
|
||||
return new Float64ArrayData(nb.asDoubleBuffer(), start, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Float64Array";
|
||||
}
|
||||
};
|
||||
|
||||
private static final class Float64ArrayData extends ArrayDataImpl {
|
||||
private Float64ArrayData(final NativeArrayBuffer buffer,
|
||||
final int byteOffset, final int elementLength) {
|
||||
super(buffer, byteOffset, elementLength);
|
||||
private static final class Float64ArrayData extends TypedArrayData<DoubleBuffer> {
|
||||
|
||||
private static final MethodHandle GET_ELEM = specialCall(MethodHandles.lookup(), Float64ArrayData.class, "getElem", double.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), Float64ArrayData.class, "setElem", void.class, int.class, double.class).methodHandle();
|
||||
|
||||
private Float64ArrayData(final DoubleBuffer nb, final int start, final int end) {
|
||||
super(((DoubleBuffer)nb.position(start).limit(end)).slice(), end - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int byteIndex(final int index) {
|
||||
return index * BYTES_PER_ELEMENT + byteOffset;
|
||||
protected MethodHandle getGetElem() {
|
||||
return GET_ELEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double getDoubleImpl(final int index) {
|
||||
final int byteIndex = byteIndex(index);
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
final long bits;
|
||||
bits = byteArray[byteIndex ] & 0x0000_0000_0000_00ffL |
|
||||
(long)byteArray[byteIndex+1] << 8 & 0x0000_0000_0000_ff00L |
|
||||
(long)byteArray[byteIndex+2] << 16 & 0x0000_0000_00ff_0000L |
|
||||
(long)byteArray[byteIndex+3] << 24 & 0x0000_0000_ff00_0000L |
|
||||
(long)byteArray[byteIndex+4] << 32 & 0x0000_00ff_0000_0000L |
|
||||
(long)byteArray[byteIndex+5] << 40 & 0x0000_ff00_0000_0000L |
|
||||
(long)byteArray[byteIndex+6] << 48 & 0x00ff_0000_0000_0000L |
|
||||
(long)byteArray[byteIndex+7] << 56 & 0xff00_0000_0000_0000L ;
|
||||
return Double.longBitsToDouble(bits);
|
||||
protected MethodHandle getSetElem() {
|
||||
return SET_ELEM;
|
||||
}
|
||||
|
||||
private double getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new ClassCastException(); //force relink - this works for unoptimistic too
|
||||
}
|
||||
}
|
||||
|
||||
private void setElem(final int index, final double elem) {
|
||||
try {
|
||||
nb.put(index, elem);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
//swallow valid array indexes. it's ok.
|
||||
if (index < 0) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getIntImpl(final int index) {
|
||||
return (int)getDoubleImpl(index);
|
||||
public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
|
||||
if (returnType == int.class || returnType == long.class) {
|
||||
return null;
|
||||
}
|
||||
return getContinuousElementGetter(getClass(), GET_ELEM, returnType, programPoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getLongImpl(final int key) {
|
||||
return (long)getDoubleImpl(key);
|
||||
public int getInt(final int index) {
|
||||
return (int)getDouble(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getObjectImpl(final int key) {
|
||||
return getDoubleImpl(key);
|
||||
public long getLong(final int index) {
|
||||
return (long)getDouble(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int index, final double value) {
|
||||
final long bits = Double.doubleToRawLongBits(value);
|
||||
final int byteIndex = byteIndex(index);
|
||||
@SuppressWarnings("MismatchedReadAndWriteOfArray")
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
byteArray[byteIndex ] = (byte)(bits & 0xff);
|
||||
byteArray[byteIndex+1] = (byte)(bits >>> 8 & 0xff);
|
||||
byteArray[byteIndex+2] = (byte)(bits >>> 16 & 0xff);
|
||||
byteArray[byteIndex+3] = (byte)(bits >>> 24 & 0xff);
|
||||
byteArray[byteIndex+4] = (byte)(bits >>> 32 & 0xff);
|
||||
byteArray[byteIndex+5] = (byte)(bits >>> 40 & 0xff);
|
||||
byteArray[byteIndex+6] = (byte)(bits >>> 48 & 0xff);
|
||||
byteArray[byteIndex+7] = (byte)(bits >>> 56 & 0xff);
|
||||
public double getDouble(final int index) {
|
||||
return getElem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int key, final int value) {
|
||||
setImpl(key, (double)value);
|
||||
public Object getObject(final int index) {
|
||||
return getDouble(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int key, final long value) {
|
||||
setImpl(key, (double)value);
|
||||
public ArrayData set(final int index, final Object value, final boolean strict) {
|
||||
return set(index, JSType.toNumber(value), strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int key, final Object value) {
|
||||
setImpl(key, JSType.toNumber(value));
|
||||
public ArrayData set(final int index, final int value, final boolean strict) {
|
||||
return set(index, (double)value, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(final int index, final long value, final boolean strict) {
|
||||
return set(index, (double)value, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(final int index, final double value, final boolean strict) {
|
||||
setElem(index, value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,18 +175,13 @@ public final class NativeFloat64Array extends ArrayBufferView {
|
||||
*/
|
||||
@Constructor(arity = 1)
|
||||
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
|
||||
return constructorImpl(args, FACTORY);
|
||||
return constructorImpl(newObj, args, FACTORY);
|
||||
}
|
||||
|
||||
NativeFloat64Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
super(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Float64Array";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Factory factory() {
|
||||
return FACTORY;
|
||||
|
@ -25,15 +25,24 @@
|
||||
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
|
||||
|
||||
/**
|
||||
* Int16 array for the TypedArray extension
|
||||
@ -56,37 +65,95 @@ public final class NativeInt16Array extends ArrayBufferView {
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new NativeInt16Array(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData createArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new Int16ArrayData(buffer, byteOffset, length);
|
||||
public Int16ArrayData createArrayData(final ByteBuffer nb, final int start, final int end) {
|
||||
return new Int16ArrayData(nb.asShortBuffer(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Int16Array";
|
||||
}
|
||||
};
|
||||
|
||||
private static final class Int16ArrayData extends ArrayDataImpl {
|
||||
private Int16ArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
super(buffer, byteOffset, elementLength);
|
||||
private static final class Int16ArrayData extends TypedArrayData<ShortBuffer> {
|
||||
|
||||
private static final MethodHandle GET_ELEM = specialCall(MethodHandles.lookup(), Int16ArrayData.class, "getElem", int.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), Int16ArrayData.class, "setElem", void.class, int.class, int.class).methodHandle();
|
||||
|
||||
private Int16ArrayData(final ShortBuffer nb, final int start, final int end) {
|
||||
super(((ShortBuffer)nb.position(start).limit(end)).slice(), end - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int byteIndex(final int index) {
|
||||
return index * BYTES_PER_ELEMENT + byteOffset;
|
||||
protected MethodHandle getGetElem() {
|
||||
return GET_ELEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getIntImpl(final int index) {
|
||||
final int byteIndex = byteIndex(index);
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
return byteArray[byteIndex ] & (short)0x00ff |
|
||||
byteArray[byteIndex+1] << 8 & (short)0xff00 ;
|
||||
protected MethodHandle getSetElem() {
|
||||
return SET_ELEM;
|
||||
}
|
||||
|
||||
private int getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new ClassCastException(); //force relink - this works for unoptimistic too
|
||||
}
|
||||
}
|
||||
|
||||
private void setElem(final int index, final int elem) {
|
||||
try {
|
||||
nb.put(index, (short)elem);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
//swallow valid array indexes. it's ok.
|
||||
if (index < 0) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int index, final int value) {
|
||||
final int byteIndex = byteIndex(index);
|
||||
@SuppressWarnings("MismatchedReadAndWriteOfArray")
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
byteArray[byteIndex ] = (byte)(value & 0xff);
|
||||
byteArray[byteIndex+1] = (byte)(value >>> 8 & 0xff);
|
||||
public int getInt(int index) {
|
||||
return getElem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, Object value, boolean strict) {
|
||||
return set(index, JSType.toInt32(value), strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, int value, boolean strict) {
|
||||
setElem(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, long value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, double value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,18 +168,13 @@ public final class NativeInt16Array extends ArrayBufferView {
|
||||
*/
|
||||
@Constructor(arity = 1)
|
||||
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
|
||||
return constructorImpl(args, FACTORY);
|
||||
return constructorImpl(newObj, args, FACTORY);
|
||||
}
|
||||
|
||||
NativeInt16Array(final NativeArrayBuffer buffer, final int byteOffset, final int byteLength) {
|
||||
super(buffer, byteOffset, byteLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Int16Array";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Factory factory() {
|
||||
return FACTORY;
|
||||
|
@ -25,15 +25,24 @@
|
||||
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
|
||||
|
||||
/**
|
||||
* Int32 array for the TypedArray extension
|
||||
@ -55,41 +64,94 @@ public final class NativeInt32Array extends ArrayBufferView {
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new NativeInt32Array(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData createArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new Int32ArrayData(buffer, byteOffset, length);
|
||||
public Int32ArrayData createArrayData(final ByteBuffer nb, final int start, final int length) {
|
||||
return new Int32ArrayData(nb.asIntBuffer(), start, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Int32Array";
|
||||
}
|
||||
};
|
||||
|
||||
private static final class Int32ArrayData extends ArrayDataImpl {
|
||||
private Int32ArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
super(buffer, byteOffset, elementLength);
|
||||
private static final class Int32ArrayData extends TypedArrayData<IntBuffer> {
|
||||
|
||||
private static final MethodHandle GET_ELEM = specialCall(MethodHandles.lookup(), Int32ArrayData.class, "getElem", int.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), Int32ArrayData.class, "setElem", void.class, int.class, int.class).methodHandle();
|
||||
|
||||
private Int32ArrayData(final IntBuffer nb, final int start, final int end) {
|
||||
super(((IntBuffer)nb.position(start).limit(end)).slice(), end - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int byteIndex(final int index) {
|
||||
return index * BYTES_PER_ELEMENT + byteOffset;
|
||||
protected MethodHandle getGetElem() {
|
||||
return GET_ELEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getIntImpl(final int index) {
|
||||
final int byteIndex = byteIndex(index);
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
return byteArray[byteIndex ] & 0x0000_00ff |
|
||||
byteArray[byteIndex+1] << 8 & 0x0000_ff00 |
|
||||
byteArray[byteIndex+2] << 16 & 0x00ff_0000 |
|
||||
byteArray[byteIndex+3] << 24 & 0xff00_0000 ;
|
||||
protected MethodHandle getSetElem() {
|
||||
return SET_ELEM;
|
||||
}
|
||||
|
||||
private int getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new ClassCastException(); //force relink - this works for unoptimistic too
|
||||
}
|
||||
}
|
||||
|
||||
private void setElem(final int index, final int elem) {
|
||||
try {
|
||||
nb.put(index, elem);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
if (index < 0) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int index, final int value) {
|
||||
final int byteIndex = byteIndex(index);
|
||||
@SuppressWarnings("MismatchedReadAndWriteOfArray")
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
byteArray[byteIndex ] = (byte)(value & 0xff);
|
||||
byteArray[byteIndex+1] = (byte)(value >>> 8 & 0xff);
|
||||
byteArray[byteIndex+2] = (byte)(value >>> 16 & 0xff);
|
||||
byteArray[byteIndex+3] = (byte)(value >>> 24 & 0xff);
|
||||
public int getInt(int index) {
|
||||
return getElem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, Object value, boolean strict) {
|
||||
return set(index, JSType.toInt32(value), strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, int value, boolean strict) {
|
||||
setElem(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, long value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, double value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,18 +166,13 @@ public final class NativeInt32Array extends ArrayBufferView {
|
||||
*/
|
||||
@Constructor(arity = 1)
|
||||
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
|
||||
return constructorImpl(args, FACTORY);
|
||||
return constructorImpl(newObj, args, FACTORY);
|
||||
}
|
||||
|
||||
NativeInt32Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
super(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Int32Array";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Factory factory() {
|
||||
return FACTORY;
|
||||
|
@ -25,15 +25,23 @@
|
||||
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
|
||||
|
||||
/**
|
||||
* Int8Array for the TypedArray extension
|
||||
@ -57,32 +65,97 @@ public final class NativeInt8Array extends ArrayBufferView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData createArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new Int8ArrayData(buffer, byteOffset, length);
|
||||
public Int8ArrayData createArrayData(final ByteBuffer nb, final int start, final int end) {
|
||||
return new Int8ArrayData(nb, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Int8Array";
|
||||
}
|
||||
};
|
||||
|
||||
private static final class Int8ArrayData extends ArrayDataImpl {
|
||||
private Int8ArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
super(buffer, byteOffset, elementLength);
|
||||
private static final class Int8ArrayData extends TypedArrayData<ByteBuffer> {
|
||||
|
||||
private static final MethodHandle GET_ELEM = specialCall(MethodHandles.lookup(), Int8ArrayData.class, "getElem", int.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), Int8ArrayData.class, "setElem", void.class, int.class, int.class).methodHandle();
|
||||
|
||||
private Int8ArrayData(final ByteBuffer nb, final int start, final int end) {
|
||||
super(((ByteBuffer)nb.position(start).limit(end)).slice(), end - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int byteIndex(final int index) {
|
||||
return index * BYTES_PER_ELEMENT + byteOffset;
|
||||
protected MethodHandle getGetElem() {
|
||||
return GET_ELEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getIntImpl(final int index) {
|
||||
return buffer.getByteArray()[byteIndex(index)];
|
||||
protected MethodHandle getSetElem() {
|
||||
return SET_ELEM;
|
||||
}
|
||||
|
||||
private int getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new ClassCastException(); //force relink - this works for unoptimistic too
|
||||
}
|
||||
}
|
||||
|
||||
private void setElem(final int index, final int elem) {
|
||||
try {
|
||||
nb.put(index, (byte)elem);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
//swallow valid array indexes. it's ok.
|
||||
if (index < 0) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int index, final int value) {
|
||||
buffer.getByteArray()[byteIndex(index)] = (byte)value;
|
||||
public int getInt(int index) {
|
||||
return getElem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, Object value, boolean strict) {
|
||||
return set(index, JSType.toInt32(value), strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, int value, boolean strict) {
|
||||
setElem(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, long value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, double value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
@ -94,18 +167,13 @@ public final class NativeInt8Array extends ArrayBufferView {
|
||||
*/
|
||||
@Constructor(arity = 1)
|
||||
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
|
||||
return constructorImpl(args, FACTORY);
|
||||
return constructorImpl(newObj, args, FACTORY);
|
||||
}
|
||||
|
||||
NativeInt8Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
super(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Int8Array";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Factory factory() {
|
||||
return FACTORY;
|
||||
|
@ -488,6 +488,20 @@ public final class NativeMath extends ScriptObject {
|
||||
return Math.max(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.8.2.11 max(x) - specialized version for two Object args
|
||||
*
|
||||
* @param self self reference
|
||||
* @param x first argument
|
||||
* @param y second argument
|
||||
*
|
||||
* @return largest value of x and y
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static double max(final Object self, final Object x, final Object y) {
|
||||
return Math.max(JSType.toNumber(x), JSType.toNumber(y));
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.8.2.12 min(x)
|
||||
*
|
||||
@ -566,6 +580,20 @@ public final class NativeMath extends ScriptObject {
|
||||
return Math.min(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.8.2.12 min(x) - specialized version for two Object args
|
||||
*
|
||||
* @param self self reference
|
||||
* @param x first argument
|
||||
* @param y second argument
|
||||
*
|
||||
* @return smallest value of x and y
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static double min(final Object self, final Object x, final Object y) {
|
||||
return Math.min(JSType.toNumber(x), JSType.toNumber(y));
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.8.2.13 pow(x,y)
|
||||
*
|
||||
|
@ -41,6 +41,7 @@ import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
@ -156,8 +157,20 @@ public final class NativeNumber extends ScriptObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object toFixed(final Object self, final Object fractionDigits) {
|
||||
final int f = JSType.toInteger(fractionDigits);
|
||||
if (f < 0 || f > 20) {
|
||||
return toFixed(self, JSType.toInteger(fractionDigits));
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.7.4.5 Number.prototype.toFixed (fractionDigits) specialized for int fractionDigits
|
||||
*
|
||||
* @param self self reference
|
||||
* @param fractionDigits how many digits should be after the decimal point, 0 if undefined
|
||||
*
|
||||
* @return number in decimal fixed point notation
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static Object toFixed(final Object self, final int fractionDigits) {
|
||||
if (fractionDigits < 0 || fractionDigits > 20) {
|
||||
throw rangeError("invalid.fraction.digits", "toFixed");
|
||||
}
|
||||
|
||||
@ -171,8 +184,8 @@ public final class NativeNumber extends ScriptObject {
|
||||
}
|
||||
|
||||
final NumberFormat format = NumberFormat.getNumberInstance(Locale.US);
|
||||
format.setMinimumFractionDigits(f);
|
||||
format.setMaximumFractionDigits(f);
|
||||
format.setMinimumFractionDigits(fractionDigits);
|
||||
format.setMaximumFractionDigits(fractionDigits);
|
||||
format.setGroupingUsed(false);
|
||||
|
||||
return format.format(x);
|
||||
@ -210,7 +223,7 @@ public final class NativeNumber extends ScriptObject {
|
||||
* ECMA 15.7.4.7 Number.prototype.toPrecision (precision)
|
||||
*
|
||||
* @param self self reference
|
||||
* @param precision use {@code precision - 1} digits after the significand's decimal point or call {@link NativeDate#toString} if undefined
|
||||
* @param precision use {@code precision - 1} digits after the significand's decimal point or call {@link JSType#toString} if undefined
|
||||
*
|
||||
* @return number in decimal exponentiation notation or decimal fixed notation depending on {@code precision}
|
||||
*/
|
||||
@ -220,8 +233,23 @@ public final class NativeNumber extends ScriptObject {
|
||||
if (precision == UNDEFINED) {
|
||||
return JSType.toString(x);
|
||||
}
|
||||
return (toPrecision(x, JSType.toInteger(precision)));
|
||||
}
|
||||
|
||||
final int p = JSType.toInteger(precision);
|
||||
/**
|
||||
* ECMA 15.7.4.7 Number.prototype.toPrecision (precision) specialized f
|
||||
*
|
||||
* @param self self reference
|
||||
* @param precision use {@code precision - 1} digits after the significand's decimal point.
|
||||
*
|
||||
* @return number in decimal exponentiation notation or decimal fixed notation depending on {@code precision}
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static Object toPrecision(Object self, final int precision) {
|
||||
return toPrecision(getNumberValue(self), precision);
|
||||
}
|
||||
|
||||
private static Object toPrecision(final double x, final int p) {
|
||||
if (Double.isNaN(x)) {
|
||||
return "NaN";
|
||||
} else if (Double.isInfinite(x)) {
|
||||
|
@ -250,8 +250,7 @@ public final class NativeObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
|
||||
public static Object defineProperty(final Object self, final Object obj, final Object prop, final Object attr) {
|
||||
Global.checkObject(obj);
|
||||
((ScriptObject)obj).defineOwnProperty(JSType.toString(prop), attr, true);
|
||||
Global.checkObject(obj).defineOwnProperty(JSType.toString(prop), attr, true);
|
||||
return obj;
|
||||
}
|
||||
|
||||
@ -265,9 +264,7 @@ public final class NativeObject {
|
||||
*/
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
|
||||
public static Object defineProperties(final Object self, final Object obj, final Object props) {
|
||||
Global.checkObject(obj);
|
||||
|
||||
final ScriptObject sobj = (ScriptObject)obj;
|
||||
final ScriptObject sobj = Global.checkObject(obj);
|
||||
final Object propsObj = Global.toObject(props);
|
||||
|
||||
if (propsObj instanceof ScriptObject) {
|
||||
@ -425,7 +422,7 @@ public final class NativeObject {
|
||||
|
||||
// Object(null), Object(undefined), Object() are same as "new Object()"
|
||||
|
||||
if (newObj || (type == JSType.NULL || type == JSType.UNDEFINED)) {
|
||||
if (newObj || type == JSType.NULL || type == JSType.UNDEFINED) {
|
||||
switch (type) {
|
||||
case BOOLEAN:
|
||||
case NUMBER:
|
||||
@ -513,7 +510,7 @@ public final class NativeObject {
|
||||
final Object key = JSType.toPrimitive(v, String.class);
|
||||
final Object obj = Global.toObject(self);
|
||||
|
||||
return (obj instanceof ScriptObject) && ((ScriptObject)obj).hasOwnProperty(key);
|
||||
return obj instanceof ScriptObject && ((ScriptObject)obj).hasOwnProperty(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -627,12 +624,10 @@ public final class NativeObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
|
||||
public static Object bindProperties(final Object self, final Object target, final Object source) {
|
||||
// target object has to be a ScriptObject
|
||||
Global.checkObject(target);
|
||||
final ScriptObject targetObj = Global.checkObject(target);
|
||||
// check null or undefined source object
|
||||
Global.checkObjectCoercible(source);
|
||||
|
||||
final ScriptObject targetObj = (ScriptObject)target;
|
||||
|
||||
if (source instanceof ScriptObject) {
|
||||
final ScriptObject sourceObj = (ScriptObject)source;
|
||||
|
||||
@ -753,7 +748,7 @@ public final class NativeObject {
|
||||
Bootstrap.bindDynamicMethod(methodGetter.invoke(source), source)), 0, Object.class);
|
||||
} catch(RuntimeException|Error e) {
|
||||
throw e;
|
||||
} catch(Throwable t) {
|
||||
} catch(final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
@ -766,7 +761,7 @@ public final class NativeObject {
|
||||
assert passesGuard(source, inv.getGuard());
|
||||
} catch(RuntimeException|Error e) {
|
||||
throw e;
|
||||
} catch(Throwable t) {
|
||||
} catch(final Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
assert inv.getSwitchPoint() == null; // Linkers in Dynalink's beans package don't use switchpoints.
|
||||
@ -780,6 +775,6 @@ public final class NativeObject {
|
||||
|
||||
private static LinkRequest createLinkRequest(String operation, MethodType methodType, Object source) {
|
||||
return new LinkRequestImpl(CallSiteDescriptorFactory.create(MethodHandles.publicLookup(), operation,
|
||||
methodType), null, false, source);
|
||||
methodType), null, 0, false, source);
|
||||
}
|
||||
}
|
||||
|
@ -602,7 +602,7 @@ public final class NativeRegExp extends ScriptObject {
|
||||
for (int i = 0, lastGroupStart = matcher.start(); i <= groupCount; i++) {
|
||||
final int groupStart = matcher.start(i);
|
||||
if (lastGroupStart > groupStart
|
||||
|| (groupsInNegativeLookahead != null && groupsInNegativeLookahead.isSet(i))) {
|
||||
|| groupsInNegativeLookahead != null && groupsInNegativeLookahead.isSet(i)) {
|
||||
// (1) ECMA 15.10.2.5 NOTE 3: need to clear Atom's captures each time Atom is repeated.
|
||||
// (2) ECMA 15.10.2.8 NOTE 3: Backreferences to captures in (?!Disjunction) from elsewhere
|
||||
// in the pattern always return undefined because the negative lookahead must fail.
|
||||
@ -750,8 +750,8 @@ public final class NativeRegExp extends ScriptObject {
|
||||
cursor++;
|
||||
if (cursor < replacement.length() && firstDigit < matcher.groupCount()) {
|
||||
final int secondDigit = replacement.charAt(cursor) - '0';
|
||||
if ((secondDigit >= 0) && (secondDigit <= 9)) {
|
||||
final int newRefNum = (firstDigit * 10) + secondDigit;
|
||||
if (secondDigit >= 0 && secondDigit <= 9) {
|
||||
final int newRefNum = firstDigit * 10 + secondDigit;
|
||||
if (newRefNum <= matcher.groupCount() && newRefNum > 0) {
|
||||
// $nn ($01-$99)
|
||||
refNum = newRefNum;
|
||||
@ -922,7 +922,6 @@ public final class NativeRegExp extends ScriptObject {
|
||||
}
|
||||
|
||||
private static NativeRegExp checkRegExp(final Object self) {
|
||||
Global.checkObjectCoercible(self);
|
||||
if (self instanceof NativeRegExp) {
|
||||
return (NativeRegExp)self;
|
||||
} else if (self != null && self == Global.instance().getRegExpPrototype()) {
|
||||
|
@ -443,22 +443,8 @@ public final class NativeString extends ScriptObject {
|
||||
public static Object fromCharCode(final Object self, final Object value) {
|
||||
if (value instanceof Integer) {
|
||||
return fromCharCode(self, (int)value);
|
||||
} else if (value instanceof String) {
|
||||
return "" + (char)JSType.toUint16(value);
|
||||
}
|
||||
return fromCharCode(self, new Object[] { value });
|
||||
}
|
||||
|
||||
/**
|
||||
* fromCharCode specialization
|
||||
*
|
||||
* @param self self reference
|
||||
* @param value one argument to be interpreted as char
|
||||
* @return string with one charcode
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static Object fromCharCode(final Object self, final String value) {
|
||||
return fromCharCode(self, new Object[] { value });
|
||||
return Character.toString((char)JSType.toUint16(value));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -469,18 +455,46 @@ public final class NativeString extends ScriptObject {
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static Object fromCharCode(final Object self, final int value) {
|
||||
return "" + (char)(value & 0xffff);
|
||||
return Character.toString((char)(value & 0xffff));
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.5.3.2 - specialization for one char of long type
|
||||
* ECMA 15.5.3.2 - specialization for two chars of int type
|
||||
* @param self self reference
|
||||
* @param value one argument to be interpreted as char
|
||||
* @param ch1 first char
|
||||
* @param ch2 second char
|
||||
* @return string with one charcode
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static Object fromCharCode(final Object self, final long value) {
|
||||
return "" + (char)((int)value & 0xffff);
|
||||
public static Object fromCharCode(final Object self, final int ch1, final int ch2) {
|
||||
return Character.toString((char)(ch1 & 0xffff)) + Character.toString((char)(ch2 & 0xffff));
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.5.3.2 - specialization for three chars of int type
|
||||
* @param self self reference
|
||||
* @param ch1 first char
|
||||
* @param ch2 second char
|
||||
* @param ch3 third char
|
||||
* @return string with one charcode
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static Object fromCharCode(final Object self, final int ch1, final int ch2, final int ch3) {
|
||||
return Character.toString((char)(ch1 & 0xffff)) + Character.toString((char)(ch2 & 0xffff)) + Character.toString((char)(ch3 & 0xffff));
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.5.3.2 - specialization for four chars of int type
|
||||
* @param self self reference
|
||||
* @param ch1 first char
|
||||
* @param ch2 second char
|
||||
* @param ch3 third char
|
||||
* @param ch4 fourth char
|
||||
* @return string with one charcode
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static Object fromCharCode(final Object self, final int ch1, final int ch2, final int ch3, final int ch4) {
|
||||
return Character.toString((char)(ch1 & 0xffff)) + Character.toString((char)(ch2 & 0xffff)) + Character.toString((char)(ch3 & 0xffff)) + Character.toString((char)(ch4 & 0xffff));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -491,7 +505,7 @@ public final class NativeString extends ScriptObject {
|
||||
*/
|
||||
@SpecializedFunction
|
||||
public static Object fromCharCode(final Object self, final double value) {
|
||||
return "" + (char)JSType.toUint16(value);
|
||||
return Character.toString((char)JSType.toUint16(value));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -548,7 +562,7 @@ public final class NativeString extends ScriptObject {
|
||||
}
|
||||
|
||||
private static String charAtImpl(final String str, final int pos) {
|
||||
return (pos < 0 || pos >= str.length()) ? "" : String.valueOf(str.charAt(pos));
|
||||
return pos < 0 || pos >= str.length() ? "" : String.valueOf(str.charAt(pos));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -585,7 +599,7 @@ public final class NativeString extends ScriptObject {
|
||||
}
|
||||
|
||||
private static double charCodeAtImpl(final String str, final int pos) {
|
||||
return (pos < 0 || pos >= str.length()) ? Double.NaN : str.charAt(pos);
|
||||
return pos < 0 || pos >= str.length() ? Double.NaN : str.charAt(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -820,7 +834,7 @@ public final class NativeString extends ScriptObject {
|
||||
@SpecializedFunction
|
||||
public static Object slice(final Object self, final int start) {
|
||||
final String str = checkObjectToString(self);
|
||||
final int from = (start < 0) ? Math.max(str.length() + start, 0) : Math.min(start, str.length());
|
||||
final int from = start < 0 ? Math.max(str.length() + start, 0) : Math.min(start, str.length());
|
||||
|
||||
return str.substring(from);
|
||||
}
|
||||
@ -851,8 +865,8 @@ public final class NativeString extends ScriptObject {
|
||||
final String str = checkObjectToString(self);
|
||||
final int len = str.length();
|
||||
|
||||
final int from = (start < 0) ? Math.max(len + start, 0) : Math.min(start, len);
|
||||
final int to = (end < 0) ? Math.max(len + end, 0) : Math.min(end, len);
|
||||
final int from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
|
||||
final int to = end < 0 ? Math.max(len + end, 0) : Math.min(end, len);
|
||||
|
||||
return str.substring(Math.min(from, to), to);
|
||||
}
|
||||
@ -881,7 +895,7 @@ public final class NativeString extends ScriptObject {
|
||||
@Function(attributes = Attribute.NOT_ENUMERABLE)
|
||||
public static Object split(final Object self, final Object separator, final Object limit) {
|
||||
final String str = checkObjectToString(self);
|
||||
final long lim = (limit == UNDEFINED) ? JSType.MAX_UINT : JSType.toUint32(limit);
|
||||
final long lim = limit == UNDEFINED ? JSType.MAX_UINT : JSType.toUint32(limit);
|
||||
|
||||
if (separator == UNDEFINED) {
|
||||
return lim == 0 ? new NativeArray() : new NativeArray(new Object[]{str});
|
||||
@ -912,7 +926,7 @@ public final class NativeString extends ScriptObject {
|
||||
int n = 0;
|
||||
|
||||
while (pos < strLength && n < limit) {
|
||||
int found = str.indexOf(separator, pos);
|
||||
final int found = str.indexOf(separator, pos);
|
||||
if (found == -1) {
|
||||
break;
|
||||
}
|
||||
@ -945,7 +959,7 @@ public final class NativeString extends ScriptObject {
|
||||
intStart = Math.max(intStart + strLength, 0);
|
||||
}
|
||||
|
||||
final int intLen = Math.min(Math.max((length == UNDEFINED) ? Integer.MAX_VALUE : JSType.toInteger(length), 0), strLength - intStart);
|
||||
final int intLen = Math.min(Math.max(length == UNDEFINED ? Integer.MAX_VALUE : JSType.toInteger(length), 0), strLength - intStart);
|
||||
|
||||
return intLen <= 0 ? "" : str.substring(intStart, intStart + intLen);
|
||||
}
|
||||
@ -1011,8 +1025,8 @@ public final class NativeString extends ScriptObject {
|
||||
public static String substring(final Object self, final int start, final int end) {
|
||||
final String str = checkObjectToString(self);
|
||||
final int len = str.length();
|
||||
final int validStart = start < 0 ? 0 : (start > len ? len : start);
|
||||
final int validEnd = end < 0 ? 0 : (end > len ? len : end);
|
||||
final int validStart = start < 0 ? 0 : start > len ? len : start;
|
||||
final int validEnd = end < 0 ? 0 : end > len ? len : end;
|
||||
|
||||
if (validStart < validEnd) {
|
||||
return str.substring(validStart, validEnd);
|
||||
@ -1105,7 +1119,7 @@ public final class NativeString extends ScriptObject {
|
||||
|
||||
final String str = checkObjectToString(self);
|
||||
int start = 0;
|
||||
int end = str.length() - 1;
|
||||
final int end = str.length() - 1;
|
||||
|
||||
while (start <= end && ScriptRuntime.isJSWhitespace(str.charAt(start))) {
|
||||
start++;
|
||||
@ -1123,7 +1137,7 @@ public final class NativeString extends ScriptObject {
|
||||
public static Object trimRight(final Object self) {
|
||||
|
||||
final String str = checkObjectToString(self);
|
||||
int start = 0;
|
||||
final int start = 0;
|
||||
int end = str.length() - 1;
|
||||
|
||||
while (end >= start && ScriptRuntime.isJSWhitespace(str.charAt(end))) {
|
||||
@ -1150,7 +1164,7 @@ public final class NativeString extends ScriptObject {
|
||||
*/
|
||||
@Constructor(arity = 1)
|
||||
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
|
||||
final CharSequence str = (args.length > 0) ? JSType.toCharSequence(args[0]) : "";
|
||||
final CharSequence str = args.length > 0 ? JSType.toCharSequence(args[0]) : "";
|
||||
return newObj ? newObj(self, str) : str.toString();
|
||||
}
|
||||
|
||||
|
@ -25,15 +25,24 @@
|
||||
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
|
||||
|
||||
/**
|
||||
* Uint16 array for TypedArray extension
|
||||
@ -55,37 +64,100 @@ public final class NativeUint16Array extends ArrayBufferView {
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new NativeUint16Array(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData createArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new Uint16ArrayData(buffer, byteOffset, length);
|
||||
public Uint16ArrayData createArrayData(final ByteBuffer nb, final int start, final int end) {
|
||||
return new Uint16ArrayData(nb.asCharBuffer(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Uint16Array";
|
||||
}
|
||||
};
|
||||
|
||||
private static final class Uint16ArrayData extends ArrayDataImpl {
|
||||
private Uint16ArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
super(buffer, byteOffset, elementLength);
|
||||
private static final class Uint16ArrayData extends TypedArrayData<CharBuffer> {
|
||||
|
||||
private static final MethodHandle GET_ELEM = specialCall(MethodHandles.lookup(), Uint16ArrayData.class, "getElem", int.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), Uint16ArrayData.class, "setElem", void.class, int.class, int.class).methodHandle();
|
||||
|
||||
private Uint16ArrayData(final CharBuffer nb, final int start, final int end) {
|
||||
super(((CharBuffer)nb.position(start).limit(end)).slice(), end - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int byteIndex(final int index) {
|
||||
return index * BYTES_PER_ELEMENT + byteOffset;
|
||||
protected MethodHandle getGetElem() {
|
||||
return GET_ELEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getIntImpl(final int index) {
|
||||
final int byteIndex = byteIndex(index);
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
return byteArray[byteIndex ] & 0x0000_00ff |
|
||||
byteArray[byteIndex+1] << 8 & 0x0000_ff00 ;
|
||||
protected MethodHandle getSetElem() {
|
||||
return SET_ELEM;
|
||||
}
|
||||
|
||||
private int getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new ClassCastException(); //force relink - this works for unoptimistic too
|
||||
}
|
||||
}
|
||||
|
||||
private void setElem(final int index, final int elem) {
|
||||
try {
|
||||
nb.put(index, (char)elem);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
//swallow valid array indexes. it's ok.
|
||||
if (index < 0) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int index, final int value) {
|
||||
final int byteIndex = byteIndex(index);
|
||||
@SuppressWarnings("MismatchedReadAndWriteOfArray")
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
byteArray[byteIndex ] = (byte)(value & 0xff);
|
||||
byteArray[byteIndex+1] = (byte)(value >>> 8 & 0xff);
|
||||
public boolean isUnsigned() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int index) {
|
||||
return getElem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, Object value, boolean strict) {
|
||||
return set(index, JSType.toInt32(value), strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, int value, boolean strict) {
|
||||
setElem(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, long value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, double value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,18 +172,13 @@ public final class NativeUint16Array extends ArrayBufferView {
|
||||
*/
|
||||
@Constructor(arity = 1)
|
||||
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
|
||||
return constructorImpl(args, FACTORY);
|
||||
return constructorImpl(newObj, args, FACTORY);
|
||||
}
|
||||
|
||||
NativeUint16Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
super(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Uint16Array";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Factory factory() {
|
||||
return FACTORY;
|
||||
|
@ -25,6 +25,14 @@
|
||||
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
@ -35,6 +43,7 @@ import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
|
||||
|
||||
/**
|
||||
* Uint32 array for TypedArray extension
|
||||
@ -56,55 +65,108 @@ public final class NativeUint32Array extends ArrayBufferView {
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteBegin, final int length) {
|
||||
return new NativeUint32Array(buffer, byteBegin, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData createArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new Uint32ArrayData(buffer, byteOffset, length);
|
||||
public Uint32ArrayData createArrayData(final ByteBuffer nb, final int start, final int end) {
|
||||
return new Uint32ArrayData(nb.order(ByteOrder.nativeOrder()).asIntBuffer(), start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Uint32Array";
|
||||
}
|
||||
};
|
||||
|
||||
private static final class Uint32ArrayData extends ArrayDataImpl {
|
||||
private Uint32ArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
super(buffer, byteOffset, elementLength);
|
||||
private static final class Uint32ArrayData extends TypedArrayData<IntBuffer> {
|
||||
|
||||
private static final MethodHandle GET_ELEM = specialCall(MethodHandles.lookup(), Uint32ArrayData.class, "getElem", long.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), Uint32ArrayData.class, "setElem", void.class, int.class, int.class).methodHandle();
|
||||
|
||||
private Uint32ArrayData(final IntBuffer nb, final int start, final int end) {
|
||||
super(((IntBuffer)nb.position(start).limit(end)).slice(), end - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int byteIndex(final int index) {
|
||||
return index * BYTES_PER_ELEMENT + byteOffset;
|
||||
protected MethodHandle getGetElem() {
|
||||
return GET_ELEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getIntImpl(final int index) {
|
||||
final int byteIndex = byteIndex(index);
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
return byteArray[byteIndex ] & 0x0000_00ff |
|
||||
byteArray[byteIndex+1] << 8 & 0x0000_ff00 |
|
||||
byteArray[byteIndex+2] << 16 & 0x00ff_0000 |
|
||||
byteArray[byteIndex+3] << 24 & 0xff00_0000 ;
|
||||
protected MethodHandle getSetElem() {
|
||||
return SET_ELEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long getLongImpl(final int key) {
|
||||
return getIntImpl(key) & JSType.MAX_UINT;
|
||||
public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
|
||||
if (returnType == int.class) {
|
||||
return null;
|
||||
}
|
||||
return getContinuousElementGetter(getClass(), GET_ELEM, returnType, programPoint);
|
||||
}
|
||||
|
||||
private long getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index) & JSType.MAX_UINT;
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new ClassCastException(); //force relink - this works for unoptimistic too
|
||||
}
|
||||
}
|
||||
|
||||
private void setElem(final int index, final int elem) {
|
||||
try {
|
||||
nb.put(index, elem);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
//swallow valid array indexes. it's ok.
|
||||
if (index < 0) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double getDoubleImpl(final int key) {
|
||||
return getIntImpl(key) & JSType.MAX_UINT;
|
||||
public boolean isUnsigned() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getObjectImpl(final int key) {
|
||||
return getIntImpl(key) & JSType.MAX_UINT;
|
||||
public int getInt(int index) {
|
||||
return (int)getLong(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int index, final int value) {
|
||||
final int byteIndex = byteIndex(index);
|
||||
final byte[] byteArray = buffer.getByteArray();
|
||||
byteArray[byteIndex ] = (byte)(value & 0xff);
|
||||
byteArray[byteIndex+1] = (byte)(value >>> 8 & 0xff);
|
||||
byteArray[byteIndex+2] = (byte)(value >>> 16 & 0xff);
|
||||
byteArray[byteIndex+3] = (byte)(value >>> 24 & 0xff);
|
||||
public long getLong(int index) {
|
||||
return getElem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int index) {
|
||||
return getLong(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int index) {
|
||||
return getLong(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, Object value, boolean strict) {
|
||||
return set(index, JSType.toInt32(value), strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, int value, boolean strict) {
|
||||
setElem(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, long value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, double value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,18 +181,13 @@ public final class NativeUint32Array extends ArrayBufferView {
|
||||
*/
|
||||
@Constructor(arity = 1)
|
||||
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
|
||||
return constructorImpl(args, FACTORY);
|
||||
return constructorImpl(newObj, args, FACTORY);
|
||||
}
|
||||
|
||||
NativeUint32Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
super(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Uint32Array";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Factory factory() {
|
||||
return FACTORY;
|
||||
|
@ -25,21 +25,30 @@
|
||||
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
import jdk.nashorn.internal.objects.annotations.Property;
|
||||
import jdk.nashorn.internal.objects.annotations.ScriptClass;
|
||||
import jdk.nashorn.internal.objects.annotations.Where;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
|
||||
|
||||
/**
|
||||
* Uint8 array for TypedArray extension
|
||||
*/
|
||||
@ScriptClass("Uint8Array")
|
||||
public final class NativeUint8Array extends ArrayBufferView {
|
||||
|
||||
/**
|
||||
* The size in bytes of each element in the array.
|
||||
*/
|
||||
@ -55,31 +64,102 @@ public final class NativeUint8Array extends ArrayBufferView {
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new NativeUint8Array(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData createArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new Uint8ArrayData(buffer, byteOffset, length);
|
||||
public Uint8ArrayData createArrayData(final ByteBuffer nb, final int start, final int end) {
|
||||
return new Uint8ArrayData(nb, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Uint8Array";
|
||||
}
|
||||
};
|
||||
|
||||
private static final class Uint8ArrayData extends ArrayDataImpl {
|
||||
private Uint8ArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
super(buffer, byteOffset, elementLength);
|
||||
private static final class Uint8ArrayData extends TypedArrayData<ByteBuffer> {
|
||||
|
||||
private static final MethodHandle GET_ELEM = specialCall(MethodHandles.lookup(), Uint8ArrayData.class, "getElem", int.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), Uint8ArrayData.class, "setElem", void.class, int.class, int.class).methodHandle();
|
||||
|
||||
private Uint8ArrayData(final ByteBuffer nb, final int start, final int end) {
|
||||
super(((ByteBuffer)nb.position(start).limit(end)).slice(), end - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int byteIndex(final int index) {
|
||||
return index * BYTES_PER_ELEMENT + byteOffset;
|
||||
protected MethodHandle getGetElem() {
|
||||
return GET_ELEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getIntImpl(final int index) {
|
||||
return buffer.getByteArray()[byteIndex(index)] & 0xff;
|
||||
protected MethodHandle getSetElem() {
|
||||
return SET_ELEM;
|
||||
}
|
||||
|
||||
private int getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index) & 0xff;
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new ClassCastException(); //force relink - this works for unoptimistic too
|
||||
}
|
||||
}
|
||||
|
||||
private void setElem(final int index, final int elem) {
|
||||
try {
|
||||
nb.put(index, (byte)elem);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
//swallow valid array indexes. it's ok.
|
||||
if (index < 0) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int index, final int value) {
|
||||
buffer.getByteArray()[byteIndex(index)] = (byte)value;
|
||||
public boolean isUnsigned() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int index) {
|
||||
return getElem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, Object value, boolean strict) {
|
||||
return set(index, JSType.toInt32(value), strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, int value, boolean strict) {
|
||||
setElem(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, long value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, double value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -93,16 +173,11 @@ public final class NativeUint8Array extends ArrayBufferView {
|
||||
*/
|
||||
@Constructor(arity = 1)
|
||||
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
|
||||
return constructorImpl(args, FACTORY);
|
||||
return constructorImpl(newObj, args, FACTORY);
|
||||
}
|
||||
|
||||
NativeUint8Array(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
super(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Uint8Array";
|
||||
NativeUint8Array(NativeArrayBuffer buffer, int byteOffset, int elementLength) {
|
||||
super(buffer, byteOffset, elementLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,6 +25,14 @@
|
||||
|
||||
package jdk.nashorn.internal.objects;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import jdk.nashorn.internal.objects.annotations.Attribute;
|
||||
import jdk.nashorn.internal.objects.annotations.Constructor;
|
||||
import jdk.nashorn.internal.objects.annotations.Function;
|
||||
@ -35,6 +43,7 @@ import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.arrays.ArrayData;
|
||||
import jdk.nashorn.internal.runtime.arrays.TypedArrayData;
|
||||
|
||||
/**
|
||||
* Uint8 clamped array for TypedArray extension
|
||||
@ -56,47 +65,127 @@ public final class NativeUint8ClampedArray extends ArrayBufferView {
|
||||
public ArrayBufferView construct(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new NativeUint8ClampedArray(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData createArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
return new Uint8ClampedArrayData(buffer, byteOffset, length);
|
||||
public Uint8ClampedArrayData createArrayData(final ByteBuffer nb, final int start, final int end) {
|
||||
return new Uint8ClampedArrayData(nb, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Uint8ClampedArray";
|
||||
}
|
||||
};
|
||||
|
||||
private static final class Uint8ClampedArrayData extends ArrayDataImpl {
|
||||
private Uint8ClampedArrayData(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) {
|
||||
super(buffer, byteOffset, elementLength);
|
||||
private static final class Uint8ClampedArrayData extends TypedArrayData<ByteBuffer> {
|
||||
|
||||
private static final MethodHandle GET_ELEM = specialCall(MethodHandles.lookup(), Uint8ClampedArrayData.class, "getElem", int.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), Uint8ClampedArrayData.class, "setElem", void.class, int.class, int.class).methodHandle();
|
||||
private static final MethodHandle RINT = staticCall(MethodHandles.lookup(), Uint8ClampedArrayData.class, "rint", double.class, double.class).methodHandle();
|
||||
|
||||
private Uint8ClampedArrayData(final ByteBuffer nb, final int start, final int end) {
|
||||
super(((ByteBuffer)nb.position(start).limit(end)).slice(), end - start);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int byteIndex(final int index) {
|
||||
return index * BYTES_PER_ELEMENT + byteOffset;
|
||||
protected MethodHandle getGetElem() {
|
||||
return GET_ELEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getIntImpl(final int index) {
|
||||
return buffer.getByteArray()[byteIndex(index)] & 0xff;
|
||||
protected MethodHandle getSetElem() {
|
||||
return SET_ELEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int index, final int value) {
|
||||
final byte clamped;
|
||||
if ((value & 0xffff_ff00) == 0) {
|
||||
clamped = (byte) value;
|
||||
} else {
|
||||
clamped = value < 0 ? 0 : (byte)0xff;
|
||||
private int getElem(final int index) {
|
||||
try {
|
||||
return nb.get(index) & 0xff;
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
throw new ClassCastException(); //force relink - this works for unoptimistic too
|
||||
}
|
||||
buffer.getByteArray()[byteIndex(index)] = clamped;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int key, final double value) {
|
||||
setImpl(key, (int)Math.rint(value));
|
||||
public MethodHandle getElementSetter(final Class<?> elementType) {
|
||||
final MethodHandle setter = super.getElementSetter(elementType); //getContinuousElementSetter(getClass(), setElem(), elementType);
|
||||
if (setter != null && elementType == double.class) {
|
||||
return MH.filterArguments(setter, 2, RINT);
|
||||
}
|
||||
return setter;
|
||||
}
|
||||
|
||||
private void setElem(final int index, final int elem) {
|
||||
try {
|
||||
final byte clamped;
|
||||
if ((elem & 0xffff_ff00) == 0) {
|
||||
clamped = (byte)elem;
|
||||
} else {
|
||||
clamped = elem < 0 ? 0 : (byte)0xff;
|
||||
}
|
||||
nb.put(index, clamped);
|
||||
} catch (final IndexOutOfBoundsException e) {
|
||||
//swallow valid array indexes. it's ok.
|
||||
if (index < 0) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setImpl(final int key, final Object value) {
|
||||
setImpl(key, JSType.toNumber(value));
|
||||
public boolean isClamped() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUnsigned() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInt(int index) {
|
||||
return getElem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLong(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getDouble(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(int index) {
|
||||
return getInt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, Object value, boolean strict) {
|
||||
return set(index, JSType.toNumber(value), strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, int value, boolean strict) {
|
||||
setElem(index, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, long value, boolean strict) {
|
||||
return set(index, (int)value, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData set(int index, double value, boolean strict) {
|
||||
return set(index, rint(value), strict);
|
||||
}
|
||||
|
||||
private static double rint(double rint) {
|
||||
return (int)Math.rint(rint);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -110,18 +199,13 @@ public final class NativeUint8ClampedArray extends ArrayBufferView {
|
||||
*/
|
||||
@Constructor(arity = 1)
|
||||
public static Object constructor(final boolean newObj, final Object self, final Object... args) {
|
||||
return constructorImpl(args, FACTORY);
|
||||
return constructorImpl(newObj, args, FACTORY);
|
||||
}
|
||||
|
||||
NativeUint8ClampedArray(final NativeArrayBuffer buffer, final int byteOffset, final int length) {
|
||||
super(buffer, byteOffset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "Uint8ClampedArray";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Factory factory() {
|
||||
return FACTORY;
|
||||
|
@ -184,13 +184,21 @@ public class AccessorProperty extends Property {
|
||||
* @param primitiveGetter
|
||||
* @param primitiveSetter
|
||||
*/
|
||||
protected AccessorProperty(final String key, final int flags, final int slot, final MethodHandle primitiveGetter, final MethodHandle primitiveSetter, final MethodHandle objectGetter, final MethodHandle objectSetter) {
|
||||
protected AccessorProperty(
|
||||
final String key,
|
||||
final int flags,
|
||||
final int slot,
|
||||
final MethodHandle primitiveGetter,
|
||||
final MethodHandle primitiveSetter,
|
||||
final MethodHandle objectGetter,
|
||||
final MethodHandle objectSetter) {
|
||||
super(key, flags, slot);
|
||||
assert getClass() != AccessorProperty.class;
|
||||
this.primitiveGetter = primitiveGetter;
|
||||
this.primitiveSetter = primitiveSetter;
|
||||
this.objectGetter = objectGetter;
|
||||
this.objectSetter = objectSetter;
|
||||
// setCurrentType(OBJECT_FIELDS_ONLY ? Object.class : initialType);
|
||||
initializeType();
|
||||
}
|
||||
|
||||
@ -357,13 +365,15 @@ public class AccessorProperty extends Property {
|
||||
*/
|
||||
protected final void initializeType() {
|
||||
Class<?> initialType = null;
|
||||
if (OBJECT_FIELDS_ONLY || isAlwaysObject()) {
|
||||
if (OBJECT_FIELDS_ONLY) {
|
||||
initialType = Object.class;
|
||||
} else {
|
||||
if (!canBeUndefined()) { //todo if !canBeUndefined it means that we have an exact initialType
|
||||
initialType = int.class;
|
||||
}
|
||||
info(getKey(), canBeUndefined() ? " can be undefined" : " is always defined");
|
||||
if (shouldInstrument(getKey())) {
|
||||
info(getKey(), canBeUndefined() ? " can be undefined" : " is always defined");
|
||||
}
|
||||
}
|
||||
setCurrentType(initialType);
|
||||
}
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.JSType.digit;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
@ -38,7 +37,13 @@ import java.util.Locale;
|
||||
public final class GlobalFunctions {
|
||||
|
||||
/** Methodhandle to implementation of ECMA 15.1.2.2, parseInt */
|
||||
public static final MethodHandle PARSEINT = findOwnMH("parseInt", double.class, Object.class, Object.class, Object.class);
|
||||
public static final MethodHandle PARSEINT = findOwnMH("parseInt", double.class, Object.class, Object.class, Object.class);
|
||||
|
||||
/** Methodhandle (specialized) to implementation of ECMA 15.1.2.2, parseInt */
|
||||
public static final MethodHandle PARSEINT_OI = findOwnMH("parseInt", double.class, Object.class, Object.class, int.class);
|
||||
|
||||
/** Methodhandle (specialized) to implementation of ECMA 15.1.2.2, parseInt */
|
||||
public static final MethodHandle PARSEINT_O = findOwnMH("parseInt", double.class, Object.class, Object.class);
|
||||
|
||||
/** Methodhandle to implementation of ECMA 15.1.2.3, parseFloat */
|
||||
public static final MethodHandle PARSEFLOAT = findOwnMH("parseFloat", double.class, Object.class, Object.class);
|
||||
@ -78,19 +83,44 @@ public final class GlobalFunctions {
|
||||
/**
|
||||
* ECMA 15.1.2.2 parseInt implementation
|
||||
*
|
||||
* TODO: specialize
|
||||
* @param self self reference
|
||||
* @param string string to parse
|
||||
* @param rad radix
|
||||
*
|
||||
* @return numeric type representing string contents as an int
|
||||
*/
|
||||
public static double parseInt(final Object self, final Object string, final Object rad) {
|
||||
return parseIntInternal(JSType.trimLeft(JSType.toString(string)), JSType.toInt32(rad));
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.1.2.2 parseInt implementation specialized for int radix
|
||||
*
|
||||
* @param self self reference
|
||||
* @param string string to parse
|
||||
* @param rad radix
|
||||
*
|
||||
* @return numeric type representing string contents as an int (TODO: specialize for int case)
|
||||
* @return numeric type representing string contents as an int
|
||||
*/
|
||||
//TODO specialize
|
||||
public static double parseInt(final Object self, final Object string, final Object rad) {
|
||||
final String str = JSType.trimLeft(JSType.toString(string));
|
||||
final int length = str.length();
|
||||
int radix = JSType.toInt32(rad);
|
||||
public static double parseInt(final Object self, final Object string, final int rad) {
|
||||
return parseIntInternal(JSType.trimLeft(JSType.toString(string)), rad);
|
||||
}
|
||||
|
||||
/**
|
||||
* ECMA 15.1.2.2 parseInt implementation specialized for no radix argument
|
||||
*
|
||||
* @param self self reference
|
||||
* @param string string to parse
|
||||
*
|
||||
* @return numeric type representing string contents as an int
|
||||
*/
|
||||
public static double parseInt(final Object self, final Object string) {
|
||||
return parseIntInternal(JSType.trimLeft(JSType.toString(string)), 0);
|
||||
}
|
||||
|
||||
private static double parseIntInternal(final String str, final int rad) {
|
||||
final int length = str.length();
|
||||
int radix = rad;
|
||||
|
||||
// empty string is not valid
|
||||
if (length == 0) {
|
||||
@ -142,7 +172,7 @@ public final class GlobalFunctions {
|
||||
// we should see atleast one valid digit
|
||||
boolean entered = false;
|
||||
while (idx < length) {
|
||||
digit = digit(str.charAt(idx++), radix, true);
|
||||
digit = fastDigit(str.charAt(idx++), radix);
|
||||
if (digit < 0) {
|
||||
break;
|
||||
}
|
||||
@ -456,6 +486,20 @@ loop:
|
||||
return ScriptRuntime.UNDEFINED;
|
||||
}
|
||||
|
||||
private static int fastDigit(int ch, int radix) {
|
||||
int n = -1;
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
n = ch - '0';
|
||||
} else if (radix > 10) {
|
||||
if (ch >= 'a' && ch <= 'z') {
|
||||
n = ch - 'a' + 10;
|
||||
} else if (ch >= 'A' && ch <= 'Z') {
|
||||
n = ch - 'A' + 10;
|
||||
}
|
||||
}
|
||||
return n < radix ? n : -1;
|
||||
}
|
||||
|
||||
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||
return MH.findStatic(MethodHandles.lookup(), GlobalFunctions.class, name, MH.type(rtype, types));
|
||||
}
|
||||
|
@ -205,8 +205,8 @@ public final class NativeJavaPackage extends ScriptObject {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object invokeNoSuchProperty(final String name, final int programPoint) {
|
||||
final Object retval = createProperty(name);
|
||||
protected Object invokeNoSuchProperty(final String key, final int programPoint) {
|
||||
final Object retval = createProperty(key);
|
||||
if (isValid(programPoint)) {
|
||||
throw new UnwarrantedOptimismException(retval, programPoint);
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.Objects;
|
||||
import jdk.nashorn.internal.codegen.ObjectClassGenerator;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
|
||||
/**
|
||||
* This is the abstract superclass representing a JavaScript Property.
|
||||
@ -64,7 +63,7 @@ public abstract class Property {
|
||||
/** ECMA 8.6.1 - Is this property not configurable? */
|
||||
public static final int NOT_CONFIGURABLE = 1 << 2;
|
||||
|
||||
private static final int MODIFY_MASK = (NOT_WRITABLE | NOT_ENUMERABLE | NOT_CONFIGURABLE);
|
||||
private static final int MODIFY_MASK = NOT_WRITABLE | NOT_ENUMERABLE | NOT_CONFIGURABLE;
|
||||
|
||||
/** Is this a function parameter? */
|
||||
public static final int IS_PARAMETER = 1 << 3;
|
||||
@ -72,14 +71,11 @@ public abstract class Property {
|
||||
/** Is parameter accessed thru arguments? */
|
||||
public static final int HAS_ARGUMENTS = 1 << 4;
|
||||
|
||||
/** Is this property always represented as an Object? See {@link ObjectClassGenerator} and dual fields flag. */
|
||||
public static final int IS_ALWAYS_OBJECT = 1 << 5;
|
||||
|
||||
/** Can this property be undefined? */
|
||||
public static final int CAN_BE_UNDEFINED = 1 << 6;
|
||||
public static final int CAN_BE_UNDEFINED = 1 << 5;
|
||||
|
||||
/** Is this a function declaration property ? */
|
||||
public static final int IS_FUNCTION_DECLARATION = 1 << 7;
|
||||
public static final int IS_FUNCTION_DECLARATION = 1 << 6;
|
||||
|
||||
/**
|
||||
* Is this is a primitive field given to us by Nasgen, i.e.
|
||||
@ -87,7 +83,7 @@ public abstract class Property {
|
||||
* is narrower than object, e.g. Math.PI which is declared
|
||||
* as a double
|
||||
*/
|
||||
public static final int IS_NASGEN_PRIMITIVE = 1 << 8;
|
||||
public static final int IS_NASGEN_PRIMITIVE = 1 << 7;
|
||||
|
||||
/** Property key. */
|
||||
private final String key;
|
||||
@ -289,6 +285,16 @@ public abstract class Property {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a flag is set for a property
|
||||
* @param property property
|
||||
* @param flag flag to check
|
||||
* @return true if flag is set
|
||||
*/
|
||||
public static boolean checkFlag(final Property property, final int flag) {
|
||||
return (property.getFlags() & flag) == flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the flags for this property
|
||||
* @return property flags
|
||||
@ -608,17 +614,6 @@ public abstract class Property {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this Property is ever used as anything but an Object. If this is used only
|
||||
* as an object, dual fields mode need not even try to represent it as a primitive at any
|
||||
* callsite, saving map rewrites for performance.
|
||||
*
|
||||
* @return true if representation should always be an object field
|
||||
*/
|
||||
public boolean isAlwaysObject() {
|
||||
return (flags & IS_ALWAYS_OBJECT) == IS_ALWAYS_OBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this property can be primitive. This is a conservative
|
||||
* analysis result, so {@code true} might mean that it can still be
|
||||
|
@ -303,7 +303,7 @@ public final class PropertyHashMap implements Map <String, Property> {
|
||||
* @return The bin index.
|
||||
*/
|
||||
private static int binIndex(final Element[] bins, final String key) {
|
||||
return key.hashCode() & (bins.length - 1);
|
||||
return key.hashCode() & bins.length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,7 +315,7 @@ public final class PropertyHashMap implements Map <String, Property> {
|
||||
*/
|
||||
private static int binsNeeded(final int n) {
|
||||
// 50% padding
|
||||
return 1 << (32 - Integer.numberOfLeadingZeros((n + (n >>> 1)) | (INITIAL_BINS - 1)));
|
||||
return 1 << 32 - Integer.numberOfLeadingZeros(n + (n >>> 1) | INITIAL_BINS - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -442,15 +442,11 @@ public final class PropertyHashMap implements Map <String, Property> {
|
||||
if (bins != null) {
|
||||
final int binIndex = binIndex(bins, key);
|
||||
Element bin = bins[binIndex];
|
||||
// System.err.println("oldBin = " + bin);
|
||||
bin = replaceInList(bin, key, property);
|
||||
// System.err.println("newBin = " + bin);
|
||||
bins[binIndex] = bin;
|
||||
}
|
||||
Element newList = list;
|
||||
//System.err.println("oldList = " + newList);
|
||||
newList = replaceInList(newList, key, property);
|
||||
//System.err.println("newList = " + newList);
|
||||
return new PropertyHashMap(size, bins, newList);
|
||||
}
|
||||
|
||||
|
@ -118,6 +118,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData {
|
||||
* @param allocatorClassName name of our allocator class, will be looked up dynamically if used as a constructor
|
||||
* @param allocatorMap allocator map to seed instances with, when constructing
|
||||
* @param nestedFunctions nested function map
|
||||
* @param sourceURL source URL
|
||||
*/
|
||||
public RecompilableScriptFunctionData(
|
||||
final FunctionNode functionNode,
|
||||
|
@ -469,6 +469,7 @@ public abstract class ScriptFunction extends ScriptObject {
|
||||
final MethodType type = desc.getMethodType();
|
||||
assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
|
||||
final GuardedInvocation bestCtorInv = data.getBestConstructor(type);
|
||||
//TODO - ClassCastException
|
||||
return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this), bestCtorInv.getSwitchPoint());
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,7 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
|
||||
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
|
||||
@ -234,7 +235,6 @@ public abstract class ScriptFunctionData {
|
||||
|
||||
private MethodHandle createGenericInvoker() {
|
||||
return makeGenericMethod(getGeneric().createComposableInvoker());
|
||||
// TODO hannes return code.generic().getInvoker();
|
||||
}
|
||||
|
||||
final MethodHandle getGenericConstructor() {
|
||||
@ -298,7 +298,7 @@ public abstract class ScriptFunctionData {
|
||||
final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(), getGenericConstructor());
|
||||
boundList.add(bind(bindTarget, fn, self, allArgs));
|
||||
|
||||
ScriptFunctionData boundData = new FinalScriptFunctionData(name, Math.max(0, getArity() - length), boundList, isStrict(), isBuiltin(), isConstructor(), isVariableArity());
|
||||
final ScriptFunctionData boundData = new FinalScriptFunctionData(name, Math.max(0, getArity() - length), boundList, isStrict(), isBuiltin(), isConstructor(), isVariableArity());
|
||||
return boundData;
|
||||
}
|
||||
|
||||
@ -381,7 +381,7 @@ public abstract class ScriptFunctionData {
|
||||
} else {
|
||||
// If target is already bound, insert additional bound arguments after "this" argument, at position 1.
|
||||
final int argInsertPos = isTargetBound ? 1 : 0;
|
||||
final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : (needsCallee ? 2 : 1)))];
|
||||
final Object[] boundArgs = new Object[Math.min(originalInvoker.type().parameterCount() - argInsertPos, args.length + (isTargetBound ? 0 : needsCallee ? 2 : 1))];
|
||||
int next = 0;
|
||||
if (!isTargetBound) {
|
||||
if (needsCallee) {
|
||||
@ -463,7 +463,7 @@ public abstract class ScriptFunctionData {
|
||||
*/
|
||||
private static MethodHandle makeGenericMethod(final MethodHandle mh) {
|
||||
final MethodType type = mh.type();
|
||||
MethodType newType = makeGenericType(type);
|
||||
final MethodType newType = makeGenericType(type);
|
||||
return type.equals(newType) ? mh : mh.asType(newType);
|
||||
}
|
||||
|
||||
@ -681,7 +681,7 @@ public abstract class ScriptFunctionData {
|
||||
}
|
||||
|
||||
final Class<?> param0 = type.parameterType(0);
|
||||
return param0 == ScriptFunction.class || (param0 == boolean.class && length > 1 && type.parameterType(1) == ScriptFunction.class);
|
||||
return param0 == ScriptFunction.class || param0 == boolean.class && length > 1 && type.parameterType(1) == ScriptFunction.class;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -26,7 +26,6 @@
|
||||
package jdk.nashorn.internal.runtime;
|
||||
|
||||
import java.security.CodeSource;
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
/**
|
||||
* Responsible for loading script generated classes.
|
||||
|
@ -45,6 +45,7 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
|
||||
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.getArrayIndex;
|
||||
import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
|
||||
import static jdk.nashorn.internal.runtime.linker.NashornGuards.explicitInstanceOfCheck;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -60,6 +61,7 @@ import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
@ -105,19 +107,19 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
static final String NO_SUCH_PROPERTY_NAME = "__noSuchProperty__";
|
||||
|
||||
/** Per ScriptObject flag - is this a scope object? */
|
||||
public static final int IS_SCOPE = 0b0000_0001;
|
||||
public static final int IS_SCOPE = 1 << 0;
|
||||
|
||||
/** Per ScriptObject flag - is this an array object? */
|
||||
public static final int IS_ARRAY = 0b0000_0010;
|
||||
public static final int IS_ARRAY = 1 << 1;
|
||||
|
||||
/** Per ScriptObject flag - is this an arguments object? */
|
||||
public static final int IS_ARGUMENTS = 0b0000_0100;
|
||||
public static final int IS_ARGUMENTS = 1 << 2;
|
||||
|
||||
/** Is this a prototype PropertyMap? */
|
||||
public static final int IS_PROTOTYPE = 0b0000_1000;
|
||||
public static final int IS_PROTOTYPE = 1 << 3;
|
||||
|
||||
/** Is length property not-writable? */
|
||||
public static final int IS_LENGTH_NOT_WRITABLE = 0b0001_0000;
|
||||
public static final int IS_LENGTH_NOT_WRITABLE = 1 << 4;
|
||||
|
||||
/**
|
||||
* Spill growth rate - by how many elements does {@link ScriptObject#primitiveSpill} and
|
||||
@ -252,7 +254,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
}
|
||||
|
||||
private static int alignUp(final int size, final int alignment) {
|
||||
return (((size) + ((alignment) - 1)) & ~((alignment) - 1));
|
||||
return size + alignment - 1 & ~(alignment - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -448,10 +450,10 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
|
||||
if (property instanceof UserAccessorProperty) {
|
||||
return global.newAccessorDescriptor(
|
||||
(get != null) ?
|
||||
get != null ?
|
||||
get :
|
||||
UNDEFINED,
|
||||
(set != null) ?
|
||||
set != null ?
|
||||
set :
|
||||
UNDEFINED,
|
||||
configurable,
|
||||
@ -921,7 +923,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
final int slot = spillLength;
|
||||
ensureSpillSize(spillLength); //arguments=slot0, caller=slot0
|
||||
objectSpill[slot] = new UserAccessorProperty.Accessors(getter, setter);
|
||||
PropertyMap oldMap = getMap();
|
||||
final PropertyMap oldMap = getMap();
|
||||
Property newProperty;
|
||||
PropertyMap newMap;
|
||||
do {
|
||||
@ -1374,16 +1376,16 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
*/
|
||||
public ScriptObject preventExtensions() {
|
||||
PropertyMap oldMap = getMap();
|
||||
|
||||
while (true) {
|
||||
final PropertyMap newMap = getMap().preventExtensions();
|
||||
|
||||
if (!compareAndSetMap(oldMap, newMap)) {
|
||||
oldMap = getMap();
|
||||
} else {
|
||||
return this;
|
||||
}
|
||||
while (!compareAndSetMap(oldMap, getMap().preventExtensions())) {
|
||||
oldMap = getMap();
|
||||
}
|
||||
|
||||
//invalidate any fast array setters
|
||||
final ArrayData array = getArray();
|
||||
if (array != null) {
|
||||
array.invalidateSetters();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1394,7 +1396,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
* @return true if array
|
||||
*/
|
||||
public static boolean isArray(final Object obj) {
|
||||
return (obj instanceof ScriptObject) && ((ScriptObject)obj).isArray();
|
||||
return obj instanceof ScriptObject && ((ScriptObject)obj).isArray();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1857,17 +1859,26 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
* @return GuardedInvocation to be invoked at call site.
|
||||
*/
|
||||
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
|
||||
if (request.isCallSiteUnstable() || hasWithScope()) {
|
||||
return findMegaMorphicGetMethod(desc, name, "getMethod".equals(operator));
|
||||
}
|
||||
|
||||
final FindProperty find = findProperty(name, true);
|
||||
MethodHandle methodHandle;
|
||||
MethodHandle mh;
|
||||
|
||||
if (find == null) {
|
||||
if (PROTO_PROPERTY_NAME.equals(name)) {
|
||||
return new GuardedInvocation(GETPROTO, null, null, ClassCastException.class);
|
||||
return new GuardedInvocation(
|
||||
GETPROTO,
|
||||
explicitInstanceOfCheck ?
|
||||
NashornGuards.getScriptObjectGuard() :
|
||||
null,
|
||||
null,
|
||||
explicitInstanceOfCheck ?
|
||||
null : ClassCastException.class);
|
||||
}
|
||||
|
||||
switch (operator) {
|
||||
@ -1876,42 +1887,57 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
case "getMethod":
|
||||
return noSuchMethod(desc, request);
|
||||
case "getElem":
|
||||
return createEmptyGetter(desc, name);
|
||||
return createEmptyGetter(desc, explicitInstanceOfCheck, name);
|
||||
default:
|
||||
throw new AssertionError(); // never invoked with any other operation
|
||||
throw new AssertionError(operator); // never invoked with any other operation
|
||||
}
|
||||
}
|
||||
|
||||
final Class<?> returnType = desc.getMethodType().returnType();
|
||||
final Property property = find.getProperty();
|
||||
final Property property = find.getProperty();
|
||||
|
||||
final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ?
|
||||
NashornCallSiteDescriptor.getProgramPoint(desc) :
|
||||
UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
methodHandle = find.getGetter(returnType, programPoint);
|
||||
|
||||
mh = find.getGetter(returnType, programPoint);
|
||||
//we never need a guard if noGuard is set
|
||||
final boolean noGuard = OBJECT_FIELDS_ONLY && NashornCallSiteDescriptor.isFastScope(desc) && !property.canChangeType();
|
||||
// getMap() is fine as we have the prototype switchpoint depending on where the property was found
|
||||
final MethodHandle guard = noGuard ? null : NashornGuards.getMapGuard(getMap());
|
||||
|
||||
if (methodHandle != null) {
|
||||
assert methodHandle.type().returnType().equals(returnType) : "returntype mismatch for getter " + methodHandle.type().returnType() + " != " + returnType;
|
||||
if (find.isSelf()) {
|
||||
return new GuardedInvocation(methodHandle, guard, null, noGuard ? null : ClassCastException.class);
|
||||
}
|
||||
|
||||
if (!property.hasGetterFunction(find.getOwner())) {
|
||||
// If not a scope bind to actual prototype as changing prototype will change the property map.
|
||||
// For scopes we install a filter that replaces the self object with the prototype owning the property.
|
||||
methodHandle = isScope() ?
|
||||
addProtoFilter(methodHandle, find.getProtoChainLength()) :
|
||||
bindTo(methodHandle, find.getOwner());
|
||||
}
|
||||
return new GuardedInvocation(methodHandle, guard, noGuard ? null : getMap().getProtoGetSwitchPoint(proto, name), noGuard ? null : ClassCastException.class);
|
||||
final MethodHandle guard;
|
||||
final Class<? extends Throwable> exception;
|
||||
if (noGuard) {
|
||||
guard = null;
|
||||
exception = null;
|
||||
} else {
|
||||
guard = NashornGuards.getMapGuard(map, explicitInstanceOfCheck);
|
||||
exception = explicitInstanceOfCheck ? null : ClassCastException.class;
|
||||
}
|
||||
|
||||
assert !NashornCallSiteDescriptor.isFastScope(desc);
|
||||
return new GuardedInvocation(Lookup.emptyGetter(returnType), guard, getMap().getProtoGetSwitchPoint(proto, name), ClassCastException.class);
|
||||
assert noGuard == (guard == null);
|
||||
|
||||
if (mh == null) {
|
||||
mh = Lookup.emptyGetter(returnType);
|
||||
} else {
|
||||
assert mh.type().returnType().equals(returnType) : "returntype mismatch for getter " + mh.type().returnType() + " != " + returnType;
|
||||
if (find.isSelf()) {
|
||||
return new GuardedInvocation(mh, guard, null, exception);
|
||||
}
|
||||
|
||||
if (!property.hasGetterFunction(find.getOwner())) {
|
||||
// If not a scope bind to actual prototype as changing prototype will change the property map.
|
||||
// For scopes we install a filter that replaces the self object with the prototype owning the property.
|
||||
mh = isScope() ? addProtoFilter(mh, find.getProtoChainLength()) : bindTo(mh, find.getOwner());
|
||||
}
|
||||
}
|
||||
|
||||
assert OBJECT_FIELDS_ONLY || guard != null : "we always need a map guard here";
|
||||
|
||||
return new GuardedInvocation(
|
||||
mh,
|
||||
guard,
|
||||
getMap().getProtoGetSwitchPoint(proto, name),
|
||||
exception);
|
||||
}
|
||||
|
||||
private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
|
||||
@ -1939,10 +1965,11 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
* @return GuardedInvocation to be invoked at call site.
|
||||
*/
|
||||
protected GuardedInvocation findGetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
final MethodType callType = desc.getMethodType();
|
||||
final Class<?> returnType = callType.returnType();
|
||||
final Class<?> returnClass = returnType.isPrimitive() ? returnType : Object.class;
|
||||
final Class<?> keyClass = callType.parameterType(1);
|
||||
final MethodType callType = desc.getMethodType();
|
||||
final Class<?> returnType = callType.returnType();
|
||||
final Class<?> returnClass = returnType.isPrimitive() ? returnType : Object.class;
|
||||
final Class<?> keyClass = callType.parameterType(1);
|
||||
final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
|
||||
|
||||
final String name;
|
||||
if (returnClass.isPrimitive()) {
|
||||
@ -1954,7 +1981,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
}
|
||||
|
||||
final MethodHandle mh = findGetIndexMethodHandle(returnClass, name, keyClass, desc);
|
||||
return new GuardedInvocation(mh, null, null, ClassCastException.class);
|
||||
return new GuardedInvocation(mh, NashornGuards.getScriptObjectGuard(explicitInstanceOfCheck), null, explicitInstanceOfCheck ? null : ClassCastException.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1965,7 +1992,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
* @param desc call site descriptor
|
||||
* @return method handle for getter
|
||||
*/
|
||||
protected MethodHandle findGetIndexMethodHandle(final Class<?> returnType, String name, final Class<?> elementType, final CallSiteDescriptor desc) {
|
||||
protected MethodHandle findGetIndexMethodHandle(final Class<?> returnType, final String name, final Class<?> elementType, final CallSiteDescriptor desc) {
|
||||
if (!returnType.isPrimitive()) {
|
||||
return findOwnMH_V(getClass(), name, returnType, elementType);
|
||||
}
|
||||
@ -1987,12 +2014,14 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
* @return GuardedInvocation to be invoked at call site.
|
||||
*/
|
||||
protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
if (request.isCallSiteUnstable() || hasWithScope()) {
|
||||
return findMegaMorphicSetMethod(desc, name);
|
||||
}
|
||||
|
||||
final boolean scope = isScope();
|
||||
final boolean scope = isScope();
|
||||
final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
|
||||
|
||||
/*
|
||||
* If doing property set on a scope object, we should stop proto search on the first
|
||||
* non-scope object. Without this, for example, when assigning "toString" on global scope,
|
||||
@ -2006,7 +2035,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
if (!scope && find != null && find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
|
||||
// We should still check if inherited data property is not writable
|
||||
if (isExtensible() && !find.getProperty().isWritable()) {
|
||||
return createEmptySetMethod(desc, "property.not.writable", false);
|
||||
return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", false);
|
||||
}
|
||||
// Otherwise, forget the found property
|
||||
find = null;
|
||||
@ -2015,27 +2044,35 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
if (find != null) {
|
||||
if (!find.getProperty().isWritable()) {
|
||||
// Existing, non-writable property
|
||||
return createEmptySetMethod(desc, "property.not.writable", true);
|
||||
return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true);
|
||||
}
|
||||
} else {
|
||||
if (PROTO_PROPERTY_NAME.equals(name)) {
|
||||
return new GuardedInvocation(SETPROTOCHECK, null, null, ClassCastException.class);
|
||||
} else if (! isExtensible()) {
|
||||
return createEmptySetMethod(desc, "object.non.extensible", false);
|
||||
return new GuardedInvocation(
|
||||
SETPROTOCHECK,
|
||||
NashornGuards.getScriptObjectGuard(explicitInstanceOfCheck),
|
||||
null,
|
||||
explicitInstanceOfCheck ? null : ClassCastException.class);
|
||||
} else if (!isExtensible()) {
|
||||
return createEmptySetMethod(desc, explicitInstanceOfCheck, "object.non.extensible", false);
|
||||
}
|
||||
}
|
||||
|
||||
return new SetMethodCreator(this, find, desc).createGuardedInvocation();
|
||||
return new SetMethodCreator(this, find, desc, explicitInstanceOfCheck).createGuardedInvocation();
|
||||
}
|
||||
|
||||
private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, String strictErrorMessage, boolean canBeFastScope) {
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
if (NashornCallSiteDescriptor.isStrict(desc)) {
|
||||
throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString((this)));
|
||||
}
|
||||
assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
|
||||
final PropertyMap myMap = getMap();
|
||||
return new GuardedInvocation(Lookup.EMPTY_SETTER, NashornGuards.getMapGuard(myMap), myMap.getProtoGetSwitchPoint(proto, name), ClassCastException.class);
|
||||
private GuardedInvocation createEmptySetMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String strictErrorMessage, final boolean canBeFastScope) {
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
if (NashornCallSiteDescriptor.isStrict(desc)) {
|
||||
throw typeError(strictErrorMessage, name, ScriptRuntime.safeToString(this));
|
||||
}
|
||||
assert canBeFastScope || !NashornCallSiteDescriptor.isFastScope(desc);
|
||||
final PropertyMap myMap = getMap();
|
||||
return new GuardedInvocation(
|
||||
Lookup.EMPTY_SETTER,
|
||||
NashornGuards.getMapGuard(myMap, explicitInstanceOfCheck),
|
||||
myMap.getProtoGetSwitchPoint(proto, name),
|
||||
explicitInstanceOfCheck ? null : ClassCastException.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ -2052,8 +2089,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
}
|
||||
|
||||
private GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
|
||||
final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
|
||||
final GuardedInvocation inv = findSetIndexMethod(getClass(), type, NashornCallSiteDescriptor.isStrict(desc));
|
||||
final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
|
||||
//never bother with ClassCastExceptionGuard for megamorphic callsites
|
||||
final GuardedInvocation inv = findSetIndexMethod(getClass(), false, type, NashornCallSiteDescriptor.isStrict(desc));
|
||||
return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
|
||||
}
|
||||
|
||||
@ -2067,7 +2105,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
* @return GuardedInvocation to be invoked at call site.
|
||||
*/
|
||||
protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
|
||||
return findSetIndexMethod(getClass(), desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
|
||||
return findSetIndexMethod(getClass(), explicitInstanceOfCheck(desc, request), desc.getMethodType(), NashornCallSiteDescriptor.isStrict(desc));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2078,15 +2116,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
*
|
||||
* @return GuardedInvocation to be invoked at call site.
|
||||
*/
|
||||
private static GuardedInvocation findSetIndexMethod(final Class<? extends ScriptObject> clazz, final MethodType callType, final boolean isStrict) {
|
||||
private static GuardedInvocation findSetIndexMethod(final Class<? extends ScriptObject> clazz, final boolean explicitInstanceOfCheck, final MethodType callType, final boolean isStrict) {
|
||||
assert callType.parameterCount() == 3;
|
||||
final Class<?> keyClass = callType.parameterType(1);
|
||||
final Class<?> valueClass = callType.parameterType(2);
|
||||
final Class<?> keyClass = callType.parameterType(1);
|
||||
final Class<?> valueClass = callType.parameterType(2);
|
||||
|
||||
MethodHandle methodHandle = findOwnMH_V(clazz, "set", void.class, keyClass, valueClass, boolean.class);
|
||||
methodHandle = MH.insertArguments(methodHandle, 3, isStrict);
|
||||
|
||||
return new GuardedInvocation(methodHandle, null, null, ClassCastException.class);
|
||||
return new GuardedInvocation(methodHandle, NashornGuards.getScriptObjectGuard(explicitInstanceOfCheck), null, explicitInstanceOfCheck ? null : ClassCastException.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2104,17 +2142,26 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
return noSuchProperty(desc, request);
|
||||
}
|
||||
|
||||
final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
|
||||
|
||||
final Object value = getObjectValue(find);
|
||||
if (! (value instanceof ScriptFunction)) {
|
||||
return createEmptyGetter(desc, name);
|
||||
if (!(value instanceof ScriptFunction)) {
|
||||
return createEmptyGetter(desc, explicitInstanceOfCheck, name);
|
||||
}
|
||||
|
||||
final ScriptFunction func = (ScriptFunction)value;
|
||||
final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
|
||||
final Object thiz = scopeCall && func.isStrict() ? ScriptRuntime.UNDEFINED : this;
|
||||
// TODO: It'd be awesome if we could bind "name" without binding "this".
|
||||
return new GuardedInvocation(MH.dropArguments(MH.constant(ScriptFunction.class,
|
||||
func.makeBoundFunction(thiz, new Object[] { name })), 0, Object.class),
|
||||
NashornGuards.getMapGuard(getMap()), null, ClassCastException.class);
|
||||
return new GuardedInvocation(
|
||||
MH.dropArguments(
|
||||
MH.constant(
|
||||
ScriptFunction.class,
|
||||
func.makeBoundFunction(thiz, new Object[] { name })),
|
||||
0,
|
||||
Object.class),
|
||||
NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck),
|
||||
null,
|
||||
explicitInstanceOfCheck ? null : ClassCastException.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2125,9 +2172,9 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
*/
|
||||
@SuppressWarnings("null")
|
||||
public GuardedInvocation noSuchProperty(final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
|
||||
final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
|
||||
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
final FindProperty find = findProperty(NO_SUCH_PROPERTY_NAME, true);
|
||||
final boolean scopeAccess = isScope() && NashornCallSiteDescriptor.isScope(desc);
|
||||
|
||||
if (find != null) {
|
||||
final Object value = getObjectValue(find);
|
||||
@ -2145,9 +2192,15 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
}
|
||||
return new GuardedInvocation(
|
||||
methodHandle,
|
||||
getKnownFunctionPropertyGuard(getMap(), find.getGetter(Object.class, INVALID_PROGRAM_POINT), find.getOwner(), func),
|
||||
//TODO this always does a scriptobject check
|
||||
getKnownFunctionPropertyGuard(
|
||||
getMap(),
|
||||
find.getGetter(Object.class, INVALID_PROGRAM_POINT),
|
||||
find.getOwner(),
|
||||
func),
|
||||
find.isInherited() ? getMap().getProtoGetSwitchPoint(proto, NO_SUCH_PROPERTY_NAME) : null,
|
||||
ClassCastException.class);
|
||||
//TODO this doesn't need a ClassCastException as guard always checks script object
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2155,12 +2208,13 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
throw referenceError("not.defined", name);
|
||||
}
|
||||
|
||||
return createEmptyGetter(desc, name);
|
||||
return createEmptyGetter(desc, explicitInstanceOfCheck(desc, request), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke fall back if a property is not found.
|
||||
* @param name Name of property.
|
||||
* @param programPoint program point
|
||||
* @return Result from call.
|
||||
*/
|
||||
protected Object invokeNoSuchProperty(final String name, final int programPoint) {
|
||||
@ -2204,11 +2258,19 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
return ((ScriptFunction)value).makeBoundFunction(this, new Object[] {name});
|
||||
}
|
||||
|
||||
private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final String name) {
|
||||
if(NashornCallSiteDescriptor.isOptimistic(desc)) {
|
||||
private GuardedInvocation createEmptyGetter(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final String name) {
|
||||
if (NashornCallSiteDescriptor.isOptimistic(desc)) {
|
||||
throw new UnwarrantedOptimismException(UNDEFINED, NashornCallSiteDescriptor.getProgramPoint(desc), Type.OBJECT);
|
||||
}
|
||||
return new GuardedInvocation(Lookup.emptyGetter(desc.getMethodType().returnType()), NashornGuards.getMapGuard(getMap()), getMap().getProtoGetSwitchPoint(proto, name), ClassCastException.class);
|
||||
|
||||
return new GuardedInvocation(
|
||||
Lookup.emptyGetter(
|
||||
desc.getMethodType().returnType()),
|
||||
NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck),
|
||||
getMap().getProtoGetSwitchPoint(proto, name),
|
||||
explicitInstanceOfCheck ?
|
||||
null :
|
||||
ClassCastException.class);
|
||||
}
|
||||
|
||||
private abstract static class ScriptObjectIterator <T extends Object> implements Iterator<T> {
|
||||
@ -2284,8 +2346,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
*/
|
||||
private Property addSpillProperty(final String key, final int propertyFlags, final Object value, final boolean hasInitialValue) {
|
||||
final PropertyMap propertyMap = getMap();
|
||||
final int fieldCount = propertyMap.getFieldCount();
|
||||
final int fieldMax = propertyMap.getFieldMaximum();
|
||||
final int fieldCount = propertyMap.getFieldCount();
|
||||
final int fieldMax = propertyMap.getFieldMaximum();
|
||||
|
||||
Property property;
|
||||
if (fieldCount < fieldMax) {
|
||||
@ -2354,8 +2416,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
final int callCount = callType.parameterCount();
|
||||
|
||||
final boolean isCalleeVarArg = parameterCount > 0 && methodType.parameterType(parameterCount - 1).isArray();
|
||||
final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : (callCount > 0 &&
|
||||
callType.parameterType(callCount - 1).isArray());
|
||||
final boolean isCallerVarArg = callerVarArg != null ? callerVarArg.booleanValue() : callCount > 0 &&
|
||||
callType.parameterType(callCount - 1).isArray();
|
||||
|
||||
if (isCalleeVarArg) {
|
||||
return isCallerVarArg ?
|
||||
@ -2443,8 +2505,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
|
||||
if (newLength > arrayLength) {
|
||||
setArray(getArray().ensure(newLength - 1));
|
||||
if (getArray().canDelete(arrayLength, (newLength - 1), false)) {
|
||||
setArray(getArray().delete(arrayLength, (newLength - 1)));
|
||||
if (getArray().canDelete(arrayLength, newLength - 1, false)) {
|
||||
setArray(getArray().delete(arrayLength, newLength - 1));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -2869,8 +2931,8 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
private void doesNotHaveEnsureDelete(final long longIndex, final long oldLength, final boolean strict) {
|
||||
if (longIndex > oldLength) {
|
||||
ArrayData array = getArray();
|
||||
if (array.canDelete(oldLength, (longIndex - 1), strict)) {
|
||||
array = array.delete(oldLength, (longIndex - 1));
|
||||
if (array.canDelete(oldLength, longIndex - 1, strict)) {
|
||||
array = array.delete(oldLength, longIndex - 1);
|
||||
}
|
||||
setArray(array);
|
||||
}
|
||||
@ -3306,7 +3368,7 @@ public abstract class ScriptObject extends PropertyListenerManager implements Pr
|
||||
}
|
||||
|
||||
private boolean hasOwnArrayProperty(final int index) {
|
||||
return getArray().has(index) || (getMap().containsArrayKeys() && hasProperty(ArrayIndex.toKey(index), false));
|
||||
return getArray().has(index) || getMap().containsArrayKeys() && hasProperty(ArrayIndex.toKey(index), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -36,7 +36,6 @@ import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
|
||||
import jdk.nashorn.internal.runtime.linker.NashornGuards;
|
||||
|
||||
|
||||
/**
|
||||
* Instances of this class are quite ephemeral; they only exist for the duration of an invocation of
|
||||
* {@link ScriptObject#findSetMethod(CallSiteDescriptor, jdk.internal.dynalink.linker.LinkRequest)} and
|
||||
@ -49,6 +48,7 @@ final class SetMethodCreator {
|
||||
private final FindProperty find;
|
||||
private final CallSiteDescriptor desc;
|
||||
private final Class<?> type;
|
||||
private final boolean explicitInstanceOfCheck;
|
||||
|
||||
/**
|
||||
* Creates a new property setter method creator.
|
||||
@ -57,12 +57,13 @@ final class SetMethodCreator {
|
||||
* want to create a setter for. Can be null if the property does not yet exist on the object.
|
||||
* @param desc the descriptor of the call site that triggered the property setter lookup
|
||||
*/
|
||||
SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc) {
|
||||
SetMethodCreator(final ScriptObject sobj, final FindProperty find, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck) {
|
||||
this.sobj = sobj;
|
||||
this.map = sobj.getMap();
|
||||
this.find = find;
|
||||
this.desc = desc;
|
||||
this.type = desc.getMethodType().parameterType(1);
|
||||
this.explicitInstanceOfCheck = explicitInstanceOfCheck;
|
||||
|
||||
}
|
||||
|
||||
@ -118,7 +119,7 @@ final class SetMethodCreator {
|
||||
}
|
||||
|
||||
private MethodHandle getGuard() {
|
||||
return needsNoGuard() ? null : NashornGuards.getMapGuard(getMap());
|
||||
return needsNoGuard() ? null : NashornGuards.getMapGuard(getMap(), explicitInstanceOfCheck);
|
||||
}
|
||||
|
||||
private boolean needsNoGuard() {
|
||||
|
@ -27,6 +27,7 @@ package jdk.nashorn.internal.runtime;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.OBJECT_FIELDS_ONLY;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
@ -156,6 +157,7 @@ public class SpillProperty extends AccessorProperty {
|
||||
* @param flags the property flags
|
||||
* @param slot spill slot
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public SpillProperty(final String key, final int flags, final int slot) {
|
||||
super(key, flags, slot, primitiveGetter(slot), primitiveSetter(slot), objectGetter(slot), objectSetter(slot));
|
||||
assert !OBJECT_FIELDS_ONLY || getCurrentType() == Object.class;
|
||||
|
@ -31,6 +31,7 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.SwitchPoint;
|
||||
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
@ -66,7 +67,6 @@ public final class WithObject extends ScriptObject implements Scope {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete a property based on a key.
|
||||
* @param key Any valid JavaScript value.
|
||||
@ -99,7 +99,7 @@ public final class WithObject extends ScriptObject implements Scope {
|
||||
|
||||
final boolean isNamedOperation;
|
||||
final String name;
|
||||
if(desc.getNameTokenCount() > 2) {
|
||||
if (desc.getNameTokenCount() > 2) {
|
||||
isNamedOperation = true;
|
||||
name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
|
||||
} else {
|
||||
@ -114,7 +114,6 @@ public final class WithObject extends ScriptObject implements Scope {
|
||||
|
||||
if (find != null) {
|
||||
link = self.lookup(desc, request);
|
||||
|
||||
if (link != null) {
|
||||
return fixExpressionCallSite(ndesc, link);
|
||||
}
|
||||
@ -239,21 +238,33 @@ public final class WithObject extends ScriptObject implements Scope {
|
||||
private static GuardedInvocation fixExpressionCallSite(final NashornCallSiteDescriptor desc, final GuardedInvocation link) {
|
||||
// If it's not a getMethod, just add an expression filter that converts WithObject in "this" position to its
|
||||
// expression.
|
||||
if(!"getMethod".equals(desc.getFirstOperator())) {
|
||||
if (!"getMethod".equals(desc.getFirstOperator())) {
|
||||
return fixReceiverType(link, WITHEXPRESSIONFILTER).filterArguments(0, WITHEXPRESSIONFILTER);
|
||||
}
|
||||
|
||||
final MethodHandle linkInvocation = link.getInvocation();
|
||||
final MethodType linkType = linkInvocation.type();
|
||||
final boolean linkReturnsFunction = ScriptFunction.class.isAssignableFrom(linkType.returnType());
|
||||
final MethodHandle linkInvocation = link.getInvocation();
|
||||
final MethodType linkType = linkInvocation.type();
|
||||
final boolean linkReturnsFunction = ScriptFunction.class.isAssignableFrom(linkType.returnType());
|
||||
|
||||
return link.replaceMethods(
|
||||
// Make sure getMethod will bind the script functions it receives to WithObject.expression
|
||||
MH.foldArguments(linkReturnsFunction ? BIND_TO_EXPRESSION_FN : BIND_TO_EXPRESSION_OBJ,
|
||||
filterReceiver(linkInvocation.asType(linkType.changeReturnType(
|
||||
linkReturnsFunction ? ScriptFunction.class : Object.class).changeParameterType(0, ScriptObject.class)), WITHEXPRESSIONFILTER)),
|
||||
// No clever things for the guard -- it is still identically filtered.
|
||||
filterGuardReceiver(link, WITHEXPRESSIONFILTER));
|
||||
MH.foldArguments(
|
||||
linkReturnsFunction ?
|
||||
BIND_TO_EXPRESSION_FN :
|
||||
BIND_TO_EXPRESSION_OBJ,
|
||||
filterReceiver(
|
||||
linkInvocation.asType(
|
||||
linkType.changeReturnType(
|
||||
linkReturnsFunction ?
|
||||
ScriptFunction.class :
|
||||
Object.class).
|
||||
changeParameterType(
|
||||
0,
|
||||
ScriptObject.class)),
|
||||
WITHEXPRESSIONFILTER)),
|
||||
filterGuardReceiver(link, WITHEXPRESSIONFILTER));
|
||||
// No clever things for the guard -- it is still identically filtered.
|
||||
|
||||
}
|
||||
|
||||
private GuardedInvocation fixScopeCallSite(final GuardedInvocation link, final String name) {
|
||||
@ -271,10 +282,20 @@ public final class WithObject extends ScriptObject implements Scope {
|
||||
|
||||
private static MethodHandle filterGuardReceiver(final GuardedInvocation link, final MethodHandle receiverFilter) {
|
||||
final MethodHandle test = link.getGuard();
|
||||
return test == null ? null : filterReceiver(test, receiverFilter);
|
||||
if (test == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Class<?> receiverType = test.type().parameterType(0);
|
||||
final MethodHandle filter = MH.asType(receiverFilter,
|
||||
receiverFilter.type().changeParameterType(0, receiverType).
|
||||
changeReturnType(receiverType));
|
||||
|
||||
return filterReceiver(test, filter);
|
||||
}
|
||||
|
||||
private static MethodHandle filterReceiver(final MethodHandle mh, final MethodHandle receiverFilter) {
|
||||
//With expression filter == receiverFilter, i.e. receiver is cast to withobject and its expression returned
|
||||
return MH.filterArguments(mh, 0, receiverFilter);
|
||||
}
|
||||
|
||||
|
@ -26,16 +26,16 @@
|
||||
package jdk.nashorn.internal.runtime.arrays;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import jdk.nashorn.internal.codegen.CompilerConstants;
|
||||
import jdk.nashorn.internal.codegen.types.Type;
|
||||
import jdk.nashorn.internal.lookup.Lookup;
|
||||
import jdk.nashorn.internal.runtime.GlobalObject;
|
||||
import jdk.nashorn.internal.runtime.JSType;
|
||||
import jdk.nashorn.internal.runtime.PropertyDescriptor;
|
||||
@ -66,7 +66,7 @@ public abstract class ArrayData {
|
||||
}*/
|
||||
|
||||
/** Minimum chunk size for underlying arrays */
|
||||
protected static final int CHUNK_SIZE = 16;
|
||||
protected static final int CHUNK_SIZE = 32;
|
||||
|
||||
/** Mask for getting a chunk */
|
||||
protected static final int CHUNK_MASK = CHUNK_SIZE - 1;
|
||||
@ -87,31 +87,6 @@ public abstract class ArrayData {
|
||||
*/
|
||||
protected static final CompilerConstants.Call THROW_UNWARRANTED = staticCall(MethodHandles.lookup(), ArrayData.class, "throwUnwarranted", void.class, ArrayData.class, int.class, int.class);
|
||||
|
||||
/**
|
||||
* Unwarranted thrower
|
||||
*
|
||||
* @param data array data
|
||||
* @param programPoint program point
|
||||
* @param index array index
|
||||
*/
|
||||
protected static void throwUnwarranted(final ArrayData data, final int programPoint, final int index) {
|
||||
throw new UnwarrantedOptimismException(data.getObject(index), programPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Version of has that throws a class cast exception if element does not exist
|
||||
* used for relinking
|
||||
*
|
||||
* @param index index to check - currently only int indexes
|
||||
* @return index
|
||||
*/
|
||||
protected int throwHas(final int index) {
|
||||
if (!has(index)) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param length Virtual length of the array.
|
||||
@ -129,40 +104,34 @@ public abstract class ArrayData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return element getter for a {@link ContinuousArray}
|
||||
* @param getHas has getter
|
||||
* @param returnType return type
|
||||
* Unwarranted thrower
|
||||
*
|
||||
* @param data array data
|
||||
* @param programPoint program point
|
||||
* @return method handle for element setter
|
||||
* @param index array index
|
||||
*/
|
||||
protected final static MethodHandle getContinuousElementGetter(final MethodHandle getHas, final Class<?> returnType, final int programPoint) {
|
||||
final boolean isOptimistic = isValid(programPoint);
|
||||
final int fti = getAccessorTypeIndex(getHas.type().returnType());
|
||||
final int ti = getAccessorTypeIndex(returnType);
|
||||
MethodHandle mh = getHas;
|
||||
protected static void throwUnwarranted(final ArrayData data, final int programPoint, final int index) {
|
||||
throw new UnwarrantedOptimismException(data.getObject(index), programPoint);
|
||||
}
|
||||
|
||||
if (isOptimistic) {
|
||||
if (ti < fti) {
|
||||
mh = MH.insertArguments(ArrayData.THROW_UNWARRANTED.methodHandle(), 1, programPoint);
|
||||
}
|
||||
}
|
||||
mh = MH.asType(mh, mh.type().changeReturnType(returnType).changeParameterType(0, ContinuousArray.class));
|
||||
|
||||
if (!isOptimistic) {
|
||||
//for example a & array[17];
|
||||
return Lookup.filterReturnType(mh, returnType);
|
||||
}
|
||||
return mh;
|
||||
private static int alignUp(final int size) {
|
||||
return size + CHUNK_SIZE - 1 & ~(CHUNK_SIZE - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return element setter for a {@link ContinuousArray}
|
||||
* @param setHas set has guard
|
||||
* @param elementType element type
|
||||
* @return method handle for element setter
|
||||
* Generic invalidation hook for script object to have call sites to this array indexing
|
||||
* relinked, e.g. when a native array is marked as non extensible
|
||||
*/
|
||||
protected final static MethodHandle getContinuousElementSetter(final MethodHandle setHas, final Class<?> elementType) {
|
||||
return MH.asType(setHas, setHas.type().changeParameterType(2, elementType).changeParameterType(0, ContinuousArray.class));
|
||||
public void invalidateGetters() {
|
||||
//subclass responsibility
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic invalidation hook for script object to have call sites to this array indexing
|
||||
* relinked, e.g. when a native array is marked as non extensible
|
||||
*/
|
||||
public void invalidateSetters() {
|
||||
//subclass responsibility
|
||||
}
|
||||
|
||||
/**
|
||||
@ -597,6 +566,50 @@ public abstract class ArrayData {
|
||||
return newData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an array of items to the end of the array
|
||||
*
|
||||
* @param strict are we in strict mode
|
||||
* @param item the item
|
||||
* @return new array data (or same)
|
||||
*/
|
||||
public ArrayData push(final boolean strict, final Object item) {
|
||||
return push(strict, new Object[] { item });
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an array of items to the end of the array
|
||||
*
|
||||
* @param strict are we in strict mode
|
||||
* @param item the item
|
||||
* @return new array data (or same)
|
||||
*/
|
||||
public ArrayData push(final boolean strict, final double item) {
|
||||
return push(strict, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an array of items to the end of the array
|
||||
*
|
||||
* @param strict are we in strict mode
|
||||
* @param item the item
|
||||
* @return new array data (or same)
|
||||
*/
|
||||
public ArrayData push(final boolean strict, final long item) {
|
||||
return push(strict, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push an array of items to the end of the array
|
||||
*
|
||||
* @param strict are we in strict mode
|
||||
* @param item the item
|
||||
* @return new array data (or same)
|
||||
*/
|
||||
public ArrayData push(final boolean strict, final int item) {
|
||||
return push(strict, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop an element from the end of the array
|
||||
*
|
||||
@ -662,16 +675,7 @@ public abstract class ArrayData {
|
||||
* @return next size to allocate for internal array
|
||||
*/
|
||||
protected static int nextSize(final int size) {
|
||||
if (size == 0) {
|
||||
return CHUNK_SIZE;
|
||||
}
|
||||
|
||||
int i = size;
|
||||
while ((i & CHUNK_MASK) != 0) {
|
||||
i++;
|
||||
}
|
||||
|
||||
return i << 1;
|
||||
return alignUp(size + 1) * 2;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -696,4 +700,41 @@ public abstract class ArrayData {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a fast property getter if one exists
|
||||
*
|
||||
* @param clazz array data class
|
||||
* @param desc callsite descriptor
|
||||
* @param request link request
|
||||
* @param operator operator
|
||||
* @return fast property getter if one is found
|
||||
*/
|
||||
public GuardedInvocation findFastGetMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a fast element getter if one exists
|
||||
*
|
||||
* @param clazz array data class
|
||||
* @param desc callsite descriptor
|
||||
* @param request link request
|
||||
* @return fast index getter if one is found
|
||||
*/
|
||||
public GuardedInvocation findFastGetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a fast element setter if one exists
|
||||
*
|
||||
* @param clazz array data class
|
||||
* @param desc callsite descriptor
|
||||
* @param request link request
|
||||
* @return fast index getter if one is found
|
||||
*/
|
||||
public GuardedInvocation findFastSetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,58 +0,0 @@
|
||||
/*
|
||||
* 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.runtime.arrays;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
/**
|
||||
* Interface implemented by all arrays that are directly accessible as underlying
|
||||
* native arrays
|
||||
*/
|
||||
public interface ContinuousArray {
|
||||
|
||||
/**
|
||||
* Return the guard used for the setter, usually some kind of {@link ArrayData#has}
|
||||
* @return guard handle for setters
|
||||
*/
|
||||
public MethodHandle getSetGuard();
|
||||
|
||||
/**
|
||||
* Return element getter for a certain type at a certain program point
|
||||
* @param returnType return type
|
||||
* @param programPoint program point
|
||||
* @return element getter or null if not supported (used to implement slow linkage instead
|
||||
* as fast isn't possible)
|
||||
*/
|
||||
public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint);
|
||||
|
||||
/**
|
||||
* Return element getter for a certain type at a certain program point
|
||||
* @param elementType element type
|
||||
* @return element setter or null if not supported (used to implement slow linkage instead
|
||||
* as fast isn't possible)
|
||||
*/
|
||||
public MethodHandle getElementSetter(final Class<?> elementType);
|
||||
}
|
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* 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.runtime.arrays;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
|
||||
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.SwitchPoint;
|
||||
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.DynamicLinker;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.nashorn.internal.lookup.Lookup;
|
||||
import jdk.nashorn.internal.runtime.DebugLogger;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
|
||||
|
||||
/**
|
||||
* Interface implemented by all arrays that are directly accessible as underlying
|
||||
* native arrays
|
||||
*/
|
||||
public abstract class ContinuousArrayData extends ArrayData {
|
||||
|
||||
/** Logger for array accessor linkage */
|
||||
protected static DebugLogger LOG = new DebugLogger("arrays");
|
||||
|
||||
private SwitchPoint sp;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param length length (elementLength)
|
||||
*/
|
||||
protected ContinuousArrayData(final long length) {
|
||||
super(length);
|
||||
}
|
||||
|
||||
private SwitchPoint ensureSwitchPointExists() {
|
||||
if (sp == null){
|
||||
sp = new SwitchPoint();
|
||||
}
|
||||
return sp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateSetters() {
|
||||
SwitchPoint.invalidateAll(new SwitchPoint[] { ensureSwitchPointExists() });
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we can put one more element at the end of this continous
|
||||
* array without reallocating, or if we are overwriting an already
|
||||
* allocated element
|
||||
*
|
||||
* @param index
|
||||
* @return true if we don't need to do any array reallocation to fit an element at index
|
||||
*/
|
||||
public final boolean hasRoomFor(final int index) {
|
||||
return has(index) || (index == length() && ensure(index) == this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return element getter for a certain type at a certain program point
|
||||
* @param returnType return type
|
||||
* @param programPoint program point
|
||||
* @return element getter or null if not supported (used to implement slow linkage instead
|
||||
* as fast isn't possible)
|
||||
*/
|
||||
public abstract MethodHandle getElementGetter(final Class<?> returnType, final int programPoint);
|
||||
|
||||
/**
|
||||
* Return element getter for a certain type at a certain program point
|
||||
* @param elementType element type
|
||||
* @return element setter or null if not supported (used to implement slow linkage instead
|
||||
* as fast isn't possible)
|
||||
*/
|
||||
public abstract MethodHandle getElementSetter(final Class<?> elementType);
|
||||
|
||||
/**
|
||||
* Version of has that throws a class cast exception if element does not exist
|
||||
* used for relinking
|
||||
*
|
||||
* @param index index to check - currently only int indexes
|
||||
* @return index
|
||||
*/
|
||||
protected int throwHas(final int index) {
|
||||
if (!has(index)) {
|
||||
throw new ClassCastException();
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a continuous array element getter
|
||||
* @param get getter, sometimes combined with a has check that throws CCE on failure for relink
|
||||
* @param returnType return type
|
||||
* @param programPoint program point
|
||||
* @return array getter
|
||||
*/
|
||||
protected final MethodHandle getContinuousElementGetter(final MethodHandle get, final Class<?> returnType, final int programPoint) {
|
||||
return getContinuousElementGetter(getClass(), get, returnType, programPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up a continuous array element setter
|
||||
* @param set setter, sometimes combined with a has check that throws CCE on failure for relink
|
||||
* @param returnType return type
|
||||
* @return array setter
|
||||
*/
|
||||
protected final MethodHandle getContinuousElementSetter(final MethodHandle set, final Class<?> returnType) {
|
||||
return getContinuousElementSetter(getClass(), set, returnType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return element getter for a {@link ContinuousArrayData}
|
||||
* @param clazz clazz for exact type guard
|
||||
* @param getHas has getter
|
||||
* @param returnType return type
|
||||
* @param programPoint program point
|
||||
* @return method handle for element setter
|
||||
*/
|
||||
protected MethodHandle getContinuousElementGetter(final Class<? extends ContinuousArrayData> clazz, final MethodHandle getHas, final Class<?> returnType, final int programPoint) {
|
||||
final boolean isOptimistic = isValid(programPoint);
|
||||
final int fti = getAccessorTypeIndex(getHas.type().returnType());
|
||||
final int ti = getAccessorTypeIndex(returnType);
|
||||
MethodHandle mh = getHas;
|
||||
|
||||
if (isOptimistic) {
|
||||
if (ti < fti) {
|
||||
mh = MH.insertArguments(ArrayData.THROW_UNWARRANTED.methodHandle(), 1, programPoint);
|
||||
}
|
||||
}
|
||||
mh = MH.asType(mh, mh.type().changeReturnType(returnType).changeParameterType(0, clazz));
|
||||
|
||||
if (!isOptimistic) {
|
||||
//for example a & array[17];
|
||||
return Lookup.filterReturnType(mh, returnType);
|
||||
}
|
||||
return mh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return element setter for a {@link ContinuousArrayData}
|
||||
* @param clazz clazz for exact type guard
|
||||
* @param setHas set has guard
|
||||
* @param elementType element type
|
||||
* @return method handle for element setter
|
||||
*/
|
||||
protected MethodHandle getContinuousElementSetter(final Class<? extends ContinuousArrayData> clazz, final MethodHandle setHas, final Class<?> elementType) {
|
||||
return MH.asType(setHas, setHas.type().changeParameterType(2, elementType).changeParameterType(0, clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation findFastGetMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Fast access guard - it is impractical for JIT performance reasons to use only CCE asType as guard :-(, also we need
|
||||
the null case explicitly, which is the one that CCE doesn't handle */
|
||||
protected static final MethodHandle FAST_ACCESS_GUARD =
|
||||
MH.dropArguments(
|
||||
staticCall(
|
||||
MethodHandles.lookup(),
|
||||
ContinuousArrayData.class,
|
||||
"guard",
|
||||
boolean.class,
|
||||
Class.class,
|
||||
ScriptObject.class).methodHandle(),
|
||||
2,
|
||||
int.class);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final boolean guard(final Class<? extends ContinuousArrayData> clazz, final ScriptObject sobj) {
|
||||
if (sobj != null && sobj.getArray().getClass() == clazz) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a fast linked array getter, or null if we have to dispatch to super class
|
||||
* @param desc descriptor
|
||||
* @param request link request
|
||||
* @return invocation or null if needs to be sent to slow relink
|
||||
*/
|
||||
@Override
|
||||
public GuardedInvocation findFastGetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
final MethodType callType = desc.getMethodType();
|
||||
final Class<?> indexType = callType.parameterType(1);
|
||||
final Class<?> returnType = callType.returnType();
|
||||
|
||||
if (ContinuousArrayData.class.isAssignableFrom(clazz) && indexType == int.class) {
|
||||
final Object[] args = request.getArguments();
|
||||
final int index = (int)args[args.length - 1];
|
||||
|
||||
if (has(index)) {
|
||||
final MethodHandle getArray = ScriptObject.GET_ARRAY.methodHandle();
|
||||
final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT;
|
||||
MethodHandle getElement = getElementGetter(returnType, programPoint);
|
||||
if (getElement != null) {
|
||||
getElement = MH.filterArguments(getElement, 0, MH.asType(getArray, getArray.type().changeReturnType(clazz)));
|
||||
final MethodHandle guard = MH.insertArguments(FAST_ACCESS_GUARD, 0, clazz);
|
||||
return new GuardedInvocation(getElement, guard, null, ClassCastException.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG.isEnabled()) {
|
||||
LOG.info(getClass().getSimpleName() + ": Missed fast GETTER " + clazz.getSimpleName() + " " + desc + " " + " line:" + DynamicLinker.getLinkedCallSiteLocation().getLineNumber());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a fast linked array setter, or null if we have to dispatch to super class
|
||||
* @param desc descriptor
|
||||
* @param request link request
|
||||
* @return invocation or null if needs to be sent to slow relink
|
||||
*/
|
||||
@Override
|
||||
public GuardedInvocation findFastSetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
|
||||
final MethodType callType = desc.getMethodType();
|
||||
final Class<?> indexType = callType.parameterType(1);
|
||||
final Class<?> elementType = callType.parameterType(2);
|
||||
|
||||
if (ContinuousArrayData.class.isAssignableFrom(clazz) && indexType == int.class) {
|
||||
final Object[] args = request.getArguments();
|
||||
final int index = (int)args[args.length - 2];
|
||||
|
||||
//sp may be invalidated by e.g. preventExtensions before the first setter is linked
|
||||
//then it is already created. otherwise, create it here to guard against future
|
||||
//invalidations
|
||||
ensureSwitchPointExists();
|
||||
|
||||
if (!sp.hasBeenInvalidated() && hasRoomFor(index)) {
|
||||
MethodHandle setElement = getElementSetter(elementType); //Z(continuousarraydata, int, int), return true if successful
|
||||
if (setElement != null) {
|
||||
//else we are dealing with a wider type than supported by this callsite
|
||||
MethodHandle getArray = ScriptObject.GET_ARRAY.methodHandle();
|
||||
getArray = MH.asType(getArray, getArray.type().changeReturnType(getClass()));
|
||||
setElement = MH.filterArguments(setElement, 0, getArray);
|
||||
final MethodHandle guard = MH.insertArguments(FAST_ACCESS_GUARD, 0, clazz);
|
||||
return new GuardedInvocation(setElement, guard, sp, ClassCastException.class); //CCE if not a scriptObject anymore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (LOG.isEnabled()) {
|
||||
LOG.info(getClass().getSimpleName() + ": Missed fast SETTER " + clazz.getSimpleName() + " " + desc + " " + " line:" + DynamicLinker.getLinkedCallSiteLocation().getLineNumber());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@
|
||||
|
||||
package jdk.nashorn.internal.runtime.arrays;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -39,7 +39,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
* Implementation of {@link ArrayData} as soon as an int has been
|
||||
* written to the array. This is the default data for new arrays
|
||||
*/
|
||||
final class IntArrayData extends ArrayData implements ContinuousArray {
|
||||
final class IntArrayData extends ContinuousArrayData {
|
||||
/**
|
||||
* The wrapped array
|
||||
*/
|
||||
@ -65,14 +65,8 @@ final class IntArrayData extends ArrayData implements ContinuousArray {
|
||||
this.array = array;
|
||||
}
|
||||
|
||||
private static final MethodHandle HAS_GET_ELEM = virtualCall(MethodHandles.lookup(), IntArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", int.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = virtualCall(MethodHandles.lookup(), IntArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, int.class).methodHandle();
|
||||
private static final MethodHandle HAS = virtualCall(MethodHandles.lookup(), IntArrayData.class, "has", boolean.class, int.class).methodHandle();
|
||||
|
||||
@Override
|
||||
public final MethodHandle getSetGuard() {
|
||||
return HAS;
|
||||
}
|
||||
private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), IntArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", int.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), IntArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, int.class).methodHandle();
|
||||
|
||||
private final static long UNSAFE_BASE = UNSAFE == null ? 0L : UNSAFE.arrayBaseOffset(int[].class);
|
||||
private final static long UNSAFE_SCALE = UNSAFE == null ? 0L : UNSAFE.arrayIndexScale(int[].class);
|
||||
@ -95,12 +89,20 @@ final class IntArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void setElemUnsafe(final int index, final int elem) {
|
||||
UNSAFE.putInt(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
|
||||
if (hasRoomFor(index)) {
|
||||
UNSAFE.putInt(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
|
||||
return;
|
||||
}
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void setElem(final int index, final int elem) {
|
||||
array[index] = elem;
|
||||
if (hasRoomFor(index)) {
|
||||
array[index] = elem;
|
||||
return;
|
||||
}
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -198,22 +200,15 @@ final class IntArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@Override
|
||||
public ArrayData ensure(final long safeIndex) {
|
||||
if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= array.length) {
|
||||
if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
|
||||
return new SparseArrayData(this, safeIndex + 1);
|
||||
}
|
||||
|
||||
int newLength = array.length;
|
||||
|
||||
while (newLength <= safeIndex) {
|
||||
newLength = ArrayData.nextSize(newLength);
|
||||
}
|
||||
|
||||
if (array.length <= safeIndex) {
|
||||
final int alen = array.length;
|
||||
if (safeIndex >= alen) {
|
||||
final int newLength = ArrayData.nextSize((int)safeIndex);
|
||||
array = Arrays.copyOf(array, newLength);
|
||||
}
|
||||
|
||||
setLength(safeIndex + 1);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -337,12 +332,23 @@ final class IntArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@Override
|
||||
public ArrayData slice(final long from, final long to) {
|
||||
final long start = from < 0 ? (from + length()) : from;
|
||||
final long start = from < 0 ? from + length() : from;
|
||||
final long newLength = to - start;
|
||||
|
||||
return new IntArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ArrayData push(final boolean strict, final int item) {
|
||||
final long length = length();
|
||||
final ArrayData newData = ensure(length);
|
||||
if (newData == this) {
|
||||
array[(int)length] = item;
|
||||
return this;
|
||||
}
|
||||
return newData.set((int)length, item, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
|
||||
final long oldLength = length();
|
||||
@ -350,7 +356,7 @@ final class IntArrayData extends ArrayData implements ContinuousArray {
|
||||
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
final ArrayData returnValue = (removed == 0) ?
|
||||
final ArrayData returnValue = removed == 0 ?
|
||||
EMPTY_ARRAY : new IntArrayData(Arrays.copyOfRange(array, start, start + removed), removed);
|
||||
|
||||
if (newLength != oldLength) {
|
||||
|
@ -26,7 +26,7 @@
|
||||
package jdk.nashorn.internal.runtime.arrays;
|
||||
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -40,7 +40,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
* Implementation of {@link ArrayData} as soon as a long has been
|
||||
* written to the array
|
||||
*/
|
||||
final class LongArrayData extends ArrayData implements ContinuousArray {
|
||||
final class LongArrayData extends ContinuousArrayData {
|
||||
/**
|
||||
* The wrapped array
|
||||
*/
|
||||
@ -128,22 +128,15 @@ final class LongArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@Override
|
||||
public ArrayData ensure(final long safeIndex) {
|
||||
if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= array.length) {
|
||||
if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
|
||||
return new SparseArrayData(this, safeIndex + 1);
|
||||
}
|
||||
|
||||
int newLength = array.length;
|
||||
|
||||
while (newLength <= safeIndex) {
|
||||
newLength = ArrayData.nextSize(newLength);
|
||||
}
|
||||
|
||||
if (array.length <= safeIndex) {
|
||||
final int alen = array.length;
|
||||
if (safeIndex >= alen) {
|
||||
final int newLength = ArrayData.nextSize((int)safeIndex);
|
||||
array = Arrays.copyOf(array, newLength);
|
||||
}
|
||||
|
||||
setLength(safeIndex + 1);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -195,18 +188,12 @@ final class LongArrayData extends ArrayData implements ContinuousArray {
|
||||
return Type.LONG;
|
||||
}
|
||||
|
||||
private static final MethodHandle HAS_GET_ELEM = virtualCall(MethodHandles.lookup(), LongArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", long.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = virtualCall(MethodHandles.lookup(), LongArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, long.class).methodHandle();
|
||||
private static final MethodHandle HAS = virtualCall(MethodHandles.lookup(), LongArrayData.class, "has", boolean.class, int.class).methodHandle();
|
||||
private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), LongArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", long.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), LongArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, long.class).methodHandle();
|
||||
|
||||
private final static long UNSAFE_BASE = UNSAFE == null ? 0L : UNSAFE.arrayBaseOffset(long[].class);
|
||||
private final static long UNSAFE_SCALE = UNSAFE == null ? 0L : UNSAFE.arrayIndexScale(long[].class);
|
||||
|
||||
@Override
|
||||
public final MethodHandle getSetGuard() {
|
||||
return HAS;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private long getElemUnsafe(final int index) {
|
||||
if (has(index)) {
|
||||
@ -225,12 +212,20 @@ final class LongArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void setElemUnsafe(final int index, final long elem) {
|
||||
UNSAFE.putLong(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
|
||||
if (hasRoomFor(index)) {
|
||||
UNSAFE.putLong(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
|
||||
return;
|
||||
}
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void setElem(final int index, final long elem) {
|
||||
array[index] = elem;
|
||||
if (hasRoomFor(index)) {
|
||||
array[index] = elem;
|
||||
return;
|
||||
}
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -243,7 +238,7 @@ final class LongArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@Override
|
||||
public MethodHandle getElementSetter(final Class<?> elementType) {
|
||||
return (elementType == int.class || elementType == long.class) ? getContinuousElementSetter(MH.asType(SET_ELEM, SET_ELEM.type().changeParameterType(2, elementType)), elementType) : null;
|
||||
return elementType == int.class || elementType == long.class ? getContinuousElementSetter(MH.asType(SET_ELEM, SET_ELEM.type().changeParameterType(2, elementType)), elementType) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -307,11 +302,22 @@ final class LongArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@Override
|
||||
public ArrayData slice(final long from, final long to) {
|
||||
final long start = from < 0 ? (from + length()) : from;
|
||||
final long start = from < 0 ? from + length() : from;
|
||||
final long newLength = to - start;
|
||||
return new LongArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ArrayData push(final boolean strict, final long item) {
|
||||
final long length = length();
|
||||
final ArrayData newData = ensure(length);
|
||||
if (newData == this) {
|
||||
array[(int)length] = item;
|
||||
return this;
|
||||
}
|
||||
return newData.set((int)length, item, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
|
||||
final long oldLength = length();
|
||||
@ -319,7 +325,7 @@ final class LongArrayData extends ArrayData implements ContinuousArray {
|
||||
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
final ArrayData returnValue = (removed == 0) ?
|
||||
final ArrayData returnValue = removed == 0 ?
|
||||
EMPTY_ARRAY : new LongArrayData(Arrays.copyOfRange(array, start, start + removed), removed);
|
||||
|
||||
if (newLength != oldLength) {
|
||||
|
@ -26,7 +26,7 @@
|
||||
package jdk.nashorn.internal.runtime.arrays;
|
||||
|
||||
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
@ -39,7 +39,7 @@ import jdk.nashorn.internal.codegen.types.Type;
|
||||
* Implementation of {@link ArrayData} as soon as a double has been
|
||||
* written to the array
|
||||
*/
|
||||
final class NumberArrayData extends ArrayData implements ContinuousArray {
|
||||
final class NumberArrayData extends ContinuousArrayData {
|
||||
/**
|
||||
* The wrapped array
|
||||
*/
|
||||
@ -111,24 +111,17 @@ final class NumberArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@Override
|
||||
public ArrayData ensure(final long safeIndex) {
|
||||
if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= array.length) {
|
||||
if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
|
||||
return new SparseArrayData(this, safeIndex + 1);
|
||||
}
|
||||
|
||||
int newLength = array.length;
|
||||
|
||||
while (newLength <= safeIndex) {
|
||||
newLength = ArrayData.nextSize(newLength);
|
||||
final int alen = array.length;
|
||||
if (safeIndex >= alen) {
|
||||
final int newLength = ArrayData.nextSize((int)safeIndex);
|
||||
array = Arrays.copyOf(array, newLength); //todo fill with nan or never accessed?
|
||||
}
|
||||
|
||||
if (array.length <= safeIndex) {
|
||||
array = Arrays.copyOf(array, newLength);
|
||||
Arrays.fill(array, (int) length(), newLength, Double.NaN);
|
||||
}
|
||||
|
||||
setLength(safeIndex + 1);
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -175,18 +168,12 @@ final class NumberArrayData extends ArrayData implements ContinuousArray {
|
||||
return Type.NUMBER;
|
||||
}
|
||||
|
||||
private static final MethodHandle HAS_GET_ELEM = virtualCall(MethodHandles.lookup(), NumberArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", double.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = virtualCall(MethodHandles.lookup(), NumberArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, double.class).methodHandle();
|
||||
private static final MethodHandle HAS = virtualCall(MethodHandles.lookup(), NumberArrayData.class, "has", boolean.class, int.class).methodHandle();
|
||||
private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), NumberArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", double.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), NumberArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, double.class).methodHandle();
|
||||
|
||||
private final static long UNSAFE_BASE = UNSAFE == null ? 0L : UNSAFE.arrayBaseOffset(double[].class);
|
||||
private final static long UNSAFE_SCALE = UNSAFE == null ? 0L : UNSAFE.arrayIndexScale(double[].class);
|
||||
|
||||
@Override
|
||||
public final MethodHandle getSetGuard() {
|
||||
return HAS;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private double getElemUnsafe(final int index) {
|
||||
if (has(index)) {
|
||||
@ -205,12 +192,20 @@ final class NumberArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void setElemUnsafe(final int index, final double elem) {
|
||||
UNSAFE.putDouble(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
|
||||
if (hasRoomFor(index)) {
|
||||
UNSAFE.putDouble(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
|
||||
return;
|
||||
}
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void setElem(final int index, final double elem) {
|
||||
array[index] = elem;
|
||||
if (hasRoomFor(index)) {
|
||||
array[index] = elem;
|
||||
return;
|
||||
}
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -281,11 +276,22 @@ final class NumberArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@Override
|
||||
public ArrayData slice(final long from, final long to) {
|
||||
final long start = from < 0 ? (from + length()) : from;
|
||||
final long start = from < 0 ? from + length() : from;
|
||||
final long newLength = to - start;
|
||||
return new NumberArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ArrayData push(final boolean strict, final double item) {
|
||||
final long length = length();
|
||||
final ArrayData newData = ensure(length);
|
||||
if (newData == this) {
|
||||
array[(int)length] = item;
|
||||
return this;
|
||||
}
|
||||
return newData.set((int)length, item, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
|
||||
final long oldLength = length();
|
||||
@ -293,7 +299,7 @@ final class NumberArrayData extends ArrayData implements ContinuousArray {
|
||||
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
final ArrayData returnValue = (removed == 0) ?
|
||||
final ArrayData returnValue = removed == 0 ?
|
||||
EMPTY_ARRAY : new NumberArrayData(Arrays.copyOfRange(array, start, start + removed), removed);
|
||||
|
||||
if (newLength != oldLength) {
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
package jdk.nashorn.internal.runtime.arrays;
|
||||
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCall;
|
||||
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
@ -39,7 +39,7 @@ import jdk.nashorn.internal.runtime.ScriptRuntime;
|
||||
* Implementation of {@link ArrayData} as soon as an Object has been
|
||||
* written to the array
|
||||
*/
|
||||
final class ObjectArrayData extends ArrayData implements ContinuousArray {
|
||||
final class ObjectArrayData extends ContinuousArrayData {
|
||||
|
||||
/**
|
||||
* The wrapped array
|
||||
@ -98,23 +98,15 @@ final class ObjectArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@Override
|
||||
public ArrayData ensure(final long safeIndex) {
|
||||
if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= array.length) {
|
||||
if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH) {
|
||||
return new SparseArrayData(this, safeIndex + 1);
|
||||
}
|
||||
|
||||
int newLength = array.length;
|
||||
|
||||
while (newLength <= safeIndex) {
|
||||
newLength = ArrayData.nextSize(newLength);
|
||||
final int alen = array.length;
|
||||
if (safeIndex >= alen) {
|
||||
final int newLength = ArrayData.nextSize((int)safeIndex);
|
||||
array = Arrays.copyOf(array, newLength); //fill with undefined or OK? TODO
|
||||
}
|
||||
|
||||
if (array.length <= safeIndex) {
|
||||
array = Arrays.copyOf(array, newLength);
|
||||
Arrays.fill(array, (int) length(), newLength, ScriptRuntime.UNDEFINED);
|
||||
}
|
||||
|
||||
setLength(safeIndex + 1);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -169,18 +161,12 @@ final class ObjectArrayData extends ArrayData implements ContinuousArray {
|
||||
return Type.OBJECT;
|
||||
}
|
||||
|
||||
private static final MethodHandle HAS_GET_ELEM = virtualCall(MethodHandles.lookup(), ObjectArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", Object.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = virtualCall(MethodHandles.lookup(), ObjectArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, Object.class).methodHandle();
|
||||
private static final MethodHandle HAS = virtualCall(MethodHandles.lookup(), ObjectArrayData.class, "has", boolean.class, int.class).methodHandle();
|
||||
private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), ObjectArrayData.class, UNSAFE == null ? "getElem" : "getElemUnsafe", Object.class, int.class).methodHandle();
|
||||
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), ObjectArrayData.class, UNSAFE == null ? "setElem" : "setElemUnsafe", void.class, int.class, Object.class).methodHandle();
|
||||
|
||||
private final static long UNSAFE_BASE = UNSAFE == null ? 0L : UNSAFE.arrayBaseOffset(Object[].class);
|
||||
private final static long UNSAFE_SCALE = UNSAFE == null ? 0L : UNSAFE.arrayIndexScale(Object[].class);
|
||||
|
||||
@Override
|
||||
public final MethodHandle getSetGuard() {
|
||||
return HAS;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Object getElemUnsafe(final int index) {
|
||||
if (has(index)) {
|
||||
@ -199,12 +185,20 @@ final class ObjectArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void setElemUnsafe(final int index, final Object elem) {
|
||||
UNSAFE.putObject(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
|
||||
if (hasRoomFor(index)) {
|
||||
UNSAFE.putObject(array, UNSAFE_BASE + UNSAFE_SCALE * index, elem);
|
||||
return;
|
||||
}
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void setElem(final int index, final Object elem) {
|
||||
array[index] = elem;
|
||||
if (hasRoomFor(index)) {
|
||||
array[index] = elem;
|
||||
return;
|
||||
}
|
||||
throw new ClassCastException();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -273,11 +267,22 @@ final class ObjectArrayData extends ArrayData implements ContinuousArray {
|
||||
|
||||
@Override
|
||||
public ArrayData slice(final long from, final long to) {
|
||||
final long start = from < 0 ? (from + length()) : from;
|
||||
final long start = from < 0 ? from + length() : from;
|
||||
final long newLength = to - start;
|
||||
return new ObjectArrayData(Arrays.copyOfRange(array, (int)from, (int)to), (int)newLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData push(final boolean strict, final Object item) {
|
||||
final long length = length();
|
||||
final ArrayData newData = ensure(length);
|
||||
if (newData == this) {
|
||||
array[(int)length] = item;
|
||||
return this;
|
||||
}
|
||||
return newData.set((int)length, item, strict);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
|
||||
final long oldLength = length();
|
||||
@ -285,7 +290,7 @@ final class ObjectArrayData extends ArrayData implements ContinuousArray {
|
||||
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
final ArrayData returnValue = (removed == 0) ?
|
||||
final ArrayData returnValue = removed == 0 ?
|
||||
EMPTY_ARRAY : new ObjectArrayData(Arrays.copyOfRange(array, start, start + removed), removed);
|
||||
|
||||
if (newLength != oldLength) {
|
||||
|
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.runtime.arrays;
|
||||
|
||||
import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.nio.Buffer;
|
||||
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.DynamicLinker;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.nashorn.internal.lookup.Lookup;
|
||||
|
||||
/**
|
||||
* The superclass of all ArrayData used by TypedArrays
|
||||
*
|
||||
* @param <T> buffer implementation
|
||||
*/
|
||||
public abstract class TypedArrayData<T extends Buffer> extends ContinuousArrayData {
|
||||
|
||||
/** wrapped native buffer */
|
||||
protected final T nb;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param nb wrapped native buffer
|
||||
* @param elementLength length in elements
|
||||
*/
|
||||
protected TypedArrayData(final T nb, final int elementLength) {
|
||||
super(elementLength); //TODO is this right?
|
||||
this.nb = nb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Length in elements. Accessed from {@code ArrayBufferView}
|
||||
* @return element length
|
||||
*/
|
||||
public final int getElementLength() {
|
||||
return (int)length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this an unsigned array data?
|
||||
* @return true if unsigned
|
||||
*/
|
||||
public boolean isUnsigned() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a clamped array data?
|
||||
* @return true if clamped
|
||||
*/
|
||||
public boolean isClamped() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDelete(final int index, final boolean strict) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData copy() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] asObjectArray() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shiftLeft(int by) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData shiftRight(int by) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData ensure(long safeIndex) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData shrink(long newLength) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean has(final int index) {
|
||||
return 0 <= index && index < length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData delete(int index) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData delete(long fromIndex, long toIndex) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ArrayData convert(Class<?> type) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object pop() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayData slice(long from, long to) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Element getter method handle
|
||||
* @return getter
|
||||
*/
|
||||
protected abstract MethodHandle getGetElem();
|
||||
|
||||
/**
|
||||
* Element setter method handle
|
||||
* @return setter
|
||||
*/
|
||||
protected abstract MethodHandle getSetElem();
|
||||
|
||||
@Override
|
||||
public MethodHandle getElementGetter(final Class<?> returnType, final int programPoint) {
|
||||
final MethodHandle getter = getContinuousElementGetter(getClass(), getGetElem(), returnType, programPoint);
|
||||
if (getter != null) {
|
||||
return Lookup.filterReturnType(getter, returnType);
|
||||
}
|
||||
return getter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodHandle getElementSetter(final Class<?> elementType) {
|
||||
return getContinuousElementSetter(getClass(), Lookup.filterArgumentType(getSetElem(), 2, elementType), elementType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MethodHandle getContinuousElementSetter(final Class<? extends ContinuousArrayData> clazz, final MethodHandle setHas, final Class<?> elementType) {
|
||||
final MethodHandle mh = Lookup.filterArgumentType(setHas, 2, elementType);
|
||||
return MH.asType(mh, mh.type().changeParameterType(0, clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation findFastGetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
final GuardedInvocation inv = super.findFastGetIndexMethod(clazz, desc, request);
|
||||
|
||||
if (inv != null) {
|
||||
return inv;
|
||||
}
|
||||
|
||||
if (LOG.isEnabled()) {
|
||||
LOG.info(clazz.getSimpleName() + ": Missed fast GETTER " + clazz.getSimpleName() + " " + desc + " " + " line:" + DynamicLinker.getLinkedCallSiteLocation().getLineNumber());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GuardedInvocation findFastSetIndexMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
|
||||
final GuardedInvocation inv = super.findFastSetIndexMethod(clazz, desc, request);
|
||||
|
||||
if (inv != null) {
|
||||
return inv;
|
||||
}
|
||||
|
||||
if (LOG.isEnabled()) {
|
||||
LOG.info(clazz.getSimpleName() + ": Missed fast SETTER " + clazz.getSimpleName() + " " + desc + " " + " line:" + DynamicLinker.getLinkedCallSiteLocation().getLineNumber());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -164,7 +164,7 @@ public final class JavaAdapterFactory {
|
||||
final StaticClass adapterClass = getAdapterClassFor(new Class<?>[] { targetType }, null, lookup);
|
||||
return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(
|
||||
NashornCallSiteDescriptor.get(lookup, "dyn:new",
|
||||
MethodType.methodType(targetType, StaticClass.class, sourceType), 0), null, false,
|
||||
MethodType.methodType(targetType, StaticClass.class, sourceType), 0), null, 0, false,
|
||||
adapterClass, null)).getInvocation(), adapterClass);
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import jdk.internal.dynalink.ChainedCallSite;
|
||||
import jdk.internal.dynalink.DynamicLinker;
|
||||
import jdk.internal.dynalink.linker.GuardedInvocation;
|
||||
@ -63,6 +64,9 @@ public class LinkerCallSite extends ChainedCallSite {
|
||||
private static final String PROFILEFILE = Options.getStringProperty("nashorn.profilefile", "NashornProfile.txt");
|
||||
|
||||
private static final MethodHandle INCREASE_MISS_COUNTER = MH.findStatic(MethodHandles.lookup(), LinkerCallSite.class, "increaseMissCount", MH.type(Object.class, String.class, Object.class));
|
||||
private static final MethodHandle ON_CATCH_INVALIDATION = MH.findStatic(MethodHandles.lookup(), LinkerCallSite.class, "onCatchInvalidation", MH.type(ChainedCallSite.class, LinkerCallSite.class));
|
||||
|
||||
private int catchInvalidations;
|
||||
|
||||
LinkerCallSite(final NashornCallSiteDescriptor descriptor) {
|
||||
super(descriptor);
|
||||
@ -71,6 +75,34 @@ public class LinkerCallSite extends ChainedCallSite {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MethodHandle getPruneCatches() {
|
||||
return MH.filterArguments(super.getPruneCatches(), 0, ON_CATCH_INVALIDATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to perform when a catch guard around a callsite triggers. Increases
|
||||
* catch invalidation counter
|
||||
* @param callSite callsite
|
||||
* @return the callsite, so this can be used as argument filter
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private static ChainedCallSite onCatchInvalidation(final LinkerCallSite callSite) {
|
||||
++callSite.catchInvalidations;
|
||||
return callSite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of catch invalidations that have happened at this call site so far
|
||||
* @param callSiteToken call site token, unique to the callsite.
|
||||
* @return number of catch invalidations, i.e. thrown exceptions caught by the linker
|
||||
*/
|
||||
public static int getCatchInvalidationCount(final Object callSiteToken) {
|
||||
if (callSiteToken instanceof LinkerCallSite) {
|
||||
return ((LinkerCallSite)callSiteToken).catchInvalidations;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* Construct a new linker call site.
|
||||
* @param name Name of method.
|
||||
@ -78,8 +110,7 @@ public class LinkerCallSite extends ChainedCallSite {
|
||||
* @param flags Call site specific flags.
|
||||
* @return New LinkerCallSite.
|
||||
*/
|
||||
static LinkerCallSite newLinkerCallSite(final MethodHandles.Lookup lookup, final String name, final MethodType type,
|
||||
final int flags) {
|
||||
static LinkerCallSite newLinkerCallSite(final MethodHandles.Lookup lookup, final String name, final MethodType type, final int flags) {
|
||||
final NashornCallSiteDescriptor desc = NashornCallSiteDescriptor.get(lookup, name, type, flags);
|
||||
|
||||
if (desc.isProfile()) {
|
||||
|
@ -30,30 +30,73 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import jdk.internal.dynalink.CallSiteDescriptor;
|
||||
import jdk.internal.dynalink.linker.LinkRequest;
|
||||
import jdk.nashorn.internal.runtime.PropertyMap;
|
||||
import jdk.nashorn.internal.runtime.ScriptFunction;
|
||||
import jdk.nashorn.internal.runtime.ScriptObject;
|
||||
import jdk.nashorn.internal.runtime.options.Options;
|
||||
|
||||
/**
|
||||
* Constructor of method handles used to guard call sites.
|
||||
*/
|
||||
public final class NashornGuards {
|
||||
private static final MethodHandle IS_MAP = findOwnMH("isMap", boolean.class, ScriptObject.class, PropertyMap.class);
|
||||
private static final MethodHandle IS_INSTANCEOF_2 = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class);
|
||||
private static final MethodHandle IS_MAP = findOwnMH("isMap", boolean.class, ScriptObject.class, PropertyMap.class);
|
||||
private static final MethodHandle IS_MAP_SCRIPTOBJECT = findOwnMH("isMap", boolean.class, Object.class, PropertyMap.class);
|
||||
private static final MethodHandle IS_INSTANCEOF_2 = findOwnMH("isInstanceOf2", boolean.class, Object.class, Class.class, Class.class);
|
||||
private static final MethodHandle IS_SCRIPTOBJECT = findOwnMH("isScriptObject", boolean.class, Object.class);
|
||||
|
||||
private static final boolean CCE_ONLY = Options.getBooleanProperty("nashorn.cce");
|
||||
|
||||
// don't create me!
|
||||
private NashornGuards() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a callsite descriptor and a link request, determine whether we should use an instanceof
|
||||
* check explicitly for the guard if needed, or if we should link it with a try/catch ClassCastException
|
||||
* combinator as its relink criteria - i.e. relink when CCE is thrown.
|
||||
*
|
||||
* @param desc callsite descriptor
|
||||
* @param request link request
|
||||
* @return true of explicit instanceof check is needed
|
||||
*/
|
||||
public static boolean explicitInstanceOfCheck(final CallSiteDescriptor desc, final LinkRequest request) {
|
||||
//THIS is currently true, as the inliner encounters several problems with sun.misc.ValueConversions.castReference
|
||||
//otherwise. We should only use the exception based relink where we have no choice, and the result is faster code,
|
||||
//for example in the NativeArray, TypedArray, ContinuousArray getters. For the standard callsite, it appears that
|
||||
//we lose performance rather than gain it, due to JVM issues. :-(
|
||||
return !CCE_ONLY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a guard that does an instanceof ScriptObject check on the receiver
|
||||
* @return guard
|
||||
*/
|
||||
public static MethodHandle getScriptObjectGuard() {
|
||||
return IS_SCRIPTOBJECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a guard that does an instanceof ScriptObject check on the receiver
|
||||
* @param explicitInstanceOfCheck - if false, then this is a nop, because it's all the guard does
|
||||
* @return guard
|
||||
*/
|
||||
public static MethodHandle getScriptObjectGuard(final boolean explicitInstanceOfCheck) {
|
||||
return explicitInstanceOfCheck ? IS_SCRIPTOBJECT : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the guard that checks if a {@link PropertyMap} is equal to
|
||||
* a known map, using reference comparison
|
||||
*
|
||||
* @param explicitInstanceOfCheck true if we should do an explicit script object instanceof check instead of just casting
|
||||
* @param map The map to check against. This will be bound to the guard method handle
|
||||
*
|
||||
* @return method handle for guard
|
||||
*/
|
||||
public static MethodHandle getMapGuard(final PropertyMap map) {
|
||||
return MH.insertArguments(IS_MAP, 1, map);
|
||||
public static MethodHandle getMapGuard(final PropertyMap map, final boolean explicitInstanceOfCheck) {
|
||||
return MH.insertArguments(explicitInstanceOfCheck ? IS_MAP_SCRIPTOBJECT : IS_MAP, 1, map);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,18 +110,38 @@ public final class NashornGuards {
|
||||
return MH.insertArguments(IS_INSTANCEOF_2, 1, class1, class2);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isScriptObject(final Object self) {
|
||||
return self instanceof ScriptObject;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isScriptObject(final Class<? extends ScriptObject> clazz, final Object self) {
|
||||
return clazz.isInstance(self);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isMap(final ScriptObject self, final PropertyMap map) {
|
||||
return self.getMap() == map;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isMap(final Object self, final PropertyMap map) {
|
||||
return self instanceof ScriptObject && ((ScriptObject)self).getMap() == map;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isInstanceOf2(final Object self, final Class<?> class1, final Class<?> class2) {
|
||||
return class1.isInstance(self) || class2.isInstance(self);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static boolean isScriptFunction(final Object self) {
|
||||
return self instanceof ScriptFunction;
|
||||
}
|
||||
|
||||
private static MethodHandle findOwnMH(final String name, final Class<?> rtype, final Class<?>... types) {
|
||||
return MH.findStatic(MethodHandles.lookup(), NashornGuards.class, name, MH.type(rtype, types));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp
|
||||
} else if (self instanceof Undefined) {
|
||||
inv = Undefined.lookup(desc);
|
||||
} else {
|
||||
throw new AssertionError(); // Should never reach here.
|
||||
throw new AssertionError(self.getClass().getName()); // Should never reach here.
|
||||
}
|
||||
|
||||
return inv;
|
||||
|
@ -138,6 +138,8 @@ type.error.no.method.matches.args=Can not invoke method {0} with the passed argu
|
||||
type.error.method.not.constructor=Java method {0} can't be used as a constructor.
|
||||
type.error.env.not.object=$ENV must be an Object.
|
||||
type.error.unsupported.java.to.type=Unsupported Java.to target type {0}.
|
||||
type.error.constructor.requires.new=Constructor {0} requires 'new'.
|
||||
|
||||
range.error.inappropriate.array.length=inappropriate array length: {0}
|
||||
range.error.inappropriate.array.buffer.length=inappropriate array buffer length: {0}
|
||||
range.error.invalid.fraction.digits=fractionDigits argument to {0} must be in [0, 20]
|
||||
|
@ -46,12 +46,18 @@ bench("[]", function() {
|
||||
str[2];
|
||||
});
|
||||
|
||||
bench("fromCharCode", function() {
|
||||
bench("fromCharCode 1", function() {
|
||||
String.fromCharCode(97);
|
||||
String.fromCharCode(98);
|
||||
String.fromCharCode(99);
|
||||
});
|
||||
|
||||
bench("fromCharCode 2", function() {
|
||||
String.fromCharCode(97, 98);
|
||||
String.fromCharCode(97, 98, 99);
|
||||
String.fromCharCode(97, 98, 99, 100);
|
||||
});
|
||||
|
||||
bench("charAt 1", function() {
|
||||
str.charAt(0);
|
||||
str.charAt(1);
|
||||
|
@ -35,7 +35,7 @@ var limit = Math.pow(2, UNSIGNED_INT_BITS)/BYTES_PER_INT_32
|
||||
|
||||
// A value over the limit should throw a RangeError.
|
||||
try {
|
||||
Int32Array(limit)
|
||||
new Int32Array(limit)
|
||||
} catch(e) {
|
||||
print(e)
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ function fillArray(a, start) {
|
||||
})();
|
||||
|
||||
(function test_slice() {
|
||||
var b = ArrayBuffer(16);
|
||||
var b = new ArrayBuffer(16);
|
||||
fillArray(new Int8Array(b));
|
||||
print(bufstr(b));
|
||||
print("slice(4,8)=" + bufstr(b.slice(4, 8)), "slice(-8,-4)=" + bufstr(b.slice(-8, -4))); // negative index refers from the end of the array
|
||||
|
@ -83,7 +83,7 @@ all.forEach(function(instance) {
|
||||
var arr = Object.getOwnPropertyNames(instance);
|
||||
arr.forEach(function(p) {
|
||||
var val = instance[p];
|
||||
if(!isNaN(p)){
|
||||
if(!isNaN(p)) {
|
||||
val[p] = 99;
|
||||
}
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user