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:
Marcus Lagergren 2014-03-03 11:24:44 +01:00
parent e9e7dd2ec1
commit 8d4fc394e0
82 changed files with 2721 additions and 1295 deletions
nashorn
bin
buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen
make
src/jdk
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;
}
});