aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/fixwhitespace.sh37
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java1
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java77
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java31
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java12
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java40
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java1
-rw-r--r--buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java29
-rw-r--r--make/BuildNashorn.gmk2
-rw-r--r--make/build.xml28
-rw-r--r--make/project.properties21
-rw-r--r--samples/BufferArray.java28
-rw-r--r--src/jdk/nashorn/internal/codegen/ApplySpecialization.java21
-rw-r--r--src/jdk/nashorn/internal/codegen/ClassEmitter.java9
-rw-r--r--src/jdk/nashorn/internal/codegen/CodeGenerator.java67
-rw-r--r--src/jdk/nashorn/internal/codegen/CompilationPhase.java4
-rw-r--r--src/jdk/nashorn/internal/codegen/CompileUnit.java8
-rw-r--r--src/jdk/nashorn/internal/codegen/Compiler.java33
-rw-r--r--src/jdk/nashorn/internal/codegen/Label.java7
-rw-r--r--src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java15
-rw-r--r--src/jdk/nashorn/internal/codegen/Lower.java11
-rw-r--r--src/jdk/nashorn/internal/codegen/MethodEmitter.java70
-rw-r--r--src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java8
-rw-r--r--src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java23
-rw-r--r--src/jdk/nashorn/internal/codegen/types/Type.java43
-rw-r--r--src/jdk/nashorn/internal/ir/BreakNode.java11
-rw-r--r--src/jdk/nashorn/internal/ir/ContinueNode.java12
-rw-r--r--src/jdk/nashorn/internal/ir/FunctionNode.java10
-rw-r--r--src/jdk/nashorn/internal/ir/JumpStatement.java20
-rw-r--r--src/jdk/nashorn/internal/ir/LiteralNode.java9
-rw-r--r--src/jdk/nashorn/internal/ir/Symbol.java11
-rw-r--r--src/jdk/nashorn/internal/lookup/MethodHandleFactory.java66
-rw-r--r--src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java9
-rw-r--r--src/jdk/nashorn/internal/objects/ArrayBufferView.java1
-rw-r--r--src/jdk/nashorn/internal/objects/Global.java248
-rw-r--r--src/jdk/nashorn/internal/objects/NativeArray.java421
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDataView.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDate.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeDebug.java5
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFloat32Array.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeFloat64Array.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt16Array.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt32Array.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeInt8Array.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeJSAdapter.java1
-rw-r--r--src/jdk/nashorn/internal/objects/NativeRegExp.java9
-rw-r--r--src/jdk/nashorn/internal/objects/NativeString.java137
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint16Array.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint32Array.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint8Array.java6
-rw-r--r--src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java6
-rw-r--r--src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java18
-rw-r--r--src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java315
-rw-r--r--src/jdk/nashorn/internal/parser/AbstractParser.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/AccessorProperty.java50
-rw-r--r--src/jdk/nashorn/internal/runtime/CodeInstaller.java25
-rw-r--r--src/jdk/nashorn/internal/runtime/CodeStore.java9
-rw-r--r--src/jdk/nashorn/internal/runtime/CompiledFunction.java120
-rw-r--r--src/jdk/nashorn/internal/runtime/Context.java67
-rw-r--r--src/jdk/nashorn/internal/runtime/Debug.java9
-rw-r--r--src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java21
-rw-r--r--src/jdk/nashorn/internal/runtime/FindProperty.java3
-rw-r--r--src/jdk/nashorn/internal/runtime/GlobalConstants.java6
-rw-r--r--src/jdk/nashorn/internal/runtime/GlobalFunctions.java18
-rw-r--r--src/jdk/nashorn/internal/runtime/OptimisticBuiltins.java65
-rw-r--r--src/jdk/nashorn/internal/runtime/Property.java36
-rw-r--r--src/jdk/nashorn/internal/runtime/PropertyMap.java2
-rw-r--r--src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java68
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptEnvironment.java4
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptFunction.java119
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptFunctionData.java26
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptObject.java81
-rw-r--r--src/jdk/nashorn/internal/runtime/ScriptRuntime.java27
-rw-r--r--src/jdk/nashorn/internal/runtime/SetMethodCreator.java25
-rw-r--r--src/jdk/nashorn/internal/runtime/Specialization.java114
-rw-r--r--src/jdk/nashorn/internal/runtime/StoredScript.java6
-rw-r--r--src/jdk/nashorn/internal/runtime/UserAccessorProperty.java1
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ArrayData.java21
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java28
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java84
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java21
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java11
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java111
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/IntElements.java (renamed from src/jdk/nashorn/internal/objects/annotations/SpecializedConstructor.java)25
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/IntOrLongElements.java34
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java82
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java18
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java81
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/NumericElements.java35
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java82
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java78
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java9
-rw-r--r--src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java17
-rw-r--r--src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java28
-rw-r--r--src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java16
-rw-r--r--src/jdk/nashorn/internal/runtime/resources/Options.properties6
-rw-r--r--test/examples/charcodeat-benchmark.js63
-rw-r--r--test/examples/push-pop-benchmark.js59
-rw-r--r--test/script/basic/apply_to_call/apply_to_call5.js111
-rw-r--r--test/script/basic/apply_to_call/apply_to_call5.js.EXPECTED19
-rw-r--r--test/script/basic/fastpushpop.js61
-rw-r--r--test/script/basic/fastpushpop.js.EXPECTED6
-rw-r--r--test/script/basic/octane-payload.js2
-rw-r--r--test/script/basic/run-octane.js221
-rw-r--r--test/src/jdk/nashorn/internal/runtime/ClassFilterTest.java12
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java2
-rw-r--r--test/src/jdk/nashorn/internal/test/framework/TestFinder.java60
107 files changed, 3336 insertions, 964 deletions
diff --git a/bin/fixwhitespace.sh b/bin/fixwhitespace.sh
new file mode 100644
index 00000000..d3274700
--- /dev/null
+++ b/bin/fixwhitespace.sh
@@ -0,0 +1,37 @@
+#!/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.
+#
+
+fix() {
+ #convert tabs to spaces
+ find . -name $1 -exec sed -i "" 's/ / /g' {} \;
+ #remove trailing whitespace
+ find . -name $1 -exec sed -i "" 's/[ ]*$//' \{} \;
+}
+
+if [ ! -z $1 ]; then
+ fix $1;
+else
+ fix "*.java"
+ fix "*.js"
+fi
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java
index 2059567b..75874316 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ClassGenerator.java
@@ -292,7 +292,6 @@ public class ClassGenerator {
mi.push(memInfo.getArity());
mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY, SCRIPTFUNCTION_SETARITY_DESC);
}
-
}
static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo memInfo) {
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java
index c8a929dc..cc3524da 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MemberInfo.java
@@ -28,7 +28,6 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_ARRAY_DES
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.STRING_DESC;
-
import jdk.internal.org.objectweb.asm.Opcodes;
import jdk.internal.org.objectweb.asm.Type;
import jdk.nashorn.internal.objects.annotations.Where;
@@ -75,10 +74,6 @@ public final class MemberInfo implements Cloneable {
* This is a specialized version of a function
*/
SPECIALIZED_FUNCTION,
- /**
- * This is a specialized version of a constructor
- */
- SPECIALIZED_CONSTRUCTOR
}
// keep in sync with jdk.nashorn.internal.objects.annotations.Attribute
@@ -107,6 +102,12 @@ public final class MemberInfo implements Cloneable {
private Where where;
+ private Type linkLogicClass;
+
+ private boolean isSpecializedConstructor;
+
+ private boolean isOptimistic;
+
/**
* @return the kind
*/
@@ -136,6 +137,57 @@ public final class MemberInfo implements Cloneable {
}
/**
+ * Tag something as specialized constructor or not
+ * @param isSpecializedConstructor boolean, true if specialized constructor
+ */
+ public void setIsSpecializedConstructor(final boolean isSpecializedConstructor) {
+ this.isSpecializedConstructor = isSpecializedConstructor;
+ }
+
+ /**
+ * Check if something is a specialized constructor
+ * @return true if specialized constructor
+ */
+ public boolean isSpecializedConstructor() {
+ return isSpecializedConstructor;
+ }
+
+ /**
+ * Check if this is an optimistic builtin function
+ * @return true if optimistic builtin
+ */
+ public boolean isOptimistic() {
+ return isOptimistic;
+ }
+
+ /**
+ * Tag something as optimitic builtin or not
+ * @param isOptimistic boolean, true if builtin constructor
+ */
+ public void setIsOptimistic(final boolean isOptimistic) {
+ this.isOptimistic = isOptimistic;
+ }
+
+ /**
+ * Get the SpecializedFunction guard for specializations, i.e. optimistic
+ * builtins
+ * @return specialization, null if none
+ */
+ public Type getLinkLogicClass() {
+ return linkLogicClass;
+ }
+
+ /**
+ * Set thre SpecializedFunction link logic class for specializations, i.e. optimistic
+ * builtins
+ * @param linkLogicClass link logic class
+ */
+
+ public void setLinkLogicClass(final Type linkLogicClass) {
+ this.linkLogicClass = linkLogicClass;
+ }
+
+ /**
* @return the attributes
*/
public int getAttributes() {
@@ -304,19 +356,6 @@ public final class MemberInfo implements Cloneable {
}
}
break;
- case SPECIALIZED_CONSTRUCTOR: {
- final Type returnType = Type.getReturnType(javaDesc);
- if (!isJSObjectType(returnType)) {
- error("return value of a @SpecializedConstructor method should be a valid JS type, found " + returnType);
- }
- final Type[] argTypes = Type.getArgumentTypes(javaDesc);
- for (int i = 0; i < argTypes.length; i++) {
- if (!isValidJSType(argTypes[i])) {
- error(i + "'th argument of a @SpecializedConstructor method is not valid JS type, found " + argTypes[i]);
- }
- }
- }
- break;
case FUNCTION: {
final Type returnType = Type.getReturnType(javaDesc);
if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) {
@@ -351,7 +390,7 @@ public final class MemberInfo implements Cloneable {
break;
case SPECIALIZED_FUNCTION: {
final Type returnType = Type.getReturnType(javaDesc);
- if (!(isValidJSType(returnType) || Type.VOID_TYPE == returnType)) {
+ if (!(isValidJSType(returnType) || (isSpecializedConstructor() && Type.VOID_TYPE == returnType))) {
error("return value of a @SpecializedFunction method should be a valid JS type, found " + returnType);
}
final Type[] argTypes = Type.getArgumentTypes(javaDesc);
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
index a8d6ae2b..a8bd954f 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/MethodGenerator.java
@@ -56,6 +56,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
import static jdk.internal.org.objectweb.asm.Opcodes.IALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.IASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0;
+import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1;
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE;
import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
@@ -76,13 +77,16 @@ import static jdk.internal.org.objectweb.asm.Opcodes.SALOAD;
import static jdk.internal.org.objectweb.asm.Opcodes.SASTORE;
import static jdk.internal.org.objectweb.asm.Opcodes.SIPUSH;
import static jdk.internal.org.objectweb.asm.Opcodes.SWAP;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.METHODHANDLE_TYPE;
-import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_METHODHANDLE;
-
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_INIT2;
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_INIT3;
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.SPECIALIZATION_TYPE;
+import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_SPECIALIZATION;
import java.util.List;
import jdk.internal.org.objectweb.asm.Handle;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Type;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
/**
* Base class for all method generating classes.
@@ -95,6 +99,8 @@ public class MethodGenerator extends MethodVisitor {
private final Type returnType;
private final Type[] argumentTypes;
+ static final Type EMPTY_LINK_LOGIC_TYPE = Type.getType(LinkLogic.getEmptyLinkLogicClass());
+
MethodGenerator(final MethodVisitor mv, final int access, final String name, final String descriptor) {
super(ASM4, mv);
this.access = access;
@@ -380,6 +386,11 @@ public class MethodGenerator extends MethodVisitor {
super.visitFieldInsn(GETFIELD, owner, field, desc);
}
+ private static boolean linkLogicIsEmpty(final Type type) {
+ assert EMPTY_LINK_LOGIC_TYPE != null; //type is ok for null if we are a @SpecializedFunction without any attribs
+ return EMPTY_LINK_LOGIC_TYPE.equals(type);
+ }
+
void memberInfoArray(final String className, final List<MemberInfo> mis) {
if (mis.isEmpty()) {
pushNull();
@@ -388,12 +399,22 @@ public class MethodGenerator extends MethodVisitor {
int pos = 0;
push(mis.size());
- newObjectArray(METHODHANDLE_TYPE);
+ newObjectArray(SPECIALIZATION_TYPE);
for (final MemberInfo mi : mis) {
dup();
push(pos++);
+ visitTypeInsn(NEW, SPECIALIZATION_TYPE);
+ dup();
visitLdcInsn(new Handle(H_INVOKESTATIC, className, mi.getJavaName(), mi.getJavaDesc()));
- arrayStore(TYPE_METHODHANDLE);
+ final Type linkLogicClass = mi.getLinkLogicClass();
+ final boolean linkLogic = !linkLogicIsEmpty(linkLogicClass);
+ final String ctor = linkLogic ? SPECIALIZATION_INIT3 : SPECIALIZATION_INIT2;
+ if (linkLogic) {
+ visitLdcInsn(linkLogicClass);
+ }
+ visitInsn(mi.isOptimistic() ? ICONST_1 : ICONST_0);
+ visitMethodInsn(INVOKESPECIAL, SPECIALIZATION_TYPE, INIT, ctor, false);
+ arrayStore(TYPE_SPECIALIZATION);
}
}
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java
index 0a1579ff..9e3dfc17 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfo.java
@@ -37,8 +37,8 @@ import jdk.nashorn.internal.objects.annotations.Getter;
import jdk.nashorn.internal.objects.annotations.Property;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
import jdk.nashorn.internal.objects.annotations.Setter;
-import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
@@ -56,8 +56,8 @@ public final class ScriptClassInfo {
static final String SETTER_ANNO_DESC = Type.getDescriptor(Setter.class);
static final String PROPERTY_ANNO_DESC = Type.getDescriptor(Property.class);
static final String WHERE_ENUM_DESC = Type.getDescriptor(Where.class);
+ static final String LINK_LOGIC_DESC = Type.getDescriptor(LinkLogic.class);
static final String SPECIALIZED_FUNCTION = Type.getDescriptor(SpecializedFunction.class);
- static final String SPECIALIZED_CONSTRUCTOR = Type.getDescriptor(SpecializedConstructor.class);
static final Map<String, Kind> annotations = new HashMap<>();
@@ -69,7 +69,6 @@ public final class ScriptClassInfo {
annotations.put(SETTER_ANNO_DESC, Kind.SETTER);
annotations.put(PROPERTY_ANNO_DESC, Kind.PROPERTY);
annotations.put(SPECIALIZED_FUNCTION, Kind.SPECIALIZED_FUNCTION);
- annotations.put(SPECIALIZED_CONSTRUCTOR, Kind.SPECIALIZED_CONSTRUCTOR);
}
// name of the script class
@@ -119,11 +118,12 @@ public final class ScriptClassInfo {
List<MemberInfo> getSpecializedConstructors() {
final List<MemberInfo> res = new LinkedList<>();
for (final MemberInfo memInfo : members) {
- if (memInfo.getKind() == Kind.SPECIALIZED_CONSTRUCTOR) {
+ if (memInfo.isSpecializedConstructor()) {
+ assert memInfo.getKind() == Kind.SPECIALIZED_FUNCTION;
res.add(memInfo);
}
}
- return res;
+ return Collections.unmodifiableList(res);
}
int getPrototypeMemberCount() {
@@ -175,7 +175,7 @@ public final class ScriptClassInfo {
res.add(memInfo);
}
}
- return res;
+ return Collections.unmodifiableList(res);
}
MemberInfo findSetter(final MemberInfo getter) {
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java
index 5561cfbf..aa477621 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInfoCollector.java
@@ -27,7 +27,6 @@ package jdk.nashorn.internal.tools.nasgen;
import static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.SCRIPT_CLASS_ANNO_DESC;
import static jdk.nashorn.internal.tools.nasgen.ScriptClassInfo.WHERE_ENUM_DESC;
-
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
@@ -41,6 +40,7 @@ import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.FieldVisitor;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.Opcodes;
+import jdk.internal.org.objectweb.asm.Type;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
@@ -194,6 +194,7 @@ public class ScriptClassInfoCollector extends ClassVisitor {
final MemberInfo memInfo = new MemberInfo();
+ //annokind == e.g. GETTER or SPECIALIZED_FUNCTION
memInfo.setKind(annoKind);
memInfo.setJavaName(methodName);
memInfo.setJavaDesc(methodDesc);
@@ -208,12 +209,18 @@ public class ScriptClassInfoCollector extends ClassVisitor {
private Integer attributes;
private Integer arity;
private Where where;
+ private boolean isSpecializedConstructor;
+ private boolean isOptimistic;
+ private Type linkLogicClass = MethodGenerator.EMPTY_LINK_LOGIC_TYPE;
@Override
public void visit(final String annotationName, final Object annotationValue) {
switch (annotationName) {
case "name":
this.name = (String)annotationValue;
+ if (name.isEmpty()) {
+ name = null;
+ }
break;
case "attributes":
this.attributes = (Integer)annotationValue;
@@ -221,6 +228,17 @@ public class ScriptClassInfoCollector extends ClassVisitor {
case "arity":
this.arity = (Integer)annotationValue;
break;
+ case "isConstructor":
+ assert annoKind == Kind.SPECIALIZED_FUNCTION;
+ this.isSpecializedConstructor = (Boolean)annotationValue;
+ break;
+ case "isOptimistic":
+ assert annoKind == Kind.SPECIALIZED_FUNCTION;
+ this.isOptimistic = (Boolean)annotationValue;
+ break;
+ case "linkLogic":
+ this.linkLogicClass = (Type)annotationValue;
+ break;
default:
break;
}
@@ -230,12 +248,19 @@ public class ScriptClassInfoCollector extends ClassVisitor {
@Override
public void visitEnum(final String enumName, final String desc, final String enumValue) {
- if ("where".equals(enumName) && WHERE_ENUM_DESC.equals(desc)) {
- this.where = Where.valueOf(enumValue);
+ switch (enumName) {
+ case "where":
+ if (WHERE_ENUM_DESC.equals(desc)) {
+ this.where = Where.valueOf(enumValue);
+ }
+ break;
+ default:
+ break;
}
super.visitEnum(enumName, desc, enumValue);
}
+ @SuppressWarnings("fallthrough")
@Override
public void visitEnd() {
super.visitEnd();
@@ -256,7 +281,6 @@ public class ScriptClassInfoCollector extends ClassVisitor {
case SETTER:
where = Where.INSTANCE;
break;
- case SPECIALIZED_CONSTRUCTOR:
case CONSTRUCTOR:
where = Where.CONSTRUCTOR;
break;
@@ -264,12 +288,18 @@ public class ScriptClassInfoCollector extends ClassVisitor {
where = Where.PROTOTYPE;
break;
case SPECIALIZED_FUNCTION:
- //TODO is this correct
+ if (isSpecializedConstructor) {
+ where = Where.CONSTRUCTOR;
+ }
+ //fallthru
default:
break;
}
}
memInfo.setWhere(where);
+ memInfo.setLinkLogicClass(linkLogicClass);
+ memInfo.setIsSpecializedConstructor(isSpecializedConstructor);
+ memInfo.setIsOptimistic(isOptimistic);
}
};
}
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java
index 0ec233ae..0d6a9e37 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/ScriptClassInstrumentor.java
@@ -38,7 +38,6 @@ import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DES
import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_TYPE;
-
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
diff --git a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java
index fb72bbe6..8656a42b 100644
--- a/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java
+++ b/buildtools/nasgen/src/jdk/nashorn/internal/tools/nasgen/StringConstants.java
@@ -37,6 +37,7 @@ import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.Specialization;
/**
* String constants used for code generation/instrumentation.
@@ -44,20 +45,26 @@ import jdk.nashorn.internal.runtime.ScriptObject;
@SuppressWarnings("javadoc")
public interface StringConstants {
// standard jdk types, methods
- static final Type TYPE_METHODHANDLE = Type.getType(MethodHandle.class);
- static final Type TYPE_METHODHANDLE_ARRAY = Type.getType(MethodHandle[].class);
- static final Type TYPE_OBJECT = Type.getType(Object.class);
- static final Type TYPE_STRING = Type.getType(String.class);
- static final Type TYPE_COLLECTION = Type.getType(Collection.class);
- static final Type TYPE_COLLECTIONS = Type.getType(Collections.class);
- static final Type TYPE_ARRAYLIST = Type.getType(ArrayList.class);
- static final Type TYPE_LIST = Type.getType(List.class);
+ static final Type TYPE_METHODHANDLE = Type.getType(MethodHandle.class);
+ static final Type TYPE_METHODHANDLE_ARRAY = Type.getType(MethodHandle[].class);
+ static final Type TYPE_SPECIALIZATION = Type.getType(Specialization.class);
+ static final Type TYPE_SPECIALIZATION_ARRAY = Type.getType(Specialization[].class);
+ static final Type TYPE_OBJECT = Type.getType(Object.class);
+ static final Type TYPE_STRING = Type.getType(String.class);
+ static final Type TYPE_CLASS = Type.getType(Class.class);
+ static final Type TYPE_COLLECTION = Type.getType(Collection.class);
+ static final Type TYPE_COLLECTIONS = Type.getType(Collections.class);
+ static final Type TYPE_ARRAYLIST = Type.getType(ArrayList.class);
+ static final Type TYPE_LIST = Type.getType(List.class);
static final String CLINIT = "<clinit>";
static final String INIT = "<init>";
static final String DEFAULT_INIT_DESC = Type.getMethodDescriptor(Type.VOID_TYPE);
static final String METHODHANDLE_TYPE = TYPE_METHODHANDLE.getInternalName();
+ static final String SPECIALIZATION_TYPE = TYPE_SPECIALIZATION.getInternalName();
+ static final String SPECIALIZATION_INIT2 = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_METHODHANDLE, Type.getType(boolean.class));
+ static final String SPECIALIZATION_INIT3 = Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_METHODHANDLE, TYPE_CLASS, Type.getType(boolean.class));
static final String OBJECT_TYPE = TYPE_OBJECT.getInternalName();
static final String OBJECT_DESC = TYPE_OBJECT.getDescriptor();
static final String STRING_TYPE = TYPE_STRING.getInternalName();
@@ -122,11 +129,11 @@ public interface StringConstants {
static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC =
Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE);
static final String SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC =
- Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE, TYPE_METHODHANDLE_ARRAY);
+ Type.getMethodDescriptor(TYPE_SCRIPTFUNCTION, TYPE_STRING, TYPE_METHODHANDLE, TYPE_SPECIALIZATION_ARRAY);
static final String SCRIPTFUNCTIONIMPL_INIT_DESC3 =
- Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_METHODHANDLE_ARRAY);
+ Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_SPECIALIZATION_ARRAY);
static final String SCRIPTFUNCTIONIMPL_INIT_DESC4 =
- Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_PROPERTYMAP, TYPE_METHODHANDLE_ARRAY);
+ Type.getMethodDescriptor(Type.VOID_TYPE, TYPE_STRING, TYPE_METHODHANDLE, TYPE_PROPERTYMAP, TYPE_SPECIALIZATION_ARRAY);
// ScriptObject
static final String SCRIPTOBJECT_TYPE = TYPE_SCRIPTOBJECT.getInternalName();
diff --git a/make/BuildNashorn.gmk b/make/BuildNashorn.gmk
index a987874c..683d4624 100644
--- a/make/BuildNashorn.gmk
+++ b/make/BuildNashorn.gmk
@@ -65,7 +65,7 @@ $(eval $(call SetupJavaCompilation,BUILD_NASGEN, \
SETUP := GENERATE_NEWBYTECODE_DEBUG, \
SRC := $(NASGEN_SRC) $(ASM_SRC), \
BIN := $(NASHORN_OUTPUTDIR)/nasgen_classes, \
- ADD_JAVAC_FLAGS := -cp $(NASHORN_OUTPUTDIR)/nashorn_classes))
+ ADD_JAVAC_FLAGS := -bootclasspath "$(BOOT_RTJAR)$(PATH_SEP)$(NASHORN_OUTPUTDIR)/nashorn_classes"))
# Nasgen needs nashorn classes
$(BUILD_NASGEN): $(BUILD_NASHORN)
diff --git a/make/build.xml b/make/build.xml
index 9d21de84..9bb600dc 100644
--- a/make/build.xml
+++ b/make/build.xml
@@ -78,7 +78,7 @@
<istrue value="${jfr}"/>
</condition>
</target>
-
+
<target name="init" depends="init-conditions, init-cc">
<!-- extends jvm args -->
<property name="run.test.jvmargs" value="${run.test.jvmargs.main} ${run.test.cc.jvmargs} ${jfr.options}"/>
@@ -419,6 +419,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<propertyref prefix="test-sys-prop-no-security."/>
<mapper from="test-sys-prop-no-security.*" to="*" type="glob"/>
</propertyset>
+ <sysproperty key="optimistic.override" value="${optimistic}"/>
<classpath>
<pathelement path="${run.test.classpath}"/>
</classpath>
@@ -430,7 +431,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<delete dir="${build.dir}/nashorn_code_cache"/>
<property name="debug.test.jvmargs" value=""/>
<testng outputdir="${build.test.results.dir}" classfilesetref="test.classes"
- verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
+ verbose="${testng.verbose}" haltonfailure="true" useDefaultListeners="false" listeners="${testng.listeners}" workingDir="${basedir}">
<jvmarg line="${ext.class.path}"/>
<jvmarg line="${run.test.jvmargs} -Xmx${run.test.xmx} ${run.test.jvmsecurityargs} -Dbuild.dir=${build.dir}"/>
<jvmarg line="${debug.test.jvmargs}"/>
@@ -441,6 +442,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<propertyref prefix="test-sys-prop."/>
<mapper from="test-sys-prop.*" to="*" type="glob"/>
</propertyset>
+ <sysproperty key="optimistic.override" value="${optimistic}"/>
<sysproperty key="test.js.excludes.file" value="${exclude.list}"/>
<classpath>
<pathelement path="${run.test.classpath}"/>
@@ -448,7 +450,27 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
</testng>
</target>
- <target name="test" depends="jar, -test-classes-all,-test-classes-single, check-testng, check-external-tests, compile-test, generate-policy-file, -test-security, -test-nosecurity" if="testng.available"/>
+ <target name="test" depends="test-pessimistic, test-optimistic"/>
+
+ <target name="test-optimistic" depends="jar, -test-classes-all,-test-classes-single, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
+ <echo message="Running test suite in OPTIMISTIC mode..."/>
+ <antcall target="-test-nosecurity" inheritRefs="true">
+ <param name="optimistic" value="true"/>
+ </antcall>
+ <antcall target="-test-security" inheritRefs="true">
+ <param name="optimistic" value="true"/>
+ </antcall>
+ </target>
+
+ <target name="test-pessimistic" depends="jar, -test-classes-all,-test-classes-single, check-testng, check-external-tests, compile-test, generate-policy-file" if="testng.available">
+ <echo message="Running test suite in PESSIMISTIC mode..."/>
+ <antcall target="-test-nosecurity" inheritRefs="true">
+ <param name="optimistic" value="false"/>
+ </antcall>
+ <antcall target="-test-security" inheritRefs="true">
+ <param name="optimistic" value="false"/>
+ </antcall>
+ </target>
<target name="check-jemmy.jfx.testng" unless="jemmy.jfx.testng.available">
<echo message="WARNING: Jemmy or JavaFX or TestNG not available, will not run tests. Please copy testng.jar, JemmyCore.jar, JemmyFX.jar, JemmyAWTInput.jar under test${file.separator}lib directory. And make sure you have jfxrt.jar in ${java.home}${file.separator}lib${file.separator}ext dir."/>
diff --git a/make/project.properties b/make/project.properties
index 800a6295..9d36fbdc 100644
--- a/make/project.properties
+++ b/make/project.properties
@@ -286,7 +286,8 @@ run.test.jvmargs.common=\
# turn on assertions for tests
run.test.jvmargs.main=${run.test.jvmargs.common} -ea
-# extra jvmargs that might be useful for debugging
+# Extra jvmargs that might be useful for debugging
+# and performance improvements/monitoring
#
# -XX:+UnlockDiagnosticVMOptions
#
@@ -304,9 +305,25 @@ run.test.jvmargs.main=${run.test.jvmargs.common} -ea
#
# print all compiled nmethods with oopmaps and lots of other info
# -XX:+PrintNMethods
+#
+# activate the generic "UseNewCode" flag to test whatever functionality
+# lies behind it. This is the preferred way to test a, yet flagless,
+# feature in HotSpot - for example, the uncommon trap placement fix
+# was hidden behind this flag before it became the default
+#
+# -XX:+UnlockDiagnosticVMOptions -XX:+UseNewCode
+#
+# Crank up the type profile level to 222, which has some warmup
+# penalties, but produces much better code for JavaScript, where better
+# and more intrusive type profiling is required to get rid of
+# a large amount of unnecessary guard code, that could not otherwise
+# be eliminated
+#
+# -XX:TypeProfileLevel=222
+#
# Use best known performance options for octane
-run.test.jvmargs.octane.main=${run.test.jvmargs.common} -XX:+UnlockDiagnosticVMOptions -XX:+UseNewCode -XX:TypeProfileLevel=222
+run.test.jvmargs.octane.main=${run.test.jvmargs.common} -XX:TypeProfileLevel=222
# Security manager args - make sure that we run with the nashorn.policy that the build creates
run.test.jvmsecurityargs=-Xverify:all -Djava.security.manager -Djava.security.policy=${build.dir}/nashorn.policy
diff --git a/samples/BufferArray.java b/samples/BufferArray.java
index 00d66023..b26b5c08 100644
--- a/samples/BufferArray.java
+++ b/samples/BufferArray.java
@@ -29,8 +29,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-import jdk.nashorn.api.scripting.AbstractJSObject;
import java.nio.DoubleBuffer;
+import jdk.nashorn.api.scripting.AbstractJSObject;
/**
* Simple class demonstrating pluggable script object
@@ -52,41 +52,49 @@ public class BufferArray extends AbstractJSObject {
// underlying nio buffer
private final DoubleBuffer buf;
- public BufferArray(int size) {
+ /**
+ * Constructor
+ * @param size initial size
+ */
+ public BufferArray(final int size) {
buf = DoubleBuffer.allocate(size);
}
- public BufferArray(DoubleBuffer buf) {
+ /**
+ * Constructur
+ * @param buf {@link DoubleBuffer} to link to
+ */
+ public BufferArray(final DoubleBuffer buf) {
this.buf = buf;
}
// called to check if indexed property exists
@Override
- public boolean hasSlot(int index) {
+ public boolean hasSlot(final int index) {
return index > 0 && index < buf.capacity();
}
// get the value from that index
@Override
- public Object getSlot(int index) {
+ public Object getSlot(final int index) {
return buf.get(index);
}
// set the value at that index
@Override
- public void setSlot(int index, Object value) {
+ public void setSlot(final int index, final Object value) {
buf.put(index, ((Number)value).doubleValue());
}
// do you have a property of that given name?
@Override
- public boolean hasMember(String name) {
+ public boolean hasMember(final String name) {
return "length".equals(name) || "buf".equals(name);
}
// get the value of that named property
@Override
- public Object getMember(String name) {
+ public Object getMember(final String name) {
switch (name) {
case "length":
return buf.capacity();
@@ -94,7 +102,7 @@ public class BufferArray extends AbstractJSObject {
// return a 'function' value for this property
return new AbstractJSObject() {
@Override
- public Object call(Object thiz, Object... args) {
+ public Object call(final Object thiz, final Object... args) {
return BufferArray.this.buf;
}
@@ -104,6 +112,8 @@ public class BufferArray extends AbstractJSObject {
return true;
}
};
+ default:
+ break;
}
return null;
}
diff --git a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
index d1668894..b05475b6 100644
--- a/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
+++ b/src/jdk/nashorn/internal/codegen/ApplySpecialization.java
@@ -27,7 +27,6 @@ package jdk.nashorn.internal.codegen;
import static jdk.nashorn.internal.codegen.CompilerConstants.ARGUMENTS_VAR;
import static jdk.nashorn.internal.codegen.CompilerConstants.EXPLODED_ARGUMENT_PREFIX;
-
import java.lang.invoke.MethodType;
import java.util.ArrayDeque;
import java.util.ArrayList;
@@ -35,7 +34,6 @@ import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.Expression;
@@ -131,11 +129,12 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
@SuppressWarnings("serial")
final UnsupportedOperationException uoe = new UnsupportedOperationException() {
@Override
- public Throwable fillInStackTrace() {
+ public synchronized Throwable fillInStackTrace() {
return null;
}
};
+ final Set<Expression> argumentsFound = new HashSet<>();
final Deque<Set<Expression>> stack = new ArrayDeque<>();
//ensure that arguments is only passed as arg to apply
try {
@@ -145,7 +144,11 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
}
private boolean isArguments(final Expression expr) {
- return expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName());
+ if (expr instanceof IdentNode && ARGUMENTS.equals(((IdentNode)expr).getName())) {
+ argumentsFound.add(expr);
+ return true;
+ }
+ return false;
}
private boolean isParam(final String name) {
@@ -159,7 +162,7 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
@Override
public Node leaveIdentNode(final IdentNode identNode) {
- if (isParam(identNode.getName()) || ARGUMENTS.equals(identNode.getName()) && !isCurrentArg(identNode)) {
+ if (isParam(identNode.getName()) || isArguments(identNode) && !isCurrentArg(identNode)) {
throw uoe; //avoid filling in stack trace
}
return identNode;
@@ -186,7 +189,9 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
}
});
} catch (final UnsupportedOperationException e) {
- log.fine("'arguments' escapes, is not used in standard call dispatch, or is reassigned in '" + functionNode.getName() + "'. Aborting");
+ if (!argumentsFound.isEmpty()) {
+ log.fine("'arguments' is used but escapes, or is reassigned in '" + functionNode.getName() + "'. Aborting");
+ }
return true; //bad
}
@@ -267,9 +272,9 @@ public final class ApplySpecialization extends NodeVisitor<LexicalContext> imple
return false;
}
- if (!Global.instance().isSpecialNameValid("apply")) {
+ if (!Global.isBuiltinFunctionPrototypeApply()) {
log.fine("Apply transform disabled: apply/call overridden");
- assert !Global.instance().isSpecialNameValid("call") : "call and apply should have the same SwitchPoint";
+ assert !Global.isBuiltinFunctionPrototypeCall() : "call and apply should have the same SwitchPoint";
return false;
}
diff --git a/src/jdk/nashorn/internal/codegen/ClassEmitter.java b/src/jdk/nashorn/internal/codegen/ClassEmitter.java
index 9f7fe79d..7a89e490 100644
--- a/src/jdk/nashorn/internal/codegen/ClassEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/ClassEmitter.java
@@ -51,15 +51,14 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.className;
import static jdk.nashorn.internal.codegen.CompilerConstants.methodDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.typeDescriptor;
import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup;
-
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
-
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import jdk.internal.org.objectweb.asm.util.TraceClassVisitor;
@@ -160,8 +159,12 @@ public class ClassEmitter implements Emitter {
this.methodNames = new HashSet<>();
}
+ /**
+ * Return the method names encountered
+ * @return method names
+ */
public Set<String> getMethodNames() {
- return methodNames;
+ return Collections.unmodifiableSet(methodNames);
}
/**
diff --git a/src/jdk/nashorn/internal/codegen/CodeGenerator.java b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
index 97b26212..ae3ee694 100644
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java
+++ b/src/jdk/nashorn/internal/codegen/CodeGenerator.java
@@ -104,6 +104,7 @@ import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.JoinPredecessor;
import jdk.nashorn.internal.ir.JoinPredecessorExpression;
+import jdk.nashorn.internal.ir.JumpStatement;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LexicalContextNode;
@@ -1204,17 +1205,21 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
public boolean enterBreakNode(final BreakNode breakNode) {
+ return enterJumpStatement(breakNode);
+ }
+
+ private boolean enterJumpStatement(final JumpStatement jump) {
if(!method.isReachable()) {
return false;
}
- enterStatement(breakNode);
+ enterStatement(jump);
- method.beforeJoinPoint(breakNode);
- final BreakableNode breakFrom = lc.getBreakable(breakNode.getLabelName());
- popScopesUntil(breakFrom);
- final Label breakLabel = breakFrom.getBreakLabel();
- breakLabel.markAsBreakTarget();
- method.splitAwareGoto(lc, breakLabel, breakFrom);
+ method.beforeJoinPoint(jump);
+ final BreakableNode target = jump.getTarget(lc);
+ popScopesUntil(target);
+ final Label targetLabel = jump.getTargetLabel(target);
+ targetLabel.markAsBreakTarget();
+ method.splitAwareGoto(lc, targetLabel, target);
return false;
}
@@ -1517,19 +1522,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
@Override
public boolean enterContinueNode(final ContinueNode continueNode) {
- if(!method.isReachable()) {
- return false;
- }
- enterStatement(continueNode);
- method.beforeJoinPoint(continueNode);
-
- final LoopNode continueTo = lc.getContinueTo(continueNode.getLabelName());
- popScopesUntil(continueTo);
- final Label continueLabel = continueTo.getContinueLabel();
- continueLabel.markAsBreakTarget();
- method.splitAwareGoto(lc, continueLabel, continueTo);
-
- return false;
+ return enterJumpStatement(continueNode);
}
@Override
@@ -2807,6 +2800,7 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
final boolean hasReturn = method.hasReturn();
final SplitMethodEmitter splitMethod = ((SplitMethodEmitter)method);
final List<Label> targets = splitMethod.getExternalTargets();
+ final boolean hasControlFlow = hasReturn || !targets.isEmpty();
final List<BreakableNode> targetNodes = splitMethod.getExternalTargetNodes();
final Type returnType = lc.getCurrentFunction().getReturnType();
@@ -2814,6 +2808,9 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
// Wrap up this method.
if(method.isReachable()) {
+ if (hasControlFlow) {
+ method.setSplitState(-1);
+ }
method.loadCompilerConstant(RETURN, returnType);
method._return(returnType);
}
@@ -2831,17 +2828,16 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
throw e;
}
- // Handle return from split method if there was one.
- final MethodEmitter caller = method;
- final int targetCount = targets.size();
-
//no external jump targets or return in switch node
- if (!hasReturn && targets.isEmpty()) {
+ if (!hasControlFlow) {
return splitNode;
}
- caller.loadCompilerConstant(SCOPE);
- caller.checkcast(Scope.class);
+ // Handle return from split method if there was one.
+ final MethodEmitter caller = method;
+ final int targetCount = targets.size();
+
+ caller.loadScope();
caller.invoke(Scope.GET_SPLIT_STATE);
final Label breakLabel = new Label("no_split_state");
@@ -2873,19 +2869,16 @@ final class CodeGenerator extends NodeOperatorVisitor<CodeGeneratorLexicalContex
caller.loadCompilerConstant(RETURN, returnType);
caller._return(returnType);
} else {
- // Clear split state.
- caller.loadCompilerConstant(SCOPE);
- caller.checkcast(Scope.class);
- caller.load(-1);
- caller.invoke(Scope.SET_SPLIT_STATE);
final BreakableNode targetNode = targetNodes.get(i - 1);
final Label label = targets.get(i - 1);
- final JoinPredecessor jumpOrigin = splitNode.getJumpOrigin(label);
- if(jumpOrigin != null) {
- method.beforeJoinPoint(jumpOrigin);
+ if (!lc.isExternalTarget(splitNode, targetNode)) {
+ final JoinPredecessor jumpOrigin = splitNode.getJumpOrigin(label);
+ if(jumpOrigin != null) {
+ method.beforeJoinPoint(jumpOrigin);
+ }
+ popScopesUntil(targetNode);
}
- popScopesUntil(targetNode);
- caller.splitAwareGoto(lc, targets.get(i - 1), targetNode);
+ caller.splitAwareGoto(lc, label, targetNode);
}
}
caller.label(breakLabel);
diff --git a/src/jdk/nashorn/internal/codegen/CompilationPhase.java b/src/jdk/nashorn/internal/codegen/CompilationPhase.java
index 603157d6..1ba77a7c 100644
--- a/src/jdk/nashorn/internal/codegen/CompilationPhase.java
+++ b/src/jdk/nashorn/internal/codegen/CompilationPhase.java
@@ -38,7 +38,6 @@ import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SCOPE_DEPTHS
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SPLIT;
import static jdk.nashorn.internal.ir.FunctionNode.CompilationState.SYMBOLS_ASSIGNED;
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.EnumSet;
@@ -48,7 +47,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-
import jdk.nashorn.internal.AssertsEnabled;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
import jdk.nashorn.internal.ir.FunctionNode;
@@ -627,7 +625,7 @@ enum CompilationPhase {
/**
* Start a compilation phase
- * @param compiler
+ * @param compiler the compiler to use
* @param functionNode function to compile
* @return function node
*/
diff --git a/src/jdk/nashorn/internal/codegen/CompileUnit.java b/src/jdk/nashorn/internal/codegen/CompileUnit.java
index d45a58f1..2d3cd2be 100644
--- a/src/jdk/nashorn/internal/codegen/CompileUnit.java
+++ b/src/jdk/nashorn/internal/codegen/CompileUnit.java
@@ -60,6 +60,10 @@ public final class CompileUnit implements Comparable<CompileUnit> {
emittedUnitCount++;
}
+ /**
+ * Get the amount of emitted compile units so far in the system
+ * @return emitted compile unit count
+ */
public static int getEmittedUnitCount() {
return emittedUnitCount;
}
@@ -72,6 +76,10 @@ public final class CompileUnit implements Comparable<CompileUnit> {
return isUsed;
}
+ /**
+ * Check if a compile unit has code, not counting inits and clinits
+ * @return true of if there is "real code" in the compile unit
+ */
public boolean hasCode() {
return (classEmitter.getMethodCount() - classEmitter.getInitCount() - classEmitter.getClinitCount()) > 0;
}
diff --git a/src/jdk/nashorn/internal/codegen/Compiler.java b/src/jdk/nashorn/internal/codegen/Compiler.java
index 0a1de709..3a0cc6bf 100644
--- a/src/jdk/nashorn/internal/codegen/Compiler.java
+++ b/src/jdk/nashorn/internal/codegen/Compiler.java
@@ -32,7 +32,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
import static jdk.nashorn.internal.codegen.CompilerConstants.VARARGS;
import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote;
-
import java.io.File;
import java.lang.invoke.MethodType;
import java.util.Arrays;
@@ -154,6 +153,13 @@ public final class Compiler implements Loggable {
private RecompilableScriptFunctionData compiledFunction;
/**
+ * Most compile unit names are longer than the default StringBuilder buffer,
+ * worth startup performance when massive class generation is going on to increase
+ * this
+ */
+ private static final int COMPILE_UNIT_NAME_BUFFER_SIZE = 32;
+
+ /**
* Compilation phases that a compilation goes through
*/
public static class CompilationPhases implements Iterable<CompilationPhase> {
@@ -452,12 +458,16 @@ public final class Compiler implements Loggable {
@Override
public DebugLogger initLogger(final Context ctxt) {
+ final boolean optimisticTypes = env._optimistic_types;
+ final boolean lazyCompilation = env._lazy_compilation;
+
return ctxt.getLogger(this.getClass(), new Consumer<DebugLogger>() {
@Override
public void accept(final DebugLogger newLogger) {
- if (!Compiler.this.getScriptEnvironment()._lazy_compilation) {
+ if (!lazyCompilation) {
newLogger.warning("WARNING: Running with lazy compilation switched off. This is not a default setting.");
}
+ newLogger.warning("Optimistic types are ", optimisticTypes ? "ENABLED." : "DISABLED.");
}
});
}
@@ -535,9 +545,10 @@ public final class Compiler implements Loggable {
* @throws CompilationException if error occurs during compilation
*/
public FunctionNode compile(final FunctionNode functionNode, final CompilationPhases phases) throws CompilationException {
-
- log.finest("Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc()));
- log.indent();
+ if (log.isEnabled()) {
+ log.info(">> Starting compile job for ", DebugLogger.quote(functionNode.getName()), " phases=", quote(phases.getDesc()));
+ log.indent();
+ }
final String name = DebugLogger.quote(functionNode.getName());
@@ -554,7 +565,7 @@ public final class Compiler implements Loggable {
long time = 0L;
for (final CompilationPhase phase : phases) {
- log.fine(phase, " starting for ", quote(name));
+ log.fine(phase, " starting for ", name);
try {
newFunctionNode = phase.apply(this, phases, newFunctionNode);
@@ -582,8 +593,11 @@ public final class Compiler implements Loggable {
log.unindent();
if (info) {
- final StringBuilder sb = new StringBuilder();
- sb.append("Compile job for ").append(newFunctionNode.getSource()).append(':').append(quote(newFunctionNode.getName())).append(" finished");
+ final StringBuilder sb = new StringBuilder("<< Finished compile job for ");
+ sb.append(newFunctionNode.getSource()).
+ append(':').
+ append(quote(newFunctionNode.getName()));
+
if (time > 0L && timeLogger != null) {
assert env.isTimingEnabled();
sb.append(" in ").append(time).append(" ms");
@@ -631,7 +645,8 @@ public final class Compiler implements Loggable {
}
String nextCompileUnitName() {
- final StringBuilder sb = new StringBuilder(firstCompileUnitName);
+ final StringBuilder sb = new StringBuilder(COMPILE_UNIT_NAME_BUFFER_SIZE);
+ sb.append(firstCompileUnitName);
final int cuid = nextCompileUnitId.getAndIncrement();
if (cuid > 0) {
sb.append("$cu").append(cuid);
diff --git a/src/jdk/nashorn/internal/codegen/Label.java b/src/jdk/nashorn/internal/codegen/Label.java
index 86304690..7c86abf0 100644
--- a/src/jdk/nashorn/internal/codegen/Label.java
+++ b/src/jdk/nashorn/internal/codegen/Label.java
@@ -590,8 +590,13 @@ public final class Label {
return label.getOffset() > other.label.getOffset();
}
+ private String str;
+
@Override
public String toString() {
- return name + '_' + id;
+ if (str == null) {
+ str = name + '_' + id;
+ }
+ return str;
}
}
diff --git a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
index c9580d41..491ef2ca 100644
--- a/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
+++ b/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java
@@ -464,21 +464,20 @@ final class LocalVariableTypesCalculator extends NodeVisitor<LexicalContext>{
@Override
public boolean enterBreakNode(final BreakNode breakNode) {
- if(!reachable) {
- return false;
- }
-
- final BreakableNode target = lc.getBreakable(breakNode.getLabelName());
- return splitAwareJumpToLabel(breakNode, target, target.getBreakLabel());
+ return enterJumpStatement(breakNode);
}
@Override
public boolean enterContinueNode(final ContinueNode continueNode) {
+ return enterJumpStatement(continueNode);
+ }
+
+ private boolean enterJumpStatement(final JumpStatement jump) {
if(!reachable) {
return false;
}
- final LoopNode target = lc.getContinueTo(continueNode.getLabelName());
- return splitAwareJumpToLabel(continueNode, target, target.getContinueLabel());
+ final BreakableNode target = jump.getTarget(lc);
+ return splitAwareJumpToLabel(jump, target, jump.getTargetLabel(target));
}
private boolean splitAwareJumpToLabel(final JumpStatement jumpStatement, final BreakableNode target, final Label targetLabel) {
diff --git a/src/jdk/nashorn/internal/codegen/Lower.java b/src/jdk/nashorn/internal/codegen/Lower.java
index 2c87f3b9..dddf6976 100644
--- a/src/jdk/nashorn/internal/codegen/Lower.java
+++ b/src/jdk/nashorn/internal/codegen/Lower.java
@@ -52,6 +52,7 @@ import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
+import jdk.nashorn.internal.ir.JumpStatement;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.LiteralNode;
@@ -382,12 +383,16 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
@Override
public Node leaveBreakNode(final BreakNode breakNode) {
- return copy(breakNode, (Node)Lower.this.lc.getBreakable(breakNode.getLabelName()));
+ return leaveJumpStatement(breakNode);
}
@Override
public Node leaveContinueNode(final ContinueNode continueNode) {
- return copy(continueNode, Lower.this.lc.getContinueTo(continueNode.getLabelName()));
+ return leaveJumpStatement(continueNode);
+ }
+
+ private Node leaveJumpStatement(final JumpStatement jump) {
+ return copy(jump, (Node)jump.getTarget(Lower.this.lc));
}
@Override
@@ -627,7 +632,7 @@ final class Lower extends NodeOperatorVisitor<BlockLexicalContext> implements Lo
@Override
public Node leaveContinueNode(final ContinueNode node) {
// all inner loops have been popped.
- if (lex.contains(lex.getContinueTo(node.getLabelName()))) {
+ if (lex.contains(node.getTarget(lex))) {
escapes.add(node);
}
return node;
diff --git a/src/jdk/nashorn/internal/codegen/MethodEmitter.java b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
index 2bd4c4c5..400d5219 100644
--- a/src/jdk/nashorn/internal/codegen/MethodEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/MethodEmitter.java
@@ -71,7 +71,6 @@ import static jdk.nashorn.internal.codegen.CompilerConstants.virtualCallNoLookup
import static jdk.nashorn.internal.codegen.ObjectClassGenerator.PRIMITIVE_FIELD_TYPE;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_OPTIMISTIC;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT;
-
import java.io.PrintStream;
import java.lang.reflect.Array;
import java.util.Collection;
@@ -105,7 +104,9 @@ import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.Debug;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.RewriteException;
+import jdk.nashorn.internal.runtime.Scope;
import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
@@ -180,9 +181,6 @@ public class MethodEmitter implements Emitter {
/** Bootstrap for array populators */
private static final Handle POPULATE_ARRAY_BOOTSTRAP = new Handle(H_INVOKESTATIC, RewriteException.BOOTSTRAP.className(), RewriteException.BOOTSTRAP.name(), RewriteException.BOOTSTRAP.descriptor());
- /** Bootstrap for global name invalidation */
- private static final Handle INVALIDATE_NAME_BOOTSTRAP = new Handle(H_INVOKESTATIC, Global.BOOTSTRAP.className(), Global.BOOTSTRAP.name(), Global.BOOTSTRAP.descriptor());
-
/**
* Constructor - internal use from ClassEmitter only
* @see ClassEmitter#method
@@ -1050,6 +1048,14 @@ public class MethodEmitter implements Emitter {
return load(getCompilerConstantSymbol(cc), type != null ? type : getCompilerConstantType(cc));
}
+ MethodEmitter loadScope() {
+ return loadCompilerConstant(SCOPE).checkcast(Scope.class);
+ }
+
+ MethodEmitter setSplitState(final int state) {
+ return loadScope().load(state).invoke(Scope.SET_SPLIT_STATE);
+ }
+
void storeCompilerConstant(final CompilerConstants cc) {
storeCompilerConstant(cc, null);
}
@@ -2131,10 +2137,15 @@ public class MethodEmitter implements Emitter {
}
MethodEmitter invalidateSpecialName(final String name) {
- //this is a nop if the global hasn't registered this as a special name - we can just ignore it
- if (Global.instance().isSpecialName(name)) {
- debug("dynamic_invalidate_name", "name=", name);
- method.visitInvokeDynamicInsn(name, "()V", INVALIDATE_NAME_BOOTSTRAP);
+ switch (name) {
+ case "apply":
+ case "call":
+ debug("invalidate_name", "name=", name);
+ load("Function");
+ invoke(ScriptRuntime.INVALIDATE_RESERVED_BUILTIN_NAME);
+ break;
+ default:
+ break;
}
return this;
}
@@ -2575,12 +2586,55 @@ public class MethodEmitter implements Emitter {
*
* @param args debug information to print
*/
+ @SuppressWarnings("unused")
private void debug(final Object... args) {
if (debug) {
debug(30, args);
}
}
+ private void debug(final String arg) {
+ if (debug) {
+ debug(30, arg);
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1 });
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1, final Object arg2) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1, arg2 });
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1, arg2, arg3 });
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4 });
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4, final Object arg5) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4, arg5 });
+ }
+ }
+
+ private void debug(final Object arg0, final Object arg1, final Object arg2, final Object arg3, final Object arg4, final Object arg5, final Object arg6) {
+ if (debug) {
+ debug(30, new Object[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6 });
+ }
+ }
+
/**
* Debug function that outputs generated bytecode and stack contents
* for a label - indentation is currently the only thing that differs
diff --git a/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java b/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java
index 3c835ac5..05a76872 100644
--- a/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java
+++ b/src/jdk/nashorn/internal/codegen/OptimisticTypesPersistence.java
@@ -429,7 +429,6 @@ public final class OptimisticTypesPersistence {
}
private static void doCleanup() throws IOException {
- final long start = System.nanoTime();
final Path[] files = getAllRegularFilesInLastModifiedOrder();
final int nFiles = files.length;
final int filesToDelete = Math.max(0, nFiles - MAX_FILES);
@@ -444,8 +443,7 @@ public final class OptimisticTypesPersistence {
// does not increase filesDeleted
}
files[i] = null; // gc eligible
- };
- final long duration = System.nanoTime() - start;
+ }
}
private static Path[] getAllRegularFilesInLastModifiedOrder() throws IOException {
@@ -456,7 +454,7 @@ public final class OptimisticTypesPersistence {
@Override
public boolean test(final Path path) {
return !Files.isDirectory(path);
- };
+ }
})
.map(new Function<Path, PathAndTime>() {
@Override
@@ -497,7 +495,7 @@ public final class OptimisticTypesPersistence {
private static long getTime(final Path path) {
try {
return Files.getLastModifiedTime(path).toMillis();
- } catch (IOException e) {
+ } catch (final IOException e) {
// All files for which we can't retrieve the last modified date will be considered oldest.
return -1L;
}
diff --git a/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java b/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
index d5feac57..792a6255 100644
--- a/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
+++ b/src/jdk/nashorn/internal/codegen/SplitMethodEmitter.java
@@ -25,8 +25,6 @@
package jdk.nashorn.internal.codegen;
-import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
-
import java.util.ArrayList;
import java.util.List;
import jdk.internal.org.objectweb.asm.MethodVisitor;
@@ -34,7 +32,6 @@ import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.BreakableNode;
import jdk.nashorn.internal.ir.LexicalContext;
import jdk.nashorn.internal.ir.SplitNode;
-import jdk.nashorn.internal.runtime.Scope;
/**
* Emitter used for splitting methods. Needs to keep track of if there are jump targets
@@ -65,15 +62,13 @@ public class SplitMethodEmitter extends MethodEmitter {
assert splitNode != null;
final int index = findExternalTarget(lc, label, targetNode);
if (index >= 0) {
- loadCompilerConstant(SCOPE);
- checkcast(Scope.class);
- load(index + 1);
- invoke(Scope.SET_SPLIT_STATE);
- loadUndefined(Type.OBJECT);
- _return(functionNode.getReturnType());
- return;
+ setSplitState(index + 1); // 0 is ordinary return
+ final Type retType = functionNode.getReturnType();
+ loadUndefined(retType);
+ _return(retType);
+ } else {
+ super.splitAwareGoto(lc, label, targetNode);
}
- super.splitAwareGoto(lc, label, targetNode);
}
private int findExternalTarget(final LexicalContext lc, final Label label, final BreakableNode targetNode) {
@@ -94,11 +89,7 @@ public class SplitMethodEmitter extends MethodEmitter {
@Override
MethodEmitter registerReturn() {
setHasReturn();
- loadCompilerConstant(SCOPE);
- checkcast(Scope.class);
- load(0);
- invoke(Scope.SET_SPLIT_STATE);
- return this;
+ return setSplitState(0);
}
final List<Label> getExternalTargets() {
diff --git a/src/jdk/nashorn/internal/codegen/types/Type.java b/src/jdk/nashorn/internal/codegen/types/Type.java
index 9ce94037..9e5e5a24 100644
--- a/src/jdk/nashorn/internal/codegen/types/Type.java
+++ b/src/jdk/nashorn/internal/codegen/types/Type.java
@@ -47,7 +47,6 @@ import static jdk.internal.org.objectweb.asm.Opcodes.T_DOUBLE;
import static jdk.internal.org.objectweb.asm.Opcodes.T_INT;
import static jdk.internal.org.objectweb.asm.Opcodes.T_LONG;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
-
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
@@ -55,8 +54,10 @@ import java.lang.invoke.CallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
+import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import jdk.internal.org.objectweb.asm.Handle;
@@ -104,6 +105,16 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
/** The class for this type */
private final Class<?> clazz;
+ /**
+ * Cache for internal types - this is a query that requires complex stringbuilding inside
+ * ASM and it saves startup time to cache the type mappings
+ */
+ private static final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> INTERNAL_TYPE_CACHE =
+ Collections.synchronizedMap(new WeakHashMap<Class<?>, jdk.internal.org.objectweb.asm.Type>());
+
+ /** Internal ASM type for this Type - computed once at construction */
+ private final jdk.internal.org.objectweb.asm.Type internalType;
+
/** Weights are used to decide which types are "wider" than other types */
protected static final int MIN_WEIGHT = -1;
@@ -122,12 +133,13 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
* @param slots how many bytecode slots the type takes up
*/
Type(final String name, final Class<?> clazz, final int weight, final int slots) {
- this.name = name;
- this.clazz = clazz;
- this.descriptor = jdk.internal.org.objectweb.asm.Type.getDescriptor(clazz);
- this.weight = weight;
+ this.name = name;
+ this.clazz = clazz;
+ this.descriptor = jdk.internal.org.objectweb.asm.Type.getDescriptor(clazz);
+ this.weight = weight;
assert weight >= MIN_WEIGHT && weight <= MAX_WEIGHT : "illegal type weight: " + weight;
- this.slots = slots;
+ this.slots = slots;
+ this.internalType = getInternalType(clazz);
}
/**
@@ -299,7 +311,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
*
* @param typeMap the type map
* @param output data output
- * @throws IOException
+ * @throws IOException if write cannot be completed
*/
public static void writeTypeMap(final Map<Integer, Type> typeMap, final DataOutput output) throws IOException {
if (typeMap == null) {
@@ -329,7 +341,7 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
*
* @param input data input
* @return type map
- * @throws IOException
+ * @throws IOException if read cannot be completed
*/
public static Map<Integer, Type> readTypeMap(final DataInput input) throws IOException {
final int size = input.readInt();
@@ -357,11 +369,22 @@ public abstract class Type implements Comparable<Type>, BytecodeOps {
}
private jdk.internal.org.objectweb.asm.Type getInternalType() {
- return jdk.internal.org.objectweb.asm.Type.getType(getTypeClass());
+ return internalType;
+ }
+
+ private static jdk.internal.org.objectweb.asm.Type lookupInternalType(final Class<?> type) {
+ final Map<Class<?>, jdk.internal.org.objectweb.asm.Type> cache = INTERNAL_TYPE_CACHE;
+ jdk.internal.org.objectweb.asm.Type itype = cache.get(type);
+ if (itype != null) {
+ return itype;
+ }
+ itype = jdk.internal.org.objectweb.asm.Type.getType(type);
+ cache.put(type, itype);
+ return itype;
}
private static jdk.internal.org.objectweb.asm.Type getInternalType(final Class<?> type) {
- return jdk.internal.org.objectweb.asm.Type.getType(type);
+ return lookupInternalType(type);
}
static void invokestatic(final MethodVisitor method, final Call call) {
diff --git a/src/jdk/nashorn/internal/ir/BreakNode.java b/src/jdk/nashorn/internal/ir/BreakNode.java
index c127b1ae..7a607922 100644
--- a/src/jdk/nashorn/internal/ir/BreakNode.java
+++ b/src/jdk/nashorn/internal/ir/BreakNode.java
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -68,4 +69,14 @@ public final class BreakNode extends JumpStatement {
String getStatementName() {
return "break";
}
+
+ @Override
+ public BreakableNode getTarget(final LexicalContext lc) {
+ return lc.getBreakable(getLabelName());
+ }
+
+ @Override
+ public Label getTargetLabel(final BreakableNode target) {
+ return target.getBreakLabel();
+ }
}
diff --git a/src/jdk/nashorn/internal/ir/ContinueNode.java b/src/jdk/nashorn/internal/ir/ContinueNode.java
index d5177db6..ce621791 100644
--- a/src/jdk/nashorn/internal/ir/ContinueNode.java
+++ b/src/jdk/nashorn/internal/ir/ContinueNode.java
@@ -25,6 +25,7 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.codegen.Label;
import jdk.nashorn.internal.ir.annotations.Immutable;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
@@ -67,5 +68,16 @@ public class ContinueNode extends JumpStatement {
String getStatementName() {
return "continue";
}
+
+
+ @Override
+ public BreakableNode getTarget(final LexicalContext lc) {
+ return lc.getContinueTo(getLabelName());
+ }
+
+ @Override
+ public Label getTargetLabel(final BreakableNode target) {
+ return ((LoopNode)target).getContinueLabel();
+ }
}
diff --git a/src/jdk/nashorn/internal/ir/FunctionNode.java b/src/jdk/nashorn/internal/ir/FunctionNode.java
index b911f222..1bbc7ab0 100644
--- a/src/jdk/nashorn/internal/ir/FunctionNode.java
+++ b/src/jdk/nashorn/internal/ir/FunctionNode.java
@@ -238,17 +238,21 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
* Note that even IS_STRICT is one such flag but that requires special handling.
*/
- // parser, lower debugging this function
+ /** parser, print parse tree */
public static final int IS_PRINT_PARSE = 1 << 18;
+ /** parser, print lower parse tree */
public static final int IS_PRINT_LOWER_PARSE = 1 << 19;
+ /** parser, print AST */
public static final int IS_PRINT_AST = 1 << 20;
+ /** parser, print lower AST */
public static final int IS_PRINT_LOWER_AST = 1 << 21;
+ /** parser, print symbols */
public static final int IS_PRINT_SYMBOLS = 1 << 22;
+ // callsite tracing, profiling within this function
/** profile callsites in this function? */
public static final int IS_PROFILE = 1 << 23;
- // callsite tracing, profiling within this function
/** trace callsite enterexit in this function? */
public static final int IS_TRACE_ENTEREXIT = 1 << 24;
@@ -337,7 +341,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag
private FunctionNode(
final FunctionNode functionNode,
final long lastToken,
- Object endParserState,
+ final Object endParserState,
final int flags,
final String name,
final Type returnType,
diff --git a/src/jdk/nashorn/internal/ir/JumpStatement.java b/src/jdk/nashorn/internal/ir/JumpStatement.java
index aabb9de7..5888a836 100644
--- a/src/jdk/nashorn/internal/ir/JumpStatement.java
+++ b/src/jdk/nashorn/internal/ir/JumpStatement.java
@@ -25,6 +25,8 @@
package jdk.nashorn.internal.ir;
+import jdk.nashorn.internal.codegen.Label;
+
/**
* Common base class for jump statements (e.g. {@code break} and {@code continue}).
*/
@@ -82,6 +84,24 @@ public abstract class JumpStatement extends Statement implements JoinPredecessor
abstract String getStatementName();
+ /**
+ * Finds the target for this jump statement in a lexical context.
+ * @param lc the lexical context
+ * @return the target, or null if not found
+ */
+ public abstract BreakableNode getTarget(final LexicalContext lc);
+
+ /**
+ * Returns the label corresponding to this kind of jump statement (either a break or continue label) in the target.
+ * @param target the target. Note that it need not be the target of this jump statement, as the method can retrieve
+ * a label on any passed target as long as the target has a label of the requisite kind. Of course, it is advisable
+ * to invoke the method on a jump statement that targets the breakable.
+ * @return the label of the target corresponding to the kind of jump statement.
+ * @throws ClassCastException if invoked on the kind of breakable node that this jump statement is not prepared to
+ * handle.
+ */
+ public abstract Label getTargetLabel(final BreakableNode target);
+
@Override
public JumpStatement setLocalVariableConversion(final LexicalContext lc, final LocalVariableConversion conversion) {
if(this.conversion == conversion) {
diff --git a/src/jdk/nashorn/internal/ir/LiteralNode.java b/src/jdk/nashorn/internal/ir/LiteralNode.java
index c6ede946..42a9cf4c 100644
--- a/src/jdk/nashorn/internal/ir/LiteralNode.java
+++ b/src/jdk/nashorn/internal/ir/LiteralNode.java
@@ -237,6 +237,10 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
return value;
}
+ private static Expression[] valueToArray(final List<Expression> value) {
+ return value.toArray(new Expression[value.size()]);
+ }
+
/**
* Create a new null literal
*
@@ -981,10 +985,9 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
* @return the new literal node
*/
public static LiteralNode<Expression[]> newInstance(final long token, final int finish, final List<Expression> value) {
- return new ArrayLiteralNode(token, finish, value.toArray(new Expression[value.size()]));
+ return new ArrayLiteralNode(token, finish, valueToArray(value));
}
-
/**
* Create a new array literal based on a parent node (source, token, finish)
*
@@ -994,7 +997,7 @@ public abstract class LiteralNode<T> extends Expression implements PropertyKey {
* @return the new literal node
*/
public static LiteralNode<?> newInstance(final Node parent, final List<Expression> value) {
- return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), value.toArray(new Expression[value.size()]));
+ return new ArrayLiteralNode(parent.getToken(), parent.getFinish(), valueToArray(value));
}
/**
diff --git a/src/jdk/nashorn/internal/ir/Symbol.java b/src/jdk/nashorn/internal/ir/Symbol.java
index c6e4a50f..8d7acfbd 100644
--- a/src/jdk/nashorn/internal/ir/Symbol.java
+++ b/src/jdk/nashorn/internal/ir/Symbol.java
@@ -448,14 +448,25 @@ public final class Symbol implements Comparable<Symbol> {
return (flags & IS_FUNCTION_SELF) != 0;
}
+ /**
+ * Is this a block scoped symbol
+ * @return true if block scoped
+ */
public boolean isBlockScoped() {
return isLet() || isConst();
}
+ /**
+ * Has this symbol been declared
+ * @return true if declared
+ */
public boolean hasBeenDeclared() {
return (flags & HAS_BEEN_DECLARED) != 0;
}
+ /**
+ * Mark this symbol as declared
+ */
public void setHasBeenDeclared() {
if (!hasBeenDeclared()) {
flags |= HAS_BEEN_DECLARED;
diff --git a/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java b/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
index 8ef13d79..06222159 100644
--- a/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
+++ b/src/jdk/nashorn/internal/lookup/MethodHandleFactory.java
@@ -116,6 +116,10 @@ public final class MethodHandleFactory {
private static final String VOID_TAG = "[VOID]";
+ private static void err(final String str) {
+ Context.getContext().getErr().println(str);
+ }
+
/**
* Tracer that is applied before a value is returned from the traced function. It will output the return
* value and its class
@@ -124,13 +128,16 @@ public final class MethodHandleFactory {
* @return return value unmodified
*/
static Object traceReturn(final DebugLogger logger, final Object value) {
- if (logger.isEnabled()) {
- final String str = " return" +
- (VOID_TAG.equals(value) ?
- ";" :
- " " + stripName(value) + "; // [type=" + (value == null ? "null]" : stripName(value.getClass()) + ']'));
+ final String str = " return" +
+ (VOID_TAG.equals(value) ?
+ ";" :
+ " " + stripName(value) + "; // [type=" + (value == null ? "null]" : stripName(value.getClass()) + ']'));
+ if (logger == null) {
+ err(str);
+ } else if (logger.isEnabled()) {
logger.log(TRACE_LEVEL, str);
}
+
return value;
}
@@ -169,8 +176,11 @@ public final class MethodHandleFactory {
}
}
- assert logger != null;
- logger.log(TRACE_LEVEL, sb);
+ if (logger == null) {
+ err(sb.toString());
+ } else {
+ logger.log(TRACE_LEVEL, sb);
+ }
stacktrace(logger);
}
@@ -181,7 +191,12 @@ public final class MethodHandleFactory {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final PrintStream ps = new PrintStream(baos);
new Throwable().printStackTrace(ps);
- logger.log(TRACE_LEVEL, baos.toString());
+ final String st = baos.toString();
+ if (logger == null) {
+ err(st);
+ } else {
+ logger.log(TRACE_LEVEL, st);
+ }
}
private static String argString(final Object arg) {
@@ -201,7 +216,7 @@ public final class MethodHandleFactory {
if (arg instanceof ScriptObject) {
return arg.toString() +
" (map=" + Debug.id(((ScriptObject)arg).getMap()) +
- ")";
+ ')';
}
return arg.toString();
@@ -209,6 +224,18 @@ public final class MethodHandleFactory {
/**
* Add a debug printout to a method handle, tracing parameters and return values
+ * Output will be unconditional to stderr
+ *
+ * @param mh method handle to trace
+ * @param tag start of trace message
+ * @return traced method handle
+ */
+ public static MethodHandle addDebugPrintout(final MethodHandle mh, final Object tag) {
+ return addDebugPrintout(null, Level.OFF, mh, 0, true, tag);
+ }
+
+ /**
+ * Add a debug printout to a method handle, tracing parameters and return values
*
* @param logger a specific logger to which to write the output
* @param level level over which to print
@@ -222,6 +249,20 @@ public final class MethodHandleFactory {
/**
* Add a debug printout to a method handle, tracing parameters and return values
+ * Output will be unconditional to stderr
+ *
+ * @param mh method handle to trace
+ * @param paramStart first param to print/trace
+ * @param printReturnValue should we print/trace return value if available?
+ * @param tag start of trace message
+ * @return traced method handle
+ */
+ public static MethodHandle addDebugPrintout(final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Object tag) {
+ return addDebugPrintout(null, Level.OFF, mh, paramStart, printReturnValue, tag);
+ }
+
+ /**
+ * Add a debug printout to a method handle, tracing parameters and return values
*
* @param logger a specific logger to which to write the output
* @param level level over which to print
@@ -240,7 +281,6 @@ public final class MethodHandleFactory {
return mh;
}
- assert logger != null;
assert TRACE != null;
MethodHandle trace = MethodHandles.insertArguments(TRACE, 0, logger, tag, paramStart);
@@ -428,6 +468,12 @@ public final class MethodHandleFactory {
}
@Override
+ public MethodHandle identity(final Class<?> type) {
+ final MethodHandle mh = MethodHandles.identity(type);
+ return debug(mh, "identity", type);
+ }
+
+ @Override
public MethodHandle asCollector(final MethodHandle handle, final Class<?> arrayType, final int arrayLength) {
final MethodHandle mh = handle.asCollector(arrayType, arrayLength);
return debug(mh, "asCollector", handle, arrayType, arrayLength);
diff --git a/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java b/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java
index 756c0b7b..beaa85c2 100644
--- a/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java
+++ b/src/jdk/nashorn/internal/lookup/MethodHandleFunctionality.java
@@ -173,6 +173,15 @@ public interface MethodHandleFunctionality {
public MethodHandle constant(Class<?> type, Object value);
/**
+ * Wrapper for {@link java.lang.invoke.MethodHandles#identity(Class)}
+ *
+ * @param type type of value
+ *
+ * @return method handle that returns identity argument
+ */
+ public MethodHandle identity(Class<?> type);
+
+ /**
* Wrapper for {@link java.lang.invoke.MethodHandle#asType(MethodType)}
*
* @param handle method handle for type conversion
diff --git a/src/jdk/nashorn/internal/objects/ArrayBufferView.java b/src/jdk/nashorn/internal/objects/ArrayBufferView.java
index 93506c37..efcc3ddc 100644
--- a/src/jdk/nashorn/internal/objects/ArrayBufferView.java
+++ b/src/jdk/nashorn/internal/objects/ArrayBufferView.java
@@ -31,6 +31,7 @@ import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_
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;
diff --git a/src/jdk/nashorn/internal/objects/Global.java b/src/jdk/nashorn/internal/objects/Global.java
index b8ec90fd..eb56de0e 100644
--- a/src/jdk/nashorn/internal/objects/Global.java
+++ b/src/jdk/nashorn/internal/objects/Global.java
@@ -25,23 +25,19 @@
package jdk.nashorn.internal.objects;
-import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.invoke.CallSite;
-import java.lang.invoke.ConstantCallSite;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
import java.lang.reflect.Field;
+import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
@@ -52,8 +48,6 @@ import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
-import jdk.nashorn.internal.codegen.ApplySpecialization;
-import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.lookup.Lookup;
import jdk.nashorn.internal.objects.annotations.Attribute;
import jdk.nashorn.internal.objects.annotations.Property;
@@ -72,6 +66,7 @@ import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.ScriptingFunctions;
+import jdk.nashorn.internal.runtime.Specialization;
import jdk.nashorn.internal.runtime.arrays.ArrayData;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.InvokeByName;
@@ -107,10 +102,6 @@ public final class Global extends ScriptObject implements Scope {
* it's when you start adding property checks for said builtins you have
* problems with guard speed.
*/
- public final Map<String, SwitchPoint> optimisticFunctionMap;
-
- /** Name invalidator for things like call/apply */
- public static final Call BOOTSTRAP = staticCall(MethodHandles.lookup(), Global.class, "invalidateNameBootstrap", CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
/** Nashorn extension: arguments array */
@Property(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_CONFIGURABLE)
@@ -428,9 +419,6 @@ public final class Global extends ScriptObject implements Scope {
private static final MethodHandle LOADWITHNEWGLOBAL = findOwnMH_S("loadWithNewGlobal", Object.class, Object.class, Object[].class);
private static final MethodHandle EXIT = findOwnMH_S("exit", Object.class, Object.class, Object.class);
- /** Invalidate a reserved name, such as "apply" or "call" if assigned */
- public MethodHandle INVALIDATE_RESERVED_NAME = MH.bindTo(findOwnMH_V("invalidateReservedName", void.class, String.class), this);
-
// initialized by nasgen
private static PropertyMap $nasgenmap$;
@@ -482,7 +470,6 @@ public final class Global extends ScriptObject implements Scope {
super(checkAndGetMap(context));
this.context = context;
this.setIsScope();
- this.optimisticFunctionMap = new HashMap<>();
//we can only share one instance of Global constants between globals, or we consume way too much
//memory - this is good enough for most programs
while (gcsInstance.get() == null) {
@@ -574,6 +561,7 @@ public final class Global extends ScriptObject implements Scope {
*
* @param engine ScriptEngine to initialize
*/
+ @SuppressWarnings("hiding")
public void initBuiltinObjects(final ScriptEngine engine) {
if (this.builtinObject != null) {
// already initialized, just return
@@ -918,10 +906,12 @@ public final class Global extends ScriptObject implements Scope {
}
switch (nameStr) {
- case "context":
- return sctxt;
- case "engine":
- return global.engine;
+ case "context":
+ return sctxt;
+ case "engine":
+ return global.engine;
+ default:
+ break;
}
if (self == UNDEFINED) {
@@ -1244,6 +1234,40 @@ public final class Global extends ScriptObject implements Scope {
return instance.function == instance.getBuiltinFunction();
}
+ /**
+ * Get the switchpoint used to check property changes for Function.prototype.apply
+ * @return the switchpoint guarding apply (same as guarding call, and everything else in function)
+ */
+ public static SwitchPoint getBuiltinFunctionApplySwitchPoint() {
+ return ScriptFunction.getPrototype(Global.instance().getBuiltinFunction()).getProperty("apply").getBuiltinSwitchPoint();
+ }
+
+ private static boolean isBuiltinFunctionProperty(final String name) {
+ final Global instance = Global.instance();
+ final ScriptFunction builtinFunction = instance.getBuiltinFunction();
+ if (builtinFunction == null) {
+ return false; //conservative for compile-only mode
+ }
+ final boolean isBuiltinFunction = instance.function == builtinFunction;
+ return isBuiltinFunction && ScriptFunction.getPrototype(builtinFunction).getProperty(name).isBuiltin();
+ }
+
+ /**
+ * Check if the Function.prototype.apply has not been replaced
+ * @return true if Function.prototype.apply has been replaced
+ */
+ public static boolean isBuiltinFunctionPrototypeApply() {
+ return isBuiltinFunctionProperty("apply");
+ }
+
+ /**
+ * Check if the Function.prototype.apply has not been replaced
+ * @return true if Function.prototype.call has been replaced
+ */
+ public static boolean isBuiltinFunctionPrototypeCall() {
+ return isBuiltinFunctionProperty("call");
+ }
+
private ScriptFunction getBuiltinJSAdapter() {
return builtinJSAdapter;
}
@@ -1688,6 +1712,13 @@ public final class Global extends ScriptObject implements Scope {
splitState = state;
}
+ private <T extends ScriptObject> T initConstructorAndSwitchPoint(final String name, final Class<T> clazz) {
+ final T func = initConstructor(name, clazz);
+ tagBuiltinProperties(name, func);
+ return func;
+ }
+
+ @SuppressWarnings("hiding")
private void init(final ScriptEngine engine) {
assert Context.getGlobal() == this : "this global is not set as current";
@@ -1702,8 +1733,19 @@ public final class Global extends ScriptObject implements Scope {
// initialize global function properties
this.eval = this.builtinEval = ScriptFunctionImpl.makeFunction("eval", EVAL);
- this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT,
- new MethodHandle[] { GlobalFunctions.PARSEINT_OI, GlobalFunctions.PARSEINT_O });
+ this.parseInt = ScriptFunctionImpl.makeFunction("parseInt", GlobalFunctions.PARSEINT,
+ new Specialization[] {
+ new Specialization(GlobalFunctions.PARSEINT_Z),
+ new Specialization(GlobalFunctions.PARSEINT_I),
+ new Specialization(GlobalFunctions.PARSEINT_J),
+ new Specialization(GlobalFunctions.PARSEINT_OI),
+ new Specialization(GlobalFunctions.PARSEINT_O) });
+ this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT);
+ this.isNaN = ScriptFunctionImpl.makeFunction("isNaN", GlobalFunctions.IS_NAN,
+ new Specialization[] {
+ new Specialization(GlobalFunctions.IS_NAN_I),
+ new Specialization(GlobalFunctions.IS_NAN_J),
+ new Specialization(GlobalFunctions.IS_NAN_D) });
this.parseFloat = ScriptFunctionImpl.makeFunction("parseFloat", GlobalFunctions.PARSEFLOAT);
this.isNaN = ScriptFunctionImpl.makeFunction("isNaN", GlobalFunctions.IS_NAN);
this.isFinite = ScriptFunctionImpl.makeFunction("isFinite", GlobalFunctions.IS_FINITE);
@@ -1720,15 +1762,15 @@ public final class Global extends ScriptObject implements Scope {
this.quit = ScriptFunctionImpl.makeFunction("quit", EXIT);
// built-in constructors
- this.builtinArray = initConstructor("Array", ScriptFunction.class);
- this.builtinBoolean = initConstructor("Boolean", ScriptFunction.class);
- this.builtinDate = initConstructor("Date", ScriptFunction.class);
- this.builtinJSON = initConstructor("JSON", ScriptObject.class);
- this.builtinJSAdapter = initConstructor("JSAdapter", ScriptFunction.class);
- this.builtinMath = initConstructor("Math", ScriptObject.class);
- this.builtinNumber = initConstructor("Number", ScriptFunction.class);
- this.builtinRegExp = initConstructor("RegExp", ScriptFunction.class);
- this.builtinString = initConstructor("String", ScriptFunction.class);
+ this.builtinArray = initConstructorAndSwitchPoint("Array", ScriptFunction.class);
+ this.builtinBoolean = initConstructorAndSwitchPoint("Boolean", ScriptFunction.class);
+ this.builtinDate = initConstructorAndSwitchPoint("Date", ScriptFunction.class);
+ this.builtinJSON = initConstructorAndSwitchPoint("JSON", ScriptObject.class);
+ this.builtinJSAdapter = initConstructorAndSwitchPoint("JSAdapter", ScriptFunction.class);
+ this.builtinMath = initConstructorAndSwitchPoint("Math", ScriptObject.class);
+ this.builtinNumber = initConstructorAndSwitchPoint("Number", ScriptFunction.class);
+ this.builtinRegExp = initConstructorAndSwitchPoint("RegExp", ScriptFunction.class);
+ this.builtinString = initConstructorAndSwitchPoint("String", ScriptFunction.class);
// initialize String.prototype.length to 0
// add String.prototype.length
@@ -1830,6 +1872,8 @@ public final class Global extends ScriptObject implements Scope {
// Error.prototype.message = "";
errorProto.set(NativeError.MESSAGE, "", 0);
+ tagBuiltinProperties("Error", builtinError);
+
this.builtinEvalError = initErrorSubtype("EvalError", errorProto);
this.builtinRangeError = initErrorSubtype("RangeError", errorProto);
this.builtinReferenceError = initErrorSubtype("ReferenceError", errorProto);
@@ -1844,6 +1888,7 @@ public final class Global extends ScriptObject implements Scope {
prototype.set(NativeError.NAME, name, 0);
prototype.set(NativeError.MESSAGE, "", 0);
prototype.setInitialProto(errorProto);
+ tagBuiltinProperties(name, cons);
return cons;
}
@@ -1910,17 +1955,18 @@ public final class Global extends ScriptObject implements Scope {
}
private void initTypedArray() {
- this.builtinArrayBuffer = initConstructor("ArrayBuffer", ScriptFunction.class);
- this.builtinDataView = initConstructor("DataView", ScriptFunction.class);
- this.builtinInt8Array = initConstructor("Int8Array", ScriptFunction.class);
- this.builtinUint8Array = initConstructor("Uint8Array", ScriptFunction.class);
- this.builtinUint8ClampedArray = initConstructor("Uint8ClampedArray", ScriptFunction.class);
- this.builtinInt16Array = initConstructor("Int16Array", ScriptFunction.class);
- this.builtinUint16Array = initConstructor("Uint16Array", ScriptFunction.class);
- this.builtinInt32Array = initConstructor("Int32Array", ScriptFunction.class);
- this.builtinUint32Array = initConstructor("Uint32Array", ScriptFunction.class);
- this.builtinFloat32Array = initConstructor("Float32Array", ScriptFunction.class);
- this.builtinFloat64Array = initConstructor("Float64Array", ScriptFunction.class);
+ this.builtinArrayBuffer = initConstructorAndSwitchPoint("ArrayBuffer", ScriptFunction.class);
+ this.builtinDataView = initConstructorAndSwitchPoint("DataView", ScriptFunction.class);
+ this.builtinInt8Array = initConstructorAndSwitchPoint("Int8Array", ScriptFunction.class);
+ this.builtinUint8Array = initConstructorAndSwitchPoint("Uint8Array", ScriptFunction.class);
+ this.builtinUint8ClampedArray = initConstructorAndSwitchPoint("Uint8ClampedArray", ScriptFunction.class);
+ this.builtinInt16Array = initConstructorAndSwitchPoint("Int16Array", ScriptFunction.class);
+ this.builtinUint16Array = initConstructorAndSwitchPoint("Uint16Array", ScriptFunction.class);
+ this.builtinInt32Array = initConstructorAndSwitchPoint("Int32Array", ScriptFunction.class);
+ this.builtinUint32Array = initConstructorAndSwitchPoint("Uint32Array", ScriptFunction.class);
+ this.builtinFloat32Array = initConstructorAndSwitchPoint("Float32Array", ScriptFunction.class);
+ this.builtinFloat64Array = initConstructorAndSwitchPoint("Float64Array", ScriptFunction.class);
+
}
private void copyBuiltins() {
@@ -1993,10 +2039,6 @@ public final class Global extends ScriptObject implements Scope {
return UNDEFINED;
}
- /**
- * These classes are generated by nasgen tool and so we have to use
- * reflection to load and create new instance of these classes.
- */
private <T extends ScriptObject> T initConstructor(final String name, final Class<T> clazz) {
try {
// Assuming class name pattern for built-in JS constructors.
@@ -2021,12 +2063,52 @@ public final class Global extends ScriptObject implements Scope {
}
res.setIsBuiltin();
+
return res;
} catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
+ private List<jdk.nashorn.internal.runtime.Property> extractBuiltinProperties(final String name, final ScriptObject func) {
+ final List<jdk.nashorn.internal.runtime.Property> list = new ArrayList<>();
+
+ list.addAll(Arrays.asList(func.getMap().getProperties()));
+
+ if (func instanceof ScriptFunction) {
+ final ScriptObject proto = ScriptFunction.getPrototype((ScriptFunction)func);
+ if (proto != null) {
+ list.addAll(Arrays.asList(proto.getMap().getProperties()));
+ }
+ }
+
+ final jdk.nashorn.internal.runtime.Property prop = getProperty(name);
+ if (prop != null) {
+ list.add(prop);
+ }
+
+ return list;
+ }
+
+ /**
+ * Given a builtin object, traverse its properties recursively and associate them with a name that
+ * will be a key to their invalidation switchpoint.
+ * @param name name for key
+ * @param func builtin script object
+ */
+ private void tagBuiltinProperties(final String name, final ScriptObject func) {
+ SwitchPoint sp = context.getBuiltinSwitchPoint(name);
+ if (sp == null) {
+ sp = context.newBuiltinSwitchPoint(name);
+ }
+
+ //get all builtin properties in this builtin object and register switchpoints keyed on the propery name,
+ //one overwrite destroys all for now, e.g. Function.prototype.apply = 17; also destroys Function.prototype.call etc
+ for (final jdk.nashorn.internal.runtime.Property prop : extractBuiltinProperties(name, func)) {
+ prop.setBuiltinSwitchPoint(sp);
+ }
+ }
+
// Function and Object constructors are inter-dependent. Also,
// Function.prototype
// functions are not properly initialized. We fix the references here.
@@ -2035,7 +2117,8 @@ public final class Global extends ScriptObject implements Scope {
// to play with object references carefully!!
private void initFunctionAndObject() {
// First-n-foremost is Function
- this.builtinFunction = initConstructor("Function", ScriptFunction.class);
+
+ this.builtinFunction = initConstructor("Function", ScriptFunction.class);
// create global anonymous function
final ScriptFunction anon = ScriptFunctionImpl.newAnonymousFunction();
@@ -2101,13 +2184,6 @@ public final class Global extends ScriptObject implements Scope {
}
}
- //make sure apply and call have the same invalidation switchpoint
- final SwitchPoint sp = new SwitchPoint();
- optimisticFunctionMap.put("apply", sp);
- optimisticFunctionMap.put("call", sp);
- getFunctionPrototype().getProperty("apply").setChangeCallback(sp);
- getFunctionPrototype().getProperty("call").setChangeCallback(sp);
-
properties = getObjectPrototype().getMap().getProperties();
for (final jdk.nashorn.internal.runtime.Property property : properties) {
@@ -2125,10 +2201,10 @@ public final class Global extends ScriptObject implements Scope {
}
}
}
- }
- private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
- return MH.findVirtual(MethodHandles.lookup(), Global.class, name, MH.type(rtype, types));
+ tagBuiltinProperties("Object", builtinObject);
+ tagBuiltinProperties("Function", builtinFunction);
+ tagBuiltinProperties("Function", anon);
}
private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
@@ -2147,62 +2223,4 @@ public final class Global extends ScriptObject implements Scope {
protected boolean isGlobal() {
return true;
}
-
- /**
- * Check if there is a switchpoint for a reserved name. If there
- * is, it must be invalidated upon properties with this name
- * @param name property name
- * @return switchpoint for invalidating this property, or null if not registered
- */
- public SwitchPoint getChangeCallback(final String name) {
- return optimisticFunctionMap.get(name);
- }
-
- /**
- * Is this a special name, that might be subject to invalidation
- * on write, such as "apply" or "call"
- * @param name name to check
- * @return true if special name
- */
- public boolean isSpecialName(final String name) {
- return getChangeCallback(name) != null;
- }
-
- /**
- * Check if a reserved property name is invalidated
- * @param name property name
- * @return true if someone has written to it since Global was instantiated
- */
- public boolean isSpecialNameValid(final String name) {
- final SwitchPoint sp = getChangeCallback(name);
- return sp != null && !sp.hasBeenInvalidated();
- }
-
- /**
- * Tag a reserved name as invalidated - used when someone writes
- * to a property with this name - overly conservative, but link time
- * is too late to apply e.g. apply-&gt;call specialization
- * @param name property name
- */
- public void invalidateReservedName(final String name) {
- final SwitchPoint sp = getChangeCallback(name);
- if (sp != null) {
- getContext().getLogger(ApplySpecialization.class).info("Overwrote special name '" + name +"' - invalidating switchpoint");
- SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
- }
- }
-
- /**
- * Bootstrapper for invalidating a builtin name
- * @param lookup lookup
- * @param name name to invalidate
- * @param type methodhandle type
- * @return callsite for invalidator
- */
- public static CallSite invalidateNameBootstrap(final MethodHandles.Lookup lookup, final String name, final MethodType type) {
- final MethodHandle target = MH.insertArguments(Global.instance().INVALIDATE_RESERVED_NAME, 0, name);
- return new ConstantCallSite(target);
- }
-
-
}
diff --git a/src/jdk/nashorn/internal/objects/NativeArray.java b/src/jdk/nashorn/internal/objects/NativeArray.java
index 163e86a4..c83e8883 100644
--- a/src/jdk/nashorn/internal/objects/NativeArray.java
+++ b/src/jdk/nashorn/internal/objects/NativeArray.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -33,8 +33,8 @@ import static jdk.nashorn.internal.runtime.arrays.ArrayIndex.isValidArrayIndex;
import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.arrayLikeIterator;
import static jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator.reverseArrayLikeIterator;
import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_STRICT;
-
import java.lang.invoke.MethodHandle;
+import java.lang.invoke.SwitchPoint;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -52,12 +52,13 @@ 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.Setter;
-import jdk.nashorn.internal.objects.annotations.SpecializedConstructor;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
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.OptimisticBuiltins;
import jdk.nashorn.internal.runtime.PropertyDescriptor;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
@@ -67,17 +68,20 @@ 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.ContinuousArrayData;
+import jdk.nashorn.internal.runtime.arrays.IntElements;
+import jdk.nashorn.internal.runtime.arrays.IntOrLongElements;
import jdk.nashorn.internal.runtime.arrays.IteratorAction;
+import jdk.nashorn.internal.runtime.arrays.NumericElements;
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
* keyed values. All other values are stored in spill.
*/
@ScriptClass("Array")
-public final class NativeArray extends ScriptObject {
+public final class NativeArray extends ScriptObject implements OptimisticBuiltins {
private static final Object JOIN = new Object();
private static final Object EVERY_CALLBACK_INVOKER = new Object();
private static final Object SOME_CALLBACK_INVOKER = new Object();
@@ -88,6 +92,16 @@ public final class NativeArray extends ScriptObject {
private static final Object CALL_CMP = new Object();
private static final Object TO_LOCALE_STRING = new Object();
+ private SwitchPoint lengthMadeNotWritableSwitchPoint;
+ private PushLinkLogic pushLinkLogic;
+ private PopLinkLogic popLinkLogic;
+
+ /**
+ * Index for the modification SwitchPoint that triggers when length
+ * becomes not writable
+ */
+ private static final int LENGTH_NOT_WRITABLE_SWITCHPOINT = 0;
+
/*
* Constructors.
*/
@@ -420,6 +434,28 @@ public final class NativeArray extends ScriptObject {
return getArray().asObjectArray();
}
+ @Override
+ public void setIsLengthNotWritable() {
+ super.setIsLengthNotWritable();
+ /*
+ * Switchpoints are created lazily. If we link any push or pop site,
+ * we need to create the "length made not writable" switchpoint, if it
+ * doesn't exist.
+ *
+ * If the switchpoint already exists, we will find it here, and invalidate
+ * it, invalidating all previous callsites that use it.
+ *
+ * If the switchpoint doesn't exist, no push/pop has been linked so far,
+ * because that would create it too. We invalidate it immediately and the
+ * check link logic for all future callsites will fail immediately at link
+ * time
+ */
+ if (lengthMadeNotWritableSwitchPoint == null) {
+ lengthMadeNotWritableSwitchPoint = new SwitchPoint();
+ }
+ SwitchPoint.invalidateAll(new SwitchPoint[] { lengthMadeNotWritableSwitchPoint });
+ }
+
/**
* ECMA 15.4.3.2 Array.isArray ( arg )
*
@@ -638,7 +674,7 @@ public final class NativeArray extends ScriptObject {
* @param self self reference
* @return the new NativeArray
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeArray construct(final boolean newObj, final Object self) {
return new NativeArray(0);
}
@@ -653,7 +689,7 @@ public final class NativeArray extends ScriptObject {
* @param element first element
* @return the new NativeArray
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object construct(final boolean newObj, final Object self, final boolean element) {
return new NativeArray(new Object[] { element });
}
@@ -668,7 +704,7 @@ public final class NativeArray extends ScriptObject {
* @param length array length
* @return the new NativeArray
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeArray construct(final boolean newObj, final Object self, final int length) {
if (length >= 0) {
return new NativeArray(length);
@@ -687,7 +723,7 @@ public final class NativeArray extends ScriptObject {
* @param length array length
* @return the new NativeArray
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeArray construct(final boolean newObj, final Object self, final long length) {
if (length >= 0L && length <= JSType.MAX_UINT) {
return new NativeArray(length);
@@ -706,7 +742,7 @@ public final class NativeArray extends ScriptObject {
* @param length array length
* @return the new NativeArray
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeArray construct(final boolean newObj, final Object self, final double length) {
final long uint32length = JSType.toUint32(length);
@@ -721,7 +757,7 @@ public final class NativeArray extends ScriptObject {
* ECMA 15.4.4.4 Array.prototype.concat ( [ item1 [ , item2 [ , ... ] ] ] )
*
* @param self self reference
- * @param args arguments to concat
+ * @param args arguments
* @return resulting NativeArray
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, arity = 1)
@@ -793,6 +829,68 @@ public final class NativeArray extends ScriptObject {
}
/**
+ * Specialization of pop for ContinuousArrayData
+ * The link guard checks that the array is continuous AND not empty.
+ * The runtime guard checks that the guard is continuous (CCE otherwise)
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @return element popped
+ * @throws ClassCastException if array is empty, facilitating Undefined return value
+ */
+ @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
+ public static int popInt(final Object self) {
+ //must be non empty IntArrayData
+ return getContinuousNonEmptyArrayDataCCE(self, IntElements.class).fastPopInt();
+ }
+
+ /**
+ * Specialization of pop for ContinuousArrayData
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @return element popped
+ * @throws ClassCastException if array is empty, facilitating Undefined return value
+ */
+ @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
+ public static long popLong(final Object self) {
+ //must be non empty Int or LongArrayData
+ return getContinuousNonEmptyArrayDataCCE(self, IntOrLongElements.class).fastPopLong();
+ }
+
+ /**
+ * Specialization of pop for ContinuousArrayData
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @return element popped
+ * @throws ClassCastException if array is empty, facilitating Undefined return value
+ */
+ @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
+ public static double popDouble(final Object self) {
+ //must be non empty int long or double array data
+ return getContinuousNonEmptyArrayDataCCE(self, NumericElements.class).fastPopDouble();
+ }
+
+ /**
+ * Specialization of pop for ContinuousArrayData
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @return element popped
+ * @throws ClassCastException if array is empty, facilitating Undefined return value
+ */
+ @SpecializedFunction(name="pop", linkLogic=PopLinkLogic.class)
+ public static Object popObject(final Object self) {
+ //can be any data, because the numeric ones will throw cce and force relink
+ return getContinuousArrayDataCCE(self, null).fastPopObject();
+ }
+
+ /**
* ECMA 15.4.4.6 Array.prototype.pop ()
*
* @param self self reference
@@ -829,6 +927,62 @@ public final class NativeArray extends ScriptObject {
/**
* ECMA 15.4.4.7 Array.prototype.push (args...)
*
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @param arg a primitive to push
+ * @return array length after push
+ */
+ @SpecializedFunction(linkLogic=PushLinkLogic.class)
+ public static long push(final Object self, final int arg) {
+ return getContinuousArrayDataCCE(self, Integer.class).fastPush(arg);
+ }
+
+ /**
+ * ECMA 15.4.4.7 Array.prototype.push (args...)
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @param arg a primitive to push
+ * @return array length after push
+ */
+ @SpecializedFunction(linkLogic=PushLinkLogic.class)
+ public static long push(final Object self, final long arg) {
+ return getContinuousArrayDataCCE(self, Long.class).fastPush(arg);
+ }
+
+ /**
+ * ECMA 15.4.4.7 Array.prototype.push (args...)
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @param arg a primitive to push
+ * @return array length after push
+ */
+ @SpecializedFunction(linkLogic=PushLinkLogic.class)
+ public static long push(final Object self, final double arg) {
+ return getContinuousArrayDataCCE(self, Double.class).fastPush(arg);
+ }
+
+ /**
+ * ECMA 15.4.4.7 Array.prototype.push (args...)
+ *
+ * Primitive specialization, {@link LinkLogic}
+ *
+ * @param self self reference
+ * @param arg a primitive to push
+ * @return array length after push
+ */
+ @SpecializedFunction(name="push", linkLogic=PushLinkLogic.class)
+ public static long pushObject(final Object self, final Object arg) {
+ return getContinuousArrayDataCCE(self, Object.class).fastPush(arg);
+ }
+
+ /**
+ * ECMA 15.4.4.7 Array.prototype.push (args...)
+ *
* @param self self reference
* @param args arguments to push
* @return array length after pushes
@@ -857,61 +1011,6 @@ 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
@@ -925,7 +1024,7 @@ public final class NativeArray extends ScriptObject {
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));
+ sobj.setArray(arrayData.push(true, arg));
return length + 1;
}
@@ -1584,6 +1683,192 @@ public final class NativeArray extends ScriptObject {
@Override
public String toString() {
- return "NativeArray@" + Debug.id(this) + '@' + getArray().getClass().getSimpleName();
+ return "NativeArray@" + Debug.id(this) + " [" + getArray().getClass().getSimpleName() + ']';
+ }
+
+ @Override
+ public SpecializedFunction.LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) {
+ if (clazz == PushLinkLogic.class) {
+ return pushLinkLogic == null ? new PushLinkLogic(this) : pushLinkLogic;
+ } else if (clazz == PopLinkLogic.class) {
+ return popLinkLogic == null ? new PopLinkLogic(this) : pushLinkLogic;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean hasPerInstanceAssumptions() {
+ return true; //length switchpoint
+ }
+
+ /**
+ * This is an abstract super class that contains common functionality for all
+ * specialized optimistic builtins in NativeArray. For example, it handles the
+ * modification switchpoint which is touched when length is written.
+ */
+ private static abstract class ArrayLinkLogic extends SpecializedFunction.LinkLogic {
+ private final NativeArray array;
+
+ protected ArrayLinkLogic(final NativeArray array) {
+ this.array = array;
+ }
+
+ private SwitchPoint getSwitchPoint() {
+ return array.lengthMadeNotWritableSwitchPoint;
+ }
+
+ private SwitchPoint newSwitchPoint() {
+ assert array.lengthMadeNotWritableSwitchPoint == null;
+ final SwitchPoint sp = new SwitchPoint();
+ array.lengthMadeNotWritableSwitchPoint = sp;
+ return sp;
+ }
+
+ protected static ContinuousArrayData getContinuousArrayData(final Object self) {
+ try {
+ //cast to NativeArray, to avoid cases like x = {0:0, 1:1}, x.length = 2, where we can't use the array push/pop
+ return (ContinuousArrayData)((NativeArray)self).getArray();
+ } catch (final Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Push and pop callsites can throw ClassCastException as a mechanism to have them
+ * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x)
+ * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink
+ */
+ @Override
+ public Class<? extends Throwable> getRelinkException() {
+ return ClassCastException.class;
+ }
+
+ @Override
+ public boolean hasModificationSwitchPoints() {
+ return getSwitchPoint() != null;
+ }
+
+ @Override
+ public boolean hasModificationSwitchPoint(final int index) {
+ assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
+ return hasModificationSwitchPoints();
+ }
+
+ @Override
+ public SwitchPoint getOrCreateModificationSwitchPoint(final int index) {
+ assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
+ SwitchPoint sp = getSwitchPoint();
+ if (sp == null) {
+ sp = newSwitchPoint();
+ }
+ return sp;
+ }
+
+ @Override
+ public SwitchPoint[] getOrCreateModificationSwitchPoints() {
+ return new SwitchPoint[] { getOrCreateModificationSwitchPoint(LENGTH_NOT_WRITABLE_SWITCHPOINT) };
+ }
+
+ @Override
+ public void invalidateModificationSwitchPoint(final int index) {
+ assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
+ invalidateModificationSwitchPoints();
+ }
+
+ @Override
+ public void invalidateModificationSwitchPoints() {
+ final SwitchPoint sp = getSwitchPoint();
+ assert sp != null : "trying to invalidate non-existant modified SwitchPoint";
+ if (!sp.hasBeenInvalidated()) {
+ SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
+ }
+ }
+
+ @Override
+ public boolean hasInvalidatedModificationSwitchPoint(final int index) {
+ assert index == LENGTH_NOT_WRITABLE_SWITCHPOINT;
+ return hasInvalidatedModificationSwitchPoints();
+ }
+
+ @Override
+ public boolean hasInvalidatedModificationSwitchPoints() {
+ final SwitchPoint sp = getSwitchPoint();
+ return sp != null && !sp.hasBeenInvalidated();
+ }
+ }
+
+ /**
+ * This is linker logic for optimistic pushes
+ */
+ private static final class PushLinkLogic extends ArrayLinkLogic {
+ private PushLinkLogic(final NativeArray array) {
+ super(array);
+ }
+
+ @Override
+ public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
+ return getContinuousArrayData(self) != null;
+ }
+ }
+
+ /**
+ * This is linker logic for optimistic pops
+ */
+ private static final class PopLinkLogic extends ArrayLinkLogic {
+ private PopLinkLogic(final NativeArray array) {
+ super(array);
+ }
+
+ /**
+ * We need to check if we are dealing with a continuous non empty array data here,
+ * as pop with a primitive return value returns undefined for arrays with length 0
+ */
+ @Override
+ public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
+ final ContinuousArrayData data = getContinuousNonEmptyArrayData(self);
+ if (data != null) {
+ final Class<?> elementType = data.getElementType();
+ final Class<?> returnType = desc.getMethodType().returnType();
+ final boolean typeFits = JSType.getAccessorTypeIndex(returnType) >= JSType.getAccessorTypeIndex(elementType);
+ return typeFits;
+ }
+ return false;
+ }
+
+ private static ContinuousArrayData getContinuousNonEmptyArrayData(final Object self) {
+ final ContinuousArrayData data = getContinuousArrayData(self);
+ if (data != null) {
+ return data.length() == 0 ? null : data;
+ }
+ return null;
+ }
+ }
+
+ //runtime calls for push and pops. they could be used as guards, but they also perform the runtime logic,
+ //so rather than synthesizing them into a guard method handle that would also perform the push on the
+ //retrieved receiver, we use this as runtime logic
+
+ //TODO - fold these into the Link logics, but I'll do that as a later step, as I want to do a checkin
+ //where everything works first
+
+ private static final <T> ContinuousArrayData getContinuousNonEmptyArrayDataCCE(final Object self, final Class<T> clazz) {
+ try {
+ @SuppressWarnings("unchecked")
+ final ContinuousArrayData data = (ContinuousArrayData)(T)((NativeArray)self).getArray();
+ if (data.length() != 0L) {
+ return data; //if length is 0 we cannot pop and have to relink, because then we'd have to return an undefined, which is a wider type than e.g. int
+ }
+ } catch (final NullPointerException e) {
+ //fallthru
+ }
+ throw new ClassCastException();
+ }
+
+ private static final ContinuousArrayData getContinuousArrayDataCCE(final Object self, final Class<?> elementType) {
+ try {
+ return (ContinuousArrayData)((NativeArray)self).getArray(elementType); //ensure element type can fit "elementType"
+ } catch (final NullPointerException e) {
+ throw new ClassCastException();
+ }
}
}
diff --git a/src/jdk/nashorn/internal/objects/NativeDataView.java b/src/jdk/nashorn/internal/objects/NativeDataView.java
index 470803ef..0adde064 100644
--- a/src/jdk/nashorn/internal/objects/NativeDataView.java
+++ b/src/jdk/nashorn/internal/objects/NativeDataView.java
@@ -27,7 +27,6 @@ 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.ScriptRuntime.UNDEFINED;
-
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import jdk.nashorn.internal.objects.annotations.Attribute;
@@ -35,7 +34,6 @@ 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.SpecializedConstructor;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyMap;
@@ -156,7 +154,7 @@ public class NativeDataView extends ScriptObject {
* @param offset offset in bytes from the start of the ArrayBuffer
* @return newly constructed DataView object
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeDataView constructor(final boolean newObj, final Object self, final Object arrBuf, final int offset) {
if (!(arrBuf instanceof NativeArrayBuffer)) {
throw typeError("not.an.arraybuffer.in.dataview");
@@ -174,7 +172,7 @@ public class NativeDataView extends ScriptObject {
* @param length is the number of bytes from the offset that this DataView will reference
* @return newly constructed DataView object
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeDataView constructor(final boolean newObj, final Object self, final Object arrBuf, final int offset, final int length) {
if (!(arrBuf instanceof NativeArrayBuffer)) {
throw typeError("not.an.arraybuffer.in.dataview");
diff --git a/src/jdk/nashorn/internal/objects/NativeDate.java b/src/jdk/nashorn/internal/objects/NativeDate.java
index eac59c46..c85016a5 100644
--- a/src/jdk/nashorn/internal/objects/NativeDate.java
+++ b/src/jdk/nashorn/internal/objects/NativeDate.java
@@ -30,7 +30,6 @@ import static java.lang.Double.isInfinite;
import static java.lang.Double.isNaN;
import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
-
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.Callable;
@@ -38,7 +37,7 @@ 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.ScriptClass;
-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.parser.DateParser;
import jdk.nashorn.internal.runtime.ConsString;
@@ -155,7 +154,7 @@ public final class NativeDate extends ScriptObject {
* @param self self references
* @return Date representing now
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object construct(final boolean isNew, final Object self) {
final NativeDate result = new NativeDate();
return isNew ? result : toStringImpl(result, FORMAT_DATE_TIME);
diff --git a/src/jdk/nashorn/internal/objects/NativeDebug.java b/src/jdk/nashorn/internal/objects/NativeDebug.java
index 20dd85e5..3d8f1095 100644
--- a/src/jdk/nashorn/internal/objects/NativeDebug.java
+++ b/src/jdk/nashorn/internal/objects/NativeDebug.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.Objects;
@@ -262,8 +261,8 @@ public final class NativeDebug extends ScriptObject {
/**
* Set the event queue capacity
- * @param self
- * @param newCapacity
+ * @param self an event queue
+ * @param newCapacity new capacity
*/
@Function(attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR)
public static void setEventQueueCapacity(final Object self, final Object newCapacity) {
diff --git a/src/jdk/nashorn/internal/objects/NativeFloat32Array.java b/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
index b2639c45..049d8fa8 100644
--- a/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeFloat32Array.java
@@ -26,7 +26,6 @@
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;
@@ -85,6 +84,11 @@ public final class NativeFloat32Array extends ArrayBufferView {
}
@Override
+ public Class<?> getElementType() {
+ return double.class;
+ }
+
+ @Override
protected MethodHandle getGetElem() {
return GET_ELEM;
}
diff --git a/src/jdk/nashorn/internal/objects/NativeFloat64Array.java b/src/jdk/nashorn/internal/objects/NativeFloat64Array.java
index 93162952..79268cc5 100644
--- a/src/jdk/nashorn/internal/objects/NativeFloat64Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeFloat64Array.java
@@ -26,7 +26,6 @@
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;
@@ -94,6 +93,11 @@ public final class NativeFloat64Array extends ArrayBufferView {
return SET_ELEM;
}
+ @Override
+ public Class<?> getElementType() {
+ return double.class;
+ }
+
private double getElem(final int index) {
try {
return nb.get(index);
diff --git a/src/jdk/nashorn/internal/objects/NativeInt16Array.java b/src/jdk/nashorn/internal/objects/NativeInt16Array.java
index ad61cbd2..72e24a20 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt16Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt16Array.java
@@ -26,7 +26,6 @@
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;
@@ -95,6 +94,11 @@ public final class NativeInt16Array extends ArrayBufferView {
return SET_ELEM;
}
+ @Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
private int getElem(final int index) {
try {
return nb.get(index);
diff --git a/src/jdk/nashorn/internal/objects/NativeInt32Array.java b/src/jdk/nashorn/internal/objects/NativeInt32Array.java
index b018f04e..56b3106f 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt32Array.java
@@ -26,7 +26,6 @@
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;
@@ -113,6 +112,11 @@ public final class NativeInt32Array extends ArrayBufferView {
}
@Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
+ @Override
public int getInt(final int index) {
return getElem(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeInt8Array.java b/src/jdk/nashorn/internal/objects/NativeInt8Array.java
index 6f9f6e7b..68e6b0bc 100644
--- a/src/jdk/nashorn/internal/objects/NativeInt8Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeInt8Array.java
@@ -26,7 +26,6 @@
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;
@@ -93,6 +92,11 @@ public final class NativeInt8Array extends ArrayBufferView {
return SET_ELEM;
}
+ @Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
private int getElem(final int index) {
try {
return nb.get(index);
diff --git a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
index f2b1bef4..c5ca9101 100644
--- a/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
+++ b/src/jdk/nashorn/internal/objects/NativeJSAdapter.java
@@ -48,7 +48,6 @@ import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
-import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
import jdk.nashorn.internal.scripts.JO;
diff --git a/src/jdk/nashorn/internal/objects/NativeRegExp.java b/src/jdk/nashorn/internal/objects/NativeRegExp.java
index 01806d44..33ef2849 100644
--- a/src/jdk/nashorn/internal/objects/NativeRegExp.java
+++ b/src/jdk/nashorn/internal/objects/NativeRegExp.java
@@ -33,13 +33,14 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
+
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.Property;
import jdk.nashorn.internal.objects.annotations.ScriptClass;
-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.BitVector;
import jdk.nashorn.internal.runtime.JSType;
@@ -143,7 +144,7 @@ public final class NativeRegExp extends ScriptObject {
* @param self self reference
* @return new NativeRegExp
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeRegExp constructor(final boolean isNew, final Object self) {
return new NativeRegExp("", "");
}
@@ -158,7 +159,7 @@ public final class NativeRegExp extends ScriptObject {
* @param pattern pattern
* @return new NativeRegExp
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeRegExp constructor(final boolean isNew, final Object self, final Object pattern) {
return newRegExp(pattern, UNDEFINED);
}
@@ -174,7 +175,7 @@ public final class NativeRegExp extends ScriptObject {
* @param flags flags
* @return new NativeRegExp
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static NativeRegExp constructor(final boolean isNew, final Object self, final Object pattern, final Object flags) {
return newRegExp(pattern, flags);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeString.java b/src/jdk/nashorn/internal/objects/NativeString.java
index 69d34c2d..850af622 100644
--- a/src/jdk/nashorn/internal/objects/NativeString.java
+++ b/src/jdk/nashorn/internal/objects/NativeString.java
@@ -29,7 +29,6 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -49,11 +48,12 @@ 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.SpecializedConstructor;
import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
import jdk.nashorn.internal.objects.annotations.Where;
import jdk.nashorn.internal.runtime.ConsString;
import jdk.nashorn.internal.runtime.JSType;
+import jdk.nashorn.internal.runtime.OptimisticBuiltins;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptObject;
@@ -67,7 +67,7 @@ import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
* ECMA 15.5 String Objects.
*/
@ScriptClass("String")
-public final class NativeString extends ScriptObject {
+public final class NativeString extends ScriptObject implements OptimisticBuiltins {
private final CharSequence value;
@@ -568,6 +568,14 @@ public final class NativeString extends ScriptObject {
return pos < 0 || pos >= str.length() ? "" : String.valueOf(str.charAt(pos));
}
+ private static int getValidChar(final Object self, final int pos) {
+ try {
+ return ((CharSequence)self).charAt(pos);
+ } catch (final IndexOutOfBoundsException e) {
+ throw new ClassCastException();
+ }
+ }
+
/**
* ECMA 15.5.4.5 String.prototype.charCodeAt (pos)
* @param self self reference
@@ -576,7 +584,9 @@ public final class NativeString extends ScriptObject {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static double charCodeAt(final Object self, final Object pos) {
- return charCodeAtImpl(checkObjectToString(self), JSType.toInteger(pos));
+ final String str = checkObjectToString(self);
+ final int idx = JSType.toInteger(pos);
+ return idx < 0 || idx >= str.length() ? Double.NaN : str.charAt(idx);
}
/**
@@ -585,24 +595,32 @@ public final class NativeString extends ScriptObject {
* @param pos position in string
* @return number representing charcode at position
*/
- @SpecializedFunction
- public static double charCodeAt(final Object self, final double pos) {
- return charCodeAt(self, (int) pos);
+ @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class)
+ public static int charCodeAt(final Object self, final double pos) {
+ return charCodeAt(self, (int)pos); //toInt pos is ok
}
/**
- * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for int position
+ * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for long position
* @param self self reference
* @param pos position in string
* @return number representing charcode at position
*/
- @SpecializedFunction
- public static double charCodeAt(final Object self, final int pos) {
- return charCodeAtImpl(checkObjectToString(self), pos);
+ @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class)
+ public static int charCodeAt(final Object self, final long pos) {
+ return charCodeAt(self, (int)pos);
}
- private static double charCodeAtImpl(final String str, final int pos) {
- return pos < 0 || pos >= str.length() ? Double.NaN : str.charAt(pos);
+ /**
+ * ECMA 15.5.4.5 String.prototype.charCodeAt (pos) - specialized version for int position
+ * @param self self reference
+ * @param pos position in string
+ * @return number representing charcode at position
+ */
+
+ @SpecializedFunction(linkLogic=CharCodeAtLinkLogic.class)
+ public static int charCodeAt(final Object self, final int pos) {
+ return getValidChar(self, pos);
}
/**
@@ -1097,7 +1115,6 @@ public final class NativeString extends ScriptObject {
*/
@Function(attributes = Attribute.NOT_ENUMERABLE)
public static String trim(final Object self) {
-
final String str = checkObjectToString(self);
int start = 0;
int end = str.length() - 1;
@@ -1181,7 +1198,7 @@ public final class NativeString extends ScriptObject {
*
* @return new NativeString ("")
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object constructor(final boolean newObj, final Object self) {
return newObj ? newObj("") : "";
}
@@ -1197,7 +1214,7 @@ public final class NativeString extends ScriptObject {
*
* @return new NativeString (arg)
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object constructor(final boolean newObj, final Object self, final Object arg) {
final CharSequence str = JSType.toCharSequence(arg);
return newObj ? newObj(str) : str.toString();
@@ -1214,8 +1231,42 @@ public final class NativeString extends ScriptObject {
*
* @return new NativeString containing the string representation of the arg
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object constructor(final boolean newObj, final Object self, final int arg) {
+ final String str = Integer.toString(arg);
+ return newObj ? newObj(str) : str;
+ }
+
+ /**
+ * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg
+ *
+ * Constructor
+ *
+ * @param newObj is this constructor invoked with the new operator
+ * @param self self reference
+ * @param arg the arg
+ *
+ * @return new NativeString containing the string representation of the arg
+ */
+ @SpecializedFunction(isConstructor=true)
+ public static Object constructor(final boolean newObj, final Object self, final long arg) {
+ final String str = Long.toString(arg);
+ return newObj ? newObj(str) : str;
+ }
+
+ /**
+ * ECMA 15.5.2.1 new String ( [ value ] ) - special version with exactly one {@code int} arg
+ *
+ * Constructor
+ *
+ * @param newObj is this constructor invoked with the new operator
+ * @param self self reference
+ * @param arg the arg
+ *
+ * @return new NativeString containing the string representation of the arg
+ */
+ @SpecializedFunction(isConstructor=true)
+ public static Object constructor(final boolean newObj, final Object self, final double arg) {
final String str = JSType.toString(arg);
return newObj ? newObj(str) : str;
}
@@ -1231,9 +1282,9 @@ public final class NativeString extends ScriptObject {
*
* @return new NativeString containing the string representation of the arg
*/
- @SpecializedConstructor
+ @SpecializedFunction(isConstructor=true)
public static Object constructor(final boolean newObj, final Object self, final boolean arg) {
- final String str = JSType.toString(arg);
+ final String str = Boolean.toString(arg);
return newObj ? newObj(str) : str;
}
@@ -1281,7 +1332,7 @@ public final class NativeString extends ScriptObject {
} else if (self != null && self == Global.instance().getStringPrototype()) {
return "";
} else {
- throw typeError( "not.a.string", ScriptRuntime.safeToString(self));
+ throw typeError("not.a.string", ScriptRuntime.safeToString(self));
}
}
@@ -1310,4 +1361,50 @@ public final class NativeString extends ScriptObject {
return MH.findStatic(MethodHandles.lookup(), NativeString.class, name, type);
}
+ @Override
+ public LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz) {
+ if (clazz == CharCodeAtLinkLogic.class) {
+ return CharCodeAtLinkLogic.INSTANCE;
+ }
+ return null;
+ }
+
+ @Override
+ public boolean hasPerInstanceAssumptions() {
+ return false;
+ }
+
+ /**
+ * This is linker logic charCodeAt - when we specialize further methods in NativeString
+ * It may be expanded. It's link check makes sure that we are dealing with a char
+ * sequence and that we are in range
+ */
+ private static final class CharCodeAtLinkLogic extends SpecializedFunction.LinkLogic {
+
+ private static final CharCodeAtLinkLogic INSTANCE = new CharCodeAtLinkLogic();
+
+ @Override
+ public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
+ try {
+ //check that it's a char sequence or throw cce
+ final CharSequence cs = (CharSequence)self;
+ //check that the index, representable as an int, is inside the array
+ final int intIndex = JSType.toInteger(request.getArguments()[1]);
+ return intIndex >= 0 && intIndex < cs.length(); //can link
+ } catch (final ClassCastException | IndexOutOfBoundsException e) {
+ //fallthru
+ }
+ return false;
+ }
+
+ /**
+ * charCodeAt callsites can throw ClassCastException as a mechanism to have them
+ * relinked - this enabled fast checks of the kind of ((IntArrayData)arrayData).push(x)
+ * for an IntArrayData only push - if this fails, a CCE will be thrown and we will relink
+ */
+ @Override
+ public Class<? extends Throwable> getRelinkException() {
+ return ClassCastException.class;
+ }
+ }
}
diff --git a/src/jdk/nashorn/internal/objects/NativeUint16Array.java b/src/jdk/nashorn/internal/objects/NativeUint16Array.java
index 68bd410c..d049c933 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint16Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint16Array.java
@@ -26,7 +26,6 @@
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;
@@ -119,6 +118,11 @@ public final class NativeUint16Array extends ArrayBufferView {
}
@Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
+ @Override
public int getInt(final int index) {
return getElem(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeUint32Array.java b/src/jdk/nashorn/internal/objects/NativeUint32Array.java
index 47ee08da..2ae2be05 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint32Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint32Array.java
@@ -26,7 +26,6 @@
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;
@@ -128,6 +127,11 @@ public final class NativeUint32Array extends ArrayBufferView {
}
@Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
+ @Override
public int getInt(final int index) {
return (int)getLong(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeUint8Array.java b/src/jdk/nashorn/internal/objects/NativeUint8Array.java
index 15e01245..1067baa5 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint8Array.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint8Array.java
@@ -26,7 +26,6 @@
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;
@@ -119,6 +118,11 @@ public final class NativeUint8Array extends ArrayBufferView {
}
@Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
+ @Override
public int getInt(final int index) {
return getElem(index);
}
diff --git a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
index db19ab37..ffd0f14c 100644
--- a/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
+++ b/src/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java
@@ -28,7 +28,6 @@ package jdk.nashorn.internal.objects;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.lookup.Lookup.MH;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -98,6 +97,11 @@ public final class NativeUint8ClampedArray extends ArrayBufferView {
return SET_ELEM;
}
+ @Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
private int getElem(final int index) {
try {
return nb.get(index) & 0xff;
diff --git a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
index a52051ca..36e7716b 100644
--- a/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
+++ b/src/jdk/nashorn/internal/objects/ScriptFunctionImpl.java
@@ -30,6 +30,7 @@ import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import java.lang.invoke.MethodHandle;
import java.util.ArrayList;
+
import jdk.nashorn.internal.runtime.AccessorProperty;
import jdk.nashorn.internal.runtime.GlobalFunctions;
import jdk.nashorn.internal.runtime.Property;
@@ -38,6 +39,7 @@ import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.ScriptFunctionData;
import jdk.nashorn.internal.runtime.ScriptObject;
+import jdk.nashorn.internal.runtime.Specialization;
/**
* Concrete implementation of ScriptFunction. This sets correct map for the
@@ -58,7 +60,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
// Marker object for lazily initialized prototype object
private static final Object LAZY_PROTOTYPE = new Object();
- private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs, final Global global) {
+ private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final Specialization[] specs, final Global global) {
super(name, invokeHandle, map$, null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR);
init(global);
}
@@ -71,11 +73,11 @@ public class ScriptFunctionImpl extends ScriptFunction {
* @param invokeHandle handle for invocation
* @param specs specialized versions of this method, if available, null otherwise
*/
- ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final MethodHandle[] specs) {
+ ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final Specialization[] specs) {
this(name, invokeHandle, specs, Global.instance());
}
- private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs, final Global global) {
+ private ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs, final Global global) {
super(name, invokeHandle, map.addAll(map$), null, specs, ScriptFunctionData.IS_BUILTIN_CONSTRUCTOR);
init(global);
}
@@ -89,11 +91,11 @@ public class ScriptFunctionImpl extends ScriptFunction {
* @param map initial property map
* @param specs specialized versions of this method, if available, null otherwise
*/
- ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final MethodHandle[] specs) {
+ ScriptFunctionImpl(final String name, final MethodHandle invokeHandle, final PropertyMap map, final Specialization[] specs) {
this(name, invokeHandle, map, specs, Global.instance());
}
- private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final int flags, final Global global) {
+ private ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final Specialization[] specs, final int flags, final Global global) {
super(name, methodHandle, getMap(isStrict(flags)), scope, specs, flags);
init(global);
}
@@ -107,7 +109,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
* @param specs specialized versions of this method, if available, null otherwise
* @param flags {@link ScriptFunctionData} flags
*/
- ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final MethodHandle[] specs, final int flags) {
+ ScriptFunctionImpl(final String name, final MethodHandle methodHandle, final ScriptObject scope, final Specialization[] specs, final int flags) {
this(name, methodHandle, scope, specs, flags, Global.instance());
}
@@ -184,7 +186,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
return new AnonymousFunction();
}
- private static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs, final int flags) {
+ private static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final Specialization[] specs, final int flags) {
final ScriptFunctionImpl func = new ScriptFunctionImpl(name, methodHandle, null, specs, flags);
func.setPrototype(UNDEFINED);
// Non-constructor built-in functions do not have "prototype" property
@@ -201,7 +203,7 @@ public class ScriptFunctionImpl extends ScriptFunction {
* @param specs specialized versions of function if available, null otherwise
* @return new ScriptFunction
*/
- static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final MethodHandle[] specs) {
+ static ScriptFunction makeFunction(final String name, final MethodHandle methodHandle, final Specialization[] specs) {
return makeFunction(name, methodHandle, specs, ScriptFunctionData.IS_BUILTIN);
}
diff --git a/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java b/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java
index 1e75d2a7..36f47b27 100644
--- a/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java
+++ b/src/jdk/nashorn/internal/objects/annotations/SpecializedFunction.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,18 +29,315 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.SwitchPoint;
+import jdk.internal.dynalink.CallSiteDescriptor;
+import jdk.internal.dynalink.linker.LinkRequest;
+import jdk.nashorn.internal.runtime.ScriptFunction;
/**
- * The SpecializedFunction annotation is used to flag more type specific functions than the standard one in
- * Native objects. For example {@link jdk.nashorn.internal.objects.NativeMath#max} takes an arbitrary number of
- * Object elements as an array. Call this function, including the varargs collector that allocates the array
- * upon each invocation, is much more expensive than calling a specialized function that takes two arguments
- * of, e.g. int type from the call site, such as {@link jdk.nashorn.internal.objects.NativeMath#max(Object, int, int)}.
- * {@link jdk.nashorn.internal.runtime.ScriptFunction} will try to look up the most specific function when
- * linking the callsite.
+ * The SpecializedFunction annotation is used to flag more type specific
+ * functions than the standard one in the native objects
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SpecializedFunction {
- //empty
+
+ /**
+ * Functionality for testing if we are allowed to link a specialized
+ * function the first time we encounter it. Then the guard will handle the
+ * rest of the invocations
+ *
+ * This is the same for all callsites in Nashorn, the first time callsite is
+ * linked, we have to manually check that the linkage is OK. Even if we add
+ * a guard and it fails upon the first try, this is not good enough.
+ * (Symmetrical to how it works everywhere else in the Nashorn runtime).
+ *
+ * Here we abstract out a few of the most common link guard checks.
+ */
+ public static abstract class LinkLogic {
+ /**
+ * Empty link logic instance - this is the default
+ * "no special linking or runtime guard behavior"
+ */
+ public static final LinkLogic EMPTY_INSTANCE = new Empty();
+
+ private static final SwitchPoint[] INVALIDATED_SWITCHPOINTS = new SwitchPoint[0];
+
+ private SwitchPoint[] modificationSwitchPoints; //cache
+
+ /** Empty link logic class - allow all linking, no guards */
+ private static final class Empty extends LinkLogic {
+ @Override
+ public boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
+ return true;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+ }
+
+ /**
+ * Get the class representing the empty link logic
+ * @return class representing empty link logic
+ */
+ public static Class<? extends LinkLogic> getEmptyLinkLogicClass() {
+ return Empty.class;
+ }
+
+ /**
+ * Should this callsite relink when an exception is thrown
+ *
+ * @return the relink exception, or null if none
+ */
+ public Class<? extends Throwable> getRelinkException() {
+ return null;
+ }
+
+ /**
+ * Is this link logic class empty - i.e. no special linking logic
+ * supplied
+ *
+ * @param clazz class to check
+ *
+ * @return true if this link logic is empty
+ */
+ public static boolean isEmpty(final Class<? extends LinkLogic> clazz) {
+ return clazz == Empty.class;
+ }
+
+ /**
+ * Is this link logic instance empty - i.e. no special linking logic
+ * supplied
+ *
+ * @return true if this link logic instance is empty
+ */
+ public boolean isEmpty() {
+ return false;
+ }
+
+ /**
+ * Given a callsite, can we link this method based on the receiver and
+ * parameters?
+ *
+ * @param self receiver
+ * @param desc callsite descriptor
+ * @param request link request
+ *
+ * @return true if we can link this callsite at this time
+ */
+ public abstract boolean canLink(final Object self, final CallSiteDescriptor desc, final LinkRequest request);
+
+ /**
+ * Given a callsite, do we require an extra guard for specialization to
+ * go through?
+ *
+ * @param self receiver
+ *
+ * @return true if a guard is to be woven into the callsite
+ */
+ public boolean needsGuard(final Object self) {
+ return true;
+ }
+
+ /**
+ * Given a callsite, and optional arguments, do we need an extra guard
+ * for specialization to go through - this guard can be a function of
+ * the arguments too
+ *
+ * @param self receiver
+ * @param args arguments
+ *
+ * @return true if a guard is to be woven into the callsite
+ */
+ public boolean needsGuard(final Object self, final Object... args) {
+ return true;
+ }
+
+ /**
+ * Given a callsite, and optional arguments, return any extra guard we
+ * might need for specialization as a method handle.
+ *
+ * @return methodhandle for guard, or null if no guard is needed
+ */
+ public MethodHandle getGuard() {
+ return null;
+ }
+
+ /**
+ * Return the modification SwitchPoint of a particular index from this OptimisticBuiltins
+ * If none exists, one is created and that one is return.
+ *
+ * The implementor must map indexes to specific SwitchPoints for specific events and keep
+ * track of what they mean, for example NativeArray.LENGTH_NOT_WRITABLE_SWITCHPOINT
+ * might be a useful index mapping
+ *
+ * @param index index for SwitchPoint to get or create
+ * @return modification SwitchPoint of particular index for the receiver
+ */
+ public SwitchPoint getOrCreateModificationSwitchPoint(final int index) {
+ return null;
+ }
+
+ /**
+ * Return the modification SwitchPoint from this OptimisticBuiltins. If none
+ * exists, one is created and that one is return.
+ *
+ * @return modification SwitchPoint for the receiver
+ */
+ public SwitchPoint[] getOrCreateModificationSwitchPoints() {
+ return null;
+ }
+
+ /**
+ * Hook to invalidate a modification SwitchPoint by index.
+ *
+ * @param index index for SwitchPoint to invalidate
+ */
+ public void invalidateModificationSwitchPoint(final int index) {
+ //empty
+ }
+
+ /**
+ * Hook to invalidate all modification SwitchPoints for a receiver
+ */
+ public void invalidateModificationSwitchPoints() {
+ //empty
+ }
+
+ /**
+ * Check whether the receiver has an invalidated modification SwitchPoint.
+ *
+ * @param index index for the modification SwitchPoint
+ * @return true if the particular SwitchPoint at the index is invalidated
+ */
+ public boolean hasInvalidatedModificationSwitchPoint(final int index) {
+ return false;
+ }
+
+ /**
+ * Check whether at least one of the modification SwitchPoints has been
+ * invalidated
+ * @return true if one of the SwitchPoints has been invalidated
+ */
+ public boolean hasInvalidatedModificationSwitchPoints() {
+ return false;
+ }
+
+ /**
+ * Check whether this OptimisticBuiltins has a SwitchPoints of particular
+ * index.
+ *
+ * As creation overhead for a SwitchPoint is non-zero, we have to create them lazily instead of,
+ * e.g. in the constructor of every subclass.
+ *
+ * @param index index for the modification SwitchPoint
+ * @return true if a modification SwitchPoint exists, no matter if it has been invalidated or not
+ */
+ public boolean hasModificationSwitchPoint(final int index) {
+ return false;
+ }
+
+ /**
+ * Check whether this OptimisticBuiltins has SwitchPoints.
+ *
+ * As creation overhead for a SwitchPoint is non-zero, we have to create them lazily instead of,
+ * e.g. in the constructor of every subclass.
+ *
+ * @return true if a modification SwitchPoint exists, no matter if it has been invalidated or not
+ */
+ public boolean hasModificationSwitchPoints() {
+ return false;
+ }
+
+ /**
+ * Check, given a link request and a receiver, if this specialization
+ * fits This is used by the linker in {@link ScriptFunction} to figure
+ * out if an optimistic builtin can be linked when first discovered
+ *
+ * @param self receiver
+ * @param desc callsite descriptor
+ * @param request link request
+
+ * @return true if we can link, false otherwise - that means we have to
+ * pick a non specialized target
+ */
+ public boolean checkLinkable(final Object self, final CallSiteDescriptor desc, final LinkRequest request) {
+ // no matter what the modification switchpoints are, if any of them are invalidated,
+ // we can't link. Side effect is that if it's the first time we see this callsite,
+ // we have to create the SwitchPoint(s) so future modification switchpoint invalidations
+ // relink it
+ final SwitchPoint[] sps = getOrCreateModificationSwitchPoints(self);
+ if (sps == INVALIDATED_SWITCHPOINTS) {
+ // nope, can't do the fast link as this assumption
+ // has been invalidated already, e.g. length of an
+ // array set to not writable
+ return false;
+ }
+ modificationSwitchPoints = sps; //cache
+
+ // check the link guard, if it says we can link, go ahead
+ return canLink(self, desc, request);
+ }
+
+ private SwitchPoint[] getOrCreateModificationSwitchPoints(final Object self) {
+ final SwitchPoint[] sps = getOrCreateModificationSwitchPoints(); //ask for all my switchpoints
+ if (sps != null) { //switchpoint exists, but some may be invalidated
+ for (final SwitchPoint sp : sps) {
+ if (sp.hasBeenInvalidated()) {
+ return INVALIDATED_SWITCHPOINTS;
+ }
+ }
+ }
+ return sps;
+ }
+
+ /**
+ * Get the cached modification switchpoints. Only possible to do after a link
+ * check call has been performed, one that has answered "true", or you will get the
+ * wrong information.
+ *
+ * Should be used only from {@link ScriptFunction#findCallMethod}
+ *
+ * @return cached modification switchpoints for this callsite, null if none
+ */
+ public SwitchPoint[] getModificationSwitchPoints() {
+ return modificationSwitchPoints == null ? null : modificationSwitchPoints.clone();
+ }
+ }
+
+ /**
+ * name override for return value polymorphism, for example we can't have
+ * pop(V)I and pop(V)D in the same Java class, so they need to be named,
+ * e.g. popInt(V)I and popDouble(V)D for disambiguation, however, their
+ * names still need to resolve to "pop" to JavaScript so we can still
+ * specialize on return values and so that the linker can find them
+ *
+ * @return name, "" means no override, use the Java function name, e.g.
+ * "push"
+ */
+ String name() default "";
+
+ /**
+ * Return the guard for this specialized function. The default is no guard.
+ *
+ * @return guard
+ */
+ Class<?> linkLogic() default LinkLogic.Empty.class;
+
+ /**
+ * Is this a specialized constructor?
+ */
+ boolean isConstructor() default false;
+
+ /**
+ * Can this function throw UnwarrantedOptimismExceptions? This works just
+ * like the normal functions, but we need the function to be
+ * immutable/non-state modifying, as we can't generate continuations for
+ * native code. Luckily a lot of the methods we want to specialize have this
+ * property
+ */
+ boolean isOptimistic() default false;
}
diff --git a/src/jdk/nashorn/internal/parser/AbstractParser.java b/src/jdk/nashorn/internal/parser/AbstractParser.java
index 589fb9c1..3eb45cfb 100644
--- a/src/jdk/nashorn/internal/parser/AbstractParser.java
+++ b/src/jdk/nashorn/internal/parser/AbstractParser.java
@@ -30,7 +30,6 @@ import static jdk.nashorn.internal.parser.TokenType.DIRECTIVE_COMMENT;
import static jdk.nashorn.internal.parser.TokenType.EOF;
import static jdk.nashorn.internal.parser.TokenType.EOL;
import static jdk.nashorn.internal.parser.TokenType.IDENT;
-
import java.util.HashMap;
import java.util.Map;
import jdk.nashorn.internal.ir.IdentNode;
diff --git a/src/jdk/nashorn/internal/runtime/AccessorProperty.java b/src/jdk/nashorn/internal/runtime/AccessorProperty.java
index 44b3c3b0..e2935368 100644
--- a/src/jdk/nashorn/internal/runtime/AccessorProperty.java
+++ b/src/jdk/nashorn/internal/runtime/AccessorProperty.java
@@ -36,7 +36,6 @@ import static jdk.nashorn.internal.lookup.MethodHandleFactory.stripName;
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
import static jdk.nashorn.internal.runtime.JSType.getNumberOfAccessorTypes;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
-
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.invoke.MethodHandle;
@@ -57,9 +56,7 @@ public class AccessorProperty extends Property {
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static final MethodHandle REPLACE_MAP = findOwnMH_S("replaceMap", Object.class, Object.class, PropertyMap.class);
- private static final MethodHandle INVALIDATE_SP = findOwnMH_S("invalidateSwitchPoint", Object.class, Object.class, SwitchPoint.class);
-
- private static final SwitchPoint NO_CHANGE_CALLBACK = new SwitchPoint();
+ private static final MethodHandle INVALIDATE_SP = findOwnMH_S("invalidateSwitchPoint", Object.class, AccessorProperty.class, Object.class);
private static final int NOOF_TYPES = getNumberOfAccessorTypes();
private static final long serialVersionUID = 3371720170182154920L;
@@ -221,7 +218,7 @@ public class AccessorProperty extends Property {
* @param setter the property setter or null if non writable, non configurable
*/
private AccessorProperty(final String key, final int flags, final int slot, final MethodHandle getter, final MethodHandle setter) {
- super(key, flags | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
+ super(key, flags | IS_BUILTIN | (getter.type().returnType().isPrimitive() ? IS_NASGEN_PRIMITIVE : 0), slot);
assert !isSpill();
// we don't need to prep the setters these will never be invalidated as this is a nasgen
@@ -602,7 +599,6 @@ public class AccessorProperty extends Property {
private Property getWiderProperty(final Class<?> type) {
return copy(type); //invalidate cache of new property
-
}
private PropertyMap getWiderMap(final PropertyMap oldMap, final Property newProperty) {
@@ -627,8 +623,10 @@ public class AccessorProperty extends Property {
}
@SuppressWarnings("unused")
- private static Object invalidateSwitchPoint(final Object obj, final SwitchPoint sp) {
- SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
+ private static Object invalidateSwitchPoint(final AccessorProperty property, final Object obj) {
+ if (!property.builtinSwitchPoint.hasBeenInvalidated()) {
+ SwitchPoint.invalidateAll(new SwitchPoint[] { property.builtinSwitchPoint });
+ }
return obj;
}
@@ -668,12 +666,8 @@ public class AccessorProperty extends Property {
mh = generateSetter(!forType.isPrimitive() ? Object.class : forType, type);
}
- /**
- * Check if this is a special global name that requires switchpoint invalidation
- */
- final SwitchPoint ccb = getChangeCallback();
- if (ccb != null && ccb != NO_CHANGE_CALLBACK) {
- mh = MH.filterArguments(mh, 0, MH.insertArguments(debugInvalidate(getKey(), ccb), 1, changeCallback));
+ if (isBuiltin()) {
+ mh = MH.filterArguments(mh, 0, debugInvalidate(MH.insertArguments(INVALIDATE_SP, 0, this), getKey()));
}
assert mh.type().returnType() == void.class : mh.type();
@@ -681,25 +675,6 @@ public class AccessorProperty extends Property {
return mh;
}
- /**
- * Get the change callback for this property
- * @return switchpoint that is invalidated when property changes
- */
- protected SwitchPoint getChangeCallback() {
- if (changeCallback == null) {
- try {
- changeCallback = Global.instance().getChangeCallback(getKey());
- } catch (final NullPointerException e) {
- assert !"apply".equals(getKey()) && !"call".equals(getKey());
- //empty
- }
- if (changeCallback == null) {
- changeCallback = NO_CHANGE_CALLBACK;
- }
- }
- return changeCallback;
- }
-
@Override
public final boolean canChangeType() {
if (OBJECT_FIELDS_ONLY) {
@@ -724,7 +699,6 @@ public class AccessorProperty extends Property {
return currentType;
}
-
private MethodHandle debug(final MethodHandle mh, final Class<?> forType, final Class<?> type, final String tag) {
if (!Context.DEBUG || !Global.hasInstance()) {
return mh;
@@ -780,9 +754,9 @@ public class AccessorProperty extends Property {
return mh;
}
- private static MethodHandle debugInvalidate(final String key, final SwitchPoint sp) {
+ private static MethodHandle debugInvalidate(final MethodHandle invalidator, final String key) {
if (!Context.DEBUG || !Global.hasInstance()) {
- return INVALIDATE_SP;
+ return invalidator;
}
final Context context = Context.getContextTrusted();
@@ -790,11 +764,11 @@ public class AccessorProperty extends Property {
return context.addLoggingToHandle(
ObjectClassGenerator.class,
- INVALIDATE_SP,
+ invalidator,
new Supplier<String>() {
@Override
public String get() {
- return "Field change callback for " + key + " triggered: " + sp;
+ return "Field change callback for " + key + " triggered ";
}
});
}
diff --git a/src/jdk/nashorn/internal/runtime/CodeInstaller.java b/src/jdk/nashorn/internal/runtime/CodeInstaller.java
index 7a7df467..e2b5afe8 100644
--- a/src/jdk/nashorn/internal/runtime/CodeInstaller.java
+++ b/src/jdk/nashorn/internal/runtime/CodeInstaller.java
@@ -81,13 +81,17 @@ public interface CodeInstaller<T> {
/**
* Store a compiled script for later reuse
+ *
+ * @param cacheKey key to use in cache
* @param source the script source
* @param mainClassName the main class name
* @param classBytes map of class names to class bytes
+ * @param initializers compilation id -> FunctionInitializer map
* @param constants constants array
+ * @param compilationId compilation id
*/
- public void storeScript(String cacheKey, Source source, String mainClassName, Map<String, byte[]> classBytes,
- Map<Integer, FunctionInitializer> initializers, Object[] constants, int compilationId);
+ public void storeScript(final String cacheKey, final Source source, final String mainClassName, final Map<String, byte[]> classBytes,
+ final Map<Integer, FunctionInitializer> initializers, final Object[] constants, final int compilationId);
/**
* Load a previously compiled script
@@ -96,4 +100,21 @@ public interface CodeInstaller<T> {
* @return compiled script data
*/
public StoredScript loadScript(Source source, String functionKey);
+
+ /**
+ * Returns a new code installer that shares most of the functionality of this code installer, but uses a
+ * new, independent class loader.
+ * @return a new code installer with a new independent class loader.
+ */
+ public CodeInstaller<T> withNewLoader();
+
+ /**
+ * Returns true if this code installer is compatible with the other code installer. Compatibility is expected to be
+ * an equivalence relation, and installers are supposed to be compatible with those they create using
+ * {@link #withNewLoader()}.
+ * @param other the other code installer tested for compatibility with this code installer.
+ * @return true if this code installer is compatible with the other code installer.
+ */
+ public boolean isCompatibleWith(CodeInstaller<T> other);
+
}
diff --git a/src/jdk/nashorn/internal/runtime/CodeStore.java b/src/jdk/nashorn/internal/runtime/CodeStore.java
index a736cc36..0748ccc3 100644
--- a/src/jdk/nashorn/internal/runtime/CodeStore.java
+++ b/src/jdk/nashorn/internal/runtime/CodeStore.java
@@ -118,6 +118,8 @@ public abstract class CodeStore implements Loggable {
* @param initializers the function initializers
* @param constants the constants array
* @param compilationId the compilation id
+ *
+ * @return stored script
*/
public StoredScript store(final String functionKey,
final Source source,
@@ -153,11 +155,13 @@ public abstract class CodeStore implements Loggable {
/**
* Returns a new StoredScript instance.
*
+ * @param source the source
* @param mainClassName the main class name
* @param classBytes a map of class bytes
* @param initializers function initializers
* @param constants the constants array
* @param compilationId the compilation id
+ *
* @return The compiled script
*/
public StoredScript storedScriptFor(final Source source, final String mainClassName,
@@ -206,7 +210,7 @@ public abstract class CodeStore implements Loggable {
/**
* Constructor
*
- * @throws IOException
+ * @throws IOException if there are read/write problems with the cache and cache directory
*/
public DirectoryCodeStore() throws IOException {
this(Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"), false, DEFAULT_MIN_SIZE);
@@ -216,8 +220,9 @@ public abstract class CodeStore implements Loggable {
* Constructor
*
* @param path directory to store code in
+ * @param readOnly is this a read only code store
* @param minSize minimum file size for caching scripts
- * @throws IOException
+ * @throws IOException if there are read/write problems with the cache and cache directory
*/
public DirectoryCodeStore(final String path, final boolean readOnly, final int minSize) throws IOException {
this.dir = checkDirectory(path, readOnly);
diff --git a/src/jdk/nashorn/internal/runtime/CompiledFunction.java b/src/jdk/nashorn/internal/runtime/CompiledFunction.java
index 28a81dea..2655e781 100644
--- a/src/jdk/nashorn/internal/runtime/CompiledFunction.java
+++ b/src/jdk/nashorn/internal/runtime/CompiledFunction.java
@@ -33,12 +33,13 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.lang.invoke.SwitchPoint;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import java.util.function.Supplier;
import java.util.logging.Level;
-
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
@@ -46,6 +47,7 @@ import jdk.nashorn.internal.codegen.TypeMap;
import jdk.nashorn.internal.codegen.types.ArrayType;
import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.ir.FunctionNode;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
import jdk.nashorn.internal.runtime.events.RecompilationEvent;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.logging.DebugLogger;
@@ -63,6 +65,8 @@ final class CompiledFunction {
private final DebugLogger log;
+ static final Collection<CompiledFunction> NO_FUNCTIONS = Collections.emptySet();
+
/**
* The method type may be more specific than the invoker, if. e.g.
* the invoker is guarded, and a guard with a generic object only
@@ -75,19 +79,38 @@ final class CompiledFunction {
private final int flags; // from FunctionNode
private final MethodType callSiteType;
+ private final Specialization specialization;
+
CompiledFunction(final MethodHandle invoker) {
- this(invoker, null);
+ this(invoker, null, null);
}
- static CompiledFunction createBuiltInConstructor(final MethodHandle invoker) {
- return new CompiledFunction(MH.insertArguments(invoker, 0, false), createConstructorFromInvoker(MH.insertArguments(invoker, 0, true)));
+ static CompiledFunction createBuiltInConstructor(final MethodHandle invoker, final Specialization specialization) {
+ return new CompiledFunction(MH.insertArguments(invoker, 0, false), createConstructorFromInvoker(MH.insertArguments(invoker, 0, true)), specialization);
}
- CompiledFunction(final MethodHandle invoker, final MethodHandle constructor) {
- this(invoker, constructor, 0, null, DebugLogger.DISABLED_LOGGER);
+ CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final Specialization specialization) {
+ this(invoker, constructor, 0, null, specialization, DebugLogger.DISABLED_LOGGER);
}
- CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final int flags, final MethodType callSiteType, final DebugLogger log) {
+ CompiledFunction(final MethodHandle invoker, final MethodHandle constructor, final int flags, final MethodType callSiteType, final Specialization specialization, final DebugLogger log) {
+ this.specialization = specialization;
+ if (specialization != null && specialization.isOptimistic()) {
+ /*
+ * An optimistic builtin with isOptimistic=true works like any optimistic generated function, i.e. it
+ * can throw unwarranted optimism exceptions. As native functions trivially can't have parts of them
+ * regenerated as restof methods, this only works if the methods are atomic/functional in their behavior
+ * and doesn't modify state before an UOE can be thrown. If they aren't, we can reexecute a wider version
+ * of the same builtin in a recompilation handler for FinalScriptFunctionData. There are several
+ * candidate methods in Native* that would benefit from this, but I haven't had time to implement any
+ * of them currently. In order to fit in with the relinking framework, the current thinking is
+ * that the methods still take a program point to fit in with other optimistic functions, but
+ * it is set to "first", which is the beginning of the method. The relinker can tell the difference
+ * between builtin and JavaScript functions. This might change. TODO
+ */
+ this.invoker = MH.insertArguments(invoker, invoker.type().parameterCount() - 1, UnwarrantedOptimismException.FIRST_PROGRAM_POINT);
+ throw new AssertionError("Optimistic (UnwarrantedOptimismException throwing) builtin functions are currently not in use");
+ }
this.invoker = invoker;
this.constructor = constructor;
this.flags = flags;
@@ -97,7 +120,7 @@ final class CompiledFunction {
CompiledFunction(final MethodHandle invoker, final RecompilableScriptFunctionData functionData,
final Map<Integer, Type> invalidatedProgramPoints, final MethodType callSiteType, final int flags) {
- this(invoker, null, flags, callSiteType, functionData.getLogger());
+ this(invoker, null, flags, callSiteType, null, functionData.getLogger());
if ((flags & FunctionNode.IS_DEOPTIMIZABLE) != 0) {
optimismInfo = new OptimismInfo(functionData, invalidatedProgramPoints);
} else {
@@ -105,10 +128,45 @@ final class CompiledFunction {
}
}
+ static CompiledFunction createBuiltInConstructor(final MethodHandle invoker) {
+ return new CompiledFunction(MH.insertArguments(invoker, 0, false), createConstructorFromInvoker(MH.insertArguments(invoker, 0, true)), null);
+ }
+
+ boolean isSpecialization() {
+ return specialization != null;
+ }
+
+ boolean hasLinkLogic() {
+ return getLinkLogicClass() != null;
+ }
+
+ Class<? extends LinkLogic> getLinkLogicClass() {
+ if (isSpecialization()) {
+ final Class<? extends LinkLogic> linkLogicClass = specialization.getLinkLogicClass();
+ assert !LinkLogic.isEmpty(linkLogicClass) : "empty link logic classes should have been removed by nasgen";
+ return linkLogicClass;
+ }
+ return null;
+ }
+
int getFlags() {
return flags;
}
+ /**
+ * An optimistic specialization is one that can throw UnwarrantedOptimismException.
+ * This is allowed for native methods, as long as they are functional, i.e. don't change
+ * any state between entering and throwing the UOE. Then we can re-execute a wider version
+ * of the method in the continuation. Rest-of method generation for optimistic builtins is
+ * of course not possible, but this approach works and fits into the same relinking
+ * framework
+ *
+ * @return true if optimistic builtin
+ */
+ boolean isOptimistic() {
+ return isSpecialization() ? specialization.isOptimistic() : false;
+ }
+
boolean isApplyToCall() {
return (flags & FunctionNode.HAS_APPLY_TO_CALL_SPECIALIZATION) != 0;
}
@@ -119,7 +177,19 @@ final class CompiledFunction {
@Override
public String toString() {
- return "[invokerType=" + invoker.type() + " ctor=" + constructor + " weight=" + weight() + " isApplyToCall=" + isApplyToCall() + "]";
+ final StringBuilder sb = new StringBuilder();
+ final Class<? extends LinkLogic> linkLogicClass = getLinkLogicClass();
+
+ sb.append("[invokerType=").
+ append(invoker.type()).
+ append(" ctor=").
+ append(constructor).
+ append(" weight=").
+ append(weight()).
+ append(" linkLogic=").
+ append(linkLogicClass != null ? linkLogicClass.getSimpleName() : "none");
+
+ return sb.toString();
}
boolean needsCallee() {
@@ -281,10 +351,12 @@ final class CompiledFunction {
if (other == null) {
return true;
}
- return betterThanFinal(type(), other.type(), callSiteMethodType);
+ return betterThanFinal(this, other, callSiteMethodType);
}
- static boolean betterThanFinal(final MethodType thisMethodType, final MethodType otherMethodType, final MethodType callSiteMethodType) {
+ private static boolean betterThanFinal(final CompiledFunction cf, final CompiledFunction other, final MethodType callSiteMethodType) {
+ final MethodType thisMethodType = cf.type();
+ final MethodType otherMethodType = other.type();
final int thisParamCount = getParamCount(thisMethodType);
final int otherParamCount = getParamCount(otherMethodType);
final int callSiteRawParamCount = getParamCount(callSiteMethodType);
@@ -406,7 +478,17 @@ final class CompiledFunction {
return false;
}
- throw new AssertionError(thisMethodType + " identically applicable to " + otherMethodType + " for " + callSiteMethodType); // Signatures are identical
+ //if they are equal, pick the specialized one first
+ if (cf.isSpecialization() != other.isSpecialization()) {
+ return cf.isSpecialization(); //always pick the specialized version if we can
+ }
+
+ if (cf.isSpecialization() && other.isSpecialization()) {
+ return cf.getLinkLogicClass() != null; //pick link logic specialization above generic specializations
+ }
+
+ // Signatures are identical
+ throw new AssertionError(thisMethodType + " identically applicable to " + otherMethodType + " for " + callSiteMethodType);
}
private static Type[] toTypeWithoutCallee(final MethodType type, final int thisIndex) {
@@ -427,8 +509,8 @@ final class CompiledFunction {
return ((ArrayType)paramTypes[paramTypes.length - 1]).getElementType();
}
- boolean matchesCallSite(final MethodType callSiteType, final boolean pickVarArg) {
- if (callSiteType.equals(this.callSiteType)) {
+ boolean matchesCallSite(final MethodType other, final boolean pickVarArg) {
+ if (other.equals(this.callSiteType)) {
return true;
}
final MethodType type = type();
@@ -438,7 +520,7 @@ final class CompiledFunction {
return pickVarArg;
}
- final int csParamCount = getParamCount(callSiteType);
+ final int csParamCount = getParamCount(other);
final boolean csIsVarArg = csParamCount == Integer.MAX_VALUE;
final int thisThisIndex = needsCallee() ? 1 : 0; // Index of "this" parameter in this function's type
@@ -447,7 +529,7 @@ final class CompiledFunction {
// We must match all incoming parameters, except "this". Starting from 1 to skip "this".
for(int i = 1; i < minParams; ++i) {
final Type fnType = Type.typeFor(type.parameterType(i + thisThisIndex));
- final Type csType = csIsVarArg ? Type.OBJECT : Type.typeFor(callSiteType.parameterType(i + 1));
+ final Type csType = csIsVarArg ? Type.OBJECT : Type.typeFor(other.parameterType(i + 1));
if(!fnType.isEquivalentTo(csType)) {
return false;
}
@@ -669,9 +751,9 @@ final class CompiledFunction {
return sb.toString();
}
- private void logRecompile(final String reason, final FunctionNode fn, final MethodType callSiteType, final Map<Integer, Type> ipp) {
+ private void logRecompile(final String reason, final FunctionNode fn, final MethodType type, final Map<Integer, Type> ipp) {
if (log.isEnabled()) {
- log.info(reason, DebugLogger.quote(fn.getName()), " signature: ", callSiteType, " ", toStringInvalidations(ipp));
+ log.info(reason, DebugLogger.quote(fn.getName()), " signature: ", type, " ", toStringInvalidations(ipp));
}
}
@@ -732,8 +814,6 @@ final class CompiledFunction {
compiler.persistClassInfo(cacheKey, normalFn);
}
- FunctionNode fn2 = effectiveOptInfo.reparse();
- fn2 = compiler.compile(fn2, CompilationPhases.COMPILE_UPTO_BYTECODE);
log.info("Done.");
final boolean canBeDeoptimized = normalFn.canBeDeoptimized();
diff --git a/src/jdk/nashorn/internal/runtime/Context.java b/src/jdk/nashorn/internal/runtime/Context.java
index aa3306a4..e77c204a 100644
--- a/src/jdk/nashorn/internal/runtime/Context.java
+++ b/src/jdk/nashorn/internal/runtime/Context.java
@@ -33,13 +33,13 @@ import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.Source.sourceFor;
-
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.lang.invoke.SwitchPoint;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
@@ -127,6 +127,16 @@ public final class Context {
private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
+ /**
+ * Keeps track of which builtin prototypes and properties have been relinked
+ * Currently we are conservative and associate the name of a builtin class with all
+ * its properties, so it's enough to invalidate a property to break all assumptions
+ * about a prototype. This can be changed to a more fine grained approach, but no one
+ * ever needs this, given the very rare occurance of swapping out only parts of
+ * a builtin v.s. the entire builtin object
+ */
+ private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>();
+
/* Force DebuggerSupport to be loaded. */
static {
DebuggerSupport.FORCELOAD = true;
@@ -148,7 +158,7 @@ public final class Context {
}
/**
- * Return the context for this installer
+ * Return the script environment for this installer
* @return ScriptEnvironment
*/
@Override
@@ -212,6 +222,20 @@ public final class Context {
}
return null;
}
+
+ @Override
+ public CodeInstaller<ScriptEnvironment> withNewLoader() {
+ return new ContextCodeInstaller(context, context.createNewLoader(), codeSource);
+ }
+
+ @Override
+ public boolean isCompatibleWith(final CodeInstaller<ScriptEnvironment> other) {
+ if (other instanceof ContextCodeInstaller) {
+ final ContextCodeInstaller cci = (ContextCodeInstaller)other;
+ return cci.context == context && cci.codeSource == codeSource;
+ }
+ return false;
+ }
}
/** Is Context global debug mode enabled ? */
@@ -1113,6 +1137,9 @@ public final class Context {
StoredScript storedScript = null;
FunctionNode functionNode = null;
+ // We only use the code store here if optimistic types are disabled. With optimistic types,
+ // code is stored per function in RecompilableScriptFunctionData.
+ // TODO: This should really be triggered by lazy compilation, not optimistic types.
final boolean useCodeStore = env._persistent_cache && !env._parse_only && !env._optimistic_types;
final String cacheKey = useCodeStore ? CodeStore.getCacheKey(0, null) : null;
@@ -1199,7 +1226,7 @@ public final class Context {
final String mainClassName = storedScript.getMainClassName();
final byte[] mainClassBytes = classBytes.get(mainClassName);
final Class<?> mainClass = installer.install(mainClassName, mainClassBytes);
- final Map<Integer, FunctionInitializer> initialzers = storedScript.getInitializers();
+ final Map<Integer, FunctionInitializer> initializers = storedScript.getInitializers();
installedClasses.put(mainClassName, mainClass);
@@ -1219,8 +1246,8 @@ public final class Context {
if (constant instanceof RecompilableScriptFunctionData) {
final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constant;
data.initTransients(source, installer);
- if (initialzers != null) {
- final FunctionInitializer initializer = initialzers.get(data.getFunctionNodeId());
+ final FunctionInitializer initializer = initializers.get(data.getFunctionNodeId());
+ if (initializer != null) {
initializer.setCode(installedClasses.get(initializer.getClassName()));
data.initializeCode(initializer);
}
@@ -1371,4 +1398,34 @@ public final class Context {
return null;
}
+ /**
+ * This is a special kind of switchpoint used to guard builtin
+ * properties and prototypes. In the future it might contain
+ * logic to e.g. multiple switchpoint classes.
+ */
+ public static final class BuiltinSwitchPoint extends SwitchPoint {
+ //empty
+ }
+
+ /**
+ * Create a new builtin switchpoint and return it
+ * @param name key name
+ * @return new builtin switchpoint
+ */
+ public SwitchPoint newBuiltinSwitchPoint(final String name) {
+ assert builtinSwitchPoints.get(name) == null;
+ final SwitchPoint sp = new BuiltinSwitchPoint();
+ builtinSwitchPoints.put(name, sp);
+ return sp;
+ }
+
+ /**
+ * Return the builtin switchpoint for a particular key name
+ * @param name key name
+ * @return builtin switchpoint or null if none
+ */
+ public SwitchPoint getBuiltinSwitchPoint(final String name) {
+ return builtinSwitchPoints.get(name);
+ }
+
}
diff --git a/src/jdk/nashorn/internal/runtime/Debug.java b/src/jdk/nashorn/internal/runtime/Debug.java
index c152418a..a2d136fc 100644
--- a/src/jdk/nashorn/internal/runtime/Debug.java
+++ b/src/jdk/nashorn/internal/runtime/Debug.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.parser.TokenType.EOF;
-
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenStream;
@@ -42,12 +41,12 @@ public final class Debug {
/**
* Return the topmost JavaScript frame in a stack trace
- * @param e
+ * @param t throwable that contains the stack trace
* @return line describing the topmost JavaScript frame
*/
- public static String firstJSFrame(final Throwable e) {
- for (final StackTraceElement ste : e.getStackTrace()) {
- if(ECMAErrors.isScriptFrame(ste)) {
+ public static String firstJSFrame(final Throwable t) {
+ for (final StackTraceElement ste : t.getStackTrace()) {
+ if (ECMAErrors.isScriptFrame(ste)) {
return ste.toString();
}
}
diff --git a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
index f75dc829..c868aab2 100644
--- a/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
+++ b/src/jdk/nashorn/internal/runtime/FinalScriptFunctionData.java
@@ -60,13 +60,13 @@ final class FinalScriptFunctionData extends ScriptFunctionData {
* @param specs specializations
* @param flags {@link ScriptFunctionData} flags
*/
- FinalScriptFunctionData(final String name, final MethodHandle mh, final MethodHandle[] specs, final int flags) {
+ FinalScriptFunctionData(final String name, final MethodHandle mh, final Specialization[] specs, final int flags) {
super(name, methodHandleArity(mh), flags);
addInvoker(mh);
if (specs != null) {
- for (final MethodHandle spec : specs) {
- addInvoker(spec);
+ for (final Specialization spec : specs) {
+ addInvoker(spec.getMethodHandle(), spec);
}
}
}
@@ -114,16 +114,25 @@ final class FinalScriptFunctionData extends ScriptFunctionData {
return MethodType.genericMethodType(max + 1);
}
- private void addInvoker(final MethodHandle mh) {
+ private CompiledFunction addInvoker(final MethodHandle mh, final Specialization specialization) {
assert !needsCallee(mh);
+
+ final CompiledFunction invoker;
if (isConstructor(mh)) {
// only nasgen constructors: (boolean, self, args) are subject to binding a boolean newObj. isConstructor
// is too conservative a check. However, isConstructor(mh) always implies isConstructor param
assert isConstructor();
- code.add(CompiledFunction.createBuiltInConstructor(mh));
+ invoker = CompiledFunction.createBuiltInConstructor(mh);
} else {
- code.add(new CompiledFunction(mh));
+ invoker = new CompiledFunction(mh, null, specialization);
}
+ code.add(invoker);
+
+ return invoker;
+ }
+
+ private CompiledFunction addInvoker(final MethodHandle mh) {
+ return addInvoker(mh, null);
}
private static int methodHandleArity(final MethodHandle mh) {
diff --git a/src/jdk/nashorn/internal/runtime/FindProperty.java b/src/jdk/nashorn/internal/runtime/FindProperty.java
index 06f682bf..b4e00124 100644
--- a/src/jdk/nashorn/internal/runtime/FindProperty.java
+++ b/src/jdk/nashorn/internal/runtime/FindProperty.java
@@ -79,6 +79,8 @@ public final class FindProperty {
*
* @param type type of getter, e.g. int.class if we want a function with {@code get()I} signature
* @param programPoint program point, or INVALID_PROGRAM_POINT if pessimistic
+ * @param request link request
+ *
* @return method handle for the getter
*/
public MethodHandle getGetter(final Class<?> type, final int programPoint, final LinkRequest request) {
@@ -102,6 +104,7 @@ public final class FindProperty {
*
* @param type type of setter, e.g. int.class if we want a function with {@code set(I)V} signature
* @param strict are we in strict mode
+ * @param request link request
*
* @return method handle for the getter
*/
diff --git a/src/jdk/nashorn/internal/runtime/GlobalConstants.java b/src/jdk/nashorn/internal/runtime/GlobalConstants.java
index 3a104812..6099a70b 100644
--- a/src/jdk/nashorn/internal/runtime/GlobalConstants.java
+++ b/src/jdk/nashorn/internal/runtime/GlobalConstants.java
@@ -358,8 +358,12 @@ public final class GlobalConstants implements Loggable {
* @param c constant value
* @return method handle (with dummy receiver) that returns this constant
*/
+ public static MethodHandle staticConstantGetter(final Object c) {
+ return MH.dropArguments(JSType.unboxConstant(c), 0, Object.class);
+ }
+
private MethodHandle constantGetter(final Object c) {
- final MethodHandle mh = MH.dropArguments(JSType.unboxConstant(c), 0, Object.class);
+ final MethodHandle mh = staticConstantGetter(c);
if (log.isEnabled()) {
return MethodHandleFactory.addDebugPrintout(log, Level.FINEST, mh, "getting as constant");
}
diff --git a/src/jdk/nashorn/internal/runtime/GlobalFunctions.java b/src/jdk/nashorn/internal/runtime/GlobalFunctions.java
index 2776cc66..c7094adc 100644
--- a/src/jdk/nashorn/internal/runtime/GlobalFunctions.java
+++ b/src/jdk/nashorn/internal/runtime/GlobalFunctions.java
@@ -42,12 +42,30 @@ public final class GlobalFunctions {
/** 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);
+ /** ParseInt - NaN for booleans (thru string conversion to number conversion) */
+ public static final MethodHandle PARSEINT_Z = MH.dropArguments(MH.dropArguments(MH.constant(double.class, Double.NaN), 0, boolean.class), 0, Object.class);
+
+ /** ParseInt - identity for ints */
+ public static final MethodHandle PARSEINT_I = MH.dropArguments(MH.identity(int.class), 0, Object.class);
+
+ /** ParseInt - identity for longs */
+ public static final MethodHandle PARSEINT_J = MH.dropArguments(MH.identity(long.class), 0, Object.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);
+ /** isNan for integers - always false */
+ public static final MethodHandle IS_NAN_I = MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class);
+
+ /** isNan for longs - always false */
+ public static final MethodHandle IS_NAN_J = MH.dropArguments(MH.constant(boolean.class, false), 0, Object.class);
+
+ /** IsNan for doubles - use Double.isNaN */
+ public static final MethodHandle IS_NAN_D = MH.dropArguments(MH.findStatic(MethodHandles.lookup(), Double.class, "isNaN", MH.type(boolean.class, double.class)), 0, Object.class);
+
/** Methodhandle to implementation of ECMA 15.1.2.4, isNaN */
public static final MethodHandle IS_NAN = findOwnMH("isNaN", boolean.class, Object.class, Object.class);
diff --git a/src/jdk/nashorn/internal/runtime/OptimisticBuiltins.java b/src/jdk/nashorn/internal/runtime/OptimisticBuiltins.java
new file mode 100644
index 00000000..6829ddeb
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/OptimisticBuiltins.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
+
+/**
+ * This is an interface for classes that need custom linkage logic. This means Native objects
+ * that contain optimistic native methods, that need special/extra rules for linking, guards and
+ * SwitchPointing, known and internal to the Native object for its linkage
+ */
+public interface OptimisticBuiltins {
+
+ /**
+ * Return an instance of the linking logic we need for a particular LinkLogic
+ * subclass, gotten from the compile time annotation of a specialized builtin method
+ * No assumptions can be made about the lifetime of the instance. The receiver may
+ * keep it as a perpetual final instance field or create new linking logic depending
+ * on its current state for each call, depending on if the global state has changed
+ * or other factors
+ *
+ * @param clazz linking logic class
+ * @return linking logic instance for this class
+ */
+ public SpecializedFunction.LinkLogic getLinkLogic(final Class<? extends LinkLogic> clazz);
+
+ /**
+ * Does this link logic vary depending on which instance we are working with.
+ * Then we have to sort out certain primitives, as they are created as new
+ * objects in the wrapFilter by JavaScript semantics. An example of instance only
+ * assumptions are switchPoints per instance, as in NativeArray. NativeString is
+ * fine, as it's only static.
+ *
+ * TODO: finer granularity on this, on the function level so certain functions
+ * are forbidden only. Currently we don't have enough specialization to bump into this
+ *
+ * @return true if there are per instance assumptions for the optimism
+ */
+ public boolean hasPerInstanceAssumptions();
+
+}
diff --git a/src/jdk/nashorn/internal/runtime/Property.java b/src/jdk/nashorn/internal/runtime/Property.java
index 1f9f1459..f57246ca 100644
--- a/src/jdk/nashorn/internal/runtime/Property.java
+++ b/src/jdk/nashorn/internal/runtime/Property.java
@@ -28,7 +28,6 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.runtime.PropertyDescriptor.CONFIGURABLE;
import static jdk.nashorn.internal.runtime.PropertyDescriptor.ENUMERABLE;
import static jdk.nashorn.internal.runtime.PropertyDescriptor.WRITABLE;
-
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.SwitchPoint;
@@ -84,6 +83,9 @@ public abstract class Property implements Serializable {
*/
public static final int IS_NASGEN_PRIMITIVE = 1 << 6;
+ /** Is this a builtin property, e.g. Function.prototype.apply */
+ public static final int IS_BUILTIN = 1 << 7;
+
/** Is this property bound to a receiver? This means get/set operations will be delegated to
* a statically defined object instead of the object passed as callsite parameter. */
public static final int IS_BOUND = 1 << 7;
@@ -101,7 +103,7 @@ public abstract class Property implements Serializable {
private final int slot;
/** SwitchPoint that is invalidated when property is changed, optional */
- protected transient SwitchPoint changeCallback;
+ protected transient SwitchPoint builtinSwitchPoint;
private static final long serialVersionUID = 2099814273074501176L;
@@ -125,10 +127,10 @@ public abstract class Property implements Serializable {
* @param property source property
*/
Property(final Property property, final int flags) {
- this.key = property.key;
- this.slot = property.slot;
- this.changeCallback = property.changeCallback;
- this.flags = flags;
+ this.key = property.key;
+ this.slot = property.slot;
+ this.builtinSwitchPoint = property.builtinSwitchPoint;
+ this.flags = flags;
}
/**
@@ -182,8 +184,26 @@ public abstract class Property implements Serializable {
* changed
* @param sp SwitchPoint to use for change callback
*/
- public final void setChangeCallback(final SwitchPoint sp) {
- this.changeCallback = sp;
+ public final void setBuiltinSwitchPoint(final SwitchPoint sp) {
+ this.builtinSwitchPoint = sp;
+ }
+
+ /**
+ * Builtin properties have an invalidation switchpoint that is
+ * invalidated when they are set, this is a getter for it
+ * @return builtin switchpoint, or null if none
+ */
+ public final SwitchPoint getBuiltinSwitchPoint() {
+ return builtinSwitchPoint;
+ }
+
+ /**
+ * Checks if this is a builtin property, this means that it has
+ * a builtin switchpoint that hasn't been invalidated by a setter
+ * @return true if builtin, untouched (unset) property
+ */
+ public boolean isBuiltin() {
+ return builtinSwitchPoint != null && !builtinSwitchPoint.hasBeenInvalidated();
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/PropertyMap.java b/src/jdk/nashorn/internal/runtime/PropertyMap.java
index d8e76082..61912332 100644
--- a/src/jdk/nashorn/internal/runtime/PropertyMap.java
+++ b/src/jdk/nashorn/internal/runtime/PropertyMap.java
@@ -950,7 +950,7 @@ public final class PropertyMap implements Iterable<Object>, Serializable {
@Override
public void remove() {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("remove");
}
}
diff --git a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
index 2d5ccc05..f3ad2339 100644
--- a/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
+++ b/src/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java
@@ -31,6 +31,7 @@ import java.io.IOException;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -268,7 +269,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
if (this.source == null && this.installer == null) {
this.source = src;
this.installer = inst;
- } else if (this.source != src || this.installer != inst) {
+ } else if (this.source != src || !this.installer.isCompatibleWith(inst)) {
// Existing values must be same as those passed as parameters
throw new IllegalArgumentException();
}
@@ -407,6 +408,17 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return getCompiler(fn, actualCallSiteType, newLocals(runtimeScope), null, null);
}
+ /**
+ * Returns a code installer for installing new code. If we're using either optimistic typing or loader-per-compile,
+ * then asks for a code installer with a new class loader; otherwise just uses the current installer. We use
+ * a new class loader with optimistic typing so that deoptimized code can get reclaimed by GC.
+ * @return a code installer for installing new code.
+ */
+ private CodeInstaller<ScriptEnvironment> getInstallerForNewCode() {
+ final ScriptEnvironment env = installer.getOwner();
+ return env._optimistic_types || env._loader_per_compile ? installer.withNewLoader() : installer;
+ }
+
Compiler getCompiler(final FunctionNode functionNode, final MethodType actualCallSiteType,
final ScriptObject runtimeScope, final Map<Integer, Type> invalidatedProgramPoints,
final int[] continuationEntryPoints) {
@@ -417,7 +429,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return new Compiler(
context,
context.getEnv(),
- installer,
+ getInstallerForNewCode(),
functionNode.getSource(), // source
context.getErrorManager(),
isStrict() | functionNode.isStrict(), // is strict
@@ -454,7 +466,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
// CompilationEnvironment#declareLocalSymbol()).
if (log.isEnabled()) {
- log.info("Type specialization of '", functionName, "' signature: ", actualCallSiteType);
+ log.info("Parameter type specialization of '", functionName, "' signature: ", actualCallSiteType);
}
final boolean persistentCache = usePersistentCodeCache() && persist;
@@ -463,11 +475,12 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
final TypeMap typeMap = typeMap(actualCallSiteType);
final Type[] paramTypes = typeMap == null ? null : typeMap.getParameterTypes(functionNodeId);
cacheKey = CodeStore.getCacheKey(functionNodeId, paramTypes);
- final StoredScript script = installer.loadScript(source, cacheKey);
+ final CodeInstaller<ScriptEnvironment> newInstaller = getInstallerForNewCode();
+ final StoredScript script = newInstaller.loadScript(source, cacheKey);
if (script != null) {
Compiler.updateCompilationId(script.getCompilationId());
- return install(script);
+ return installStoredScript(script, newInstaller);
}
}
@@ -481,15 +494,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return new FunctionInitializer(compiledFn, compiler.getInvalidatedProgramPoints());
}
-
- /**
- * Install this script using the given {@code installer}.
- *
- * @param script the compiled script
- * @return the function initializer
- */
- private FunctionInitializer install(final StoredScript script) {
-
+ private static Map<String, Class<?>> installStoredScriptClasses(final StoredScript script, final CodeInstaller<ScriptEnvironment> installer) {
final Map<String, Class<?>> installedClasses = new HashMap<>();
final Map<String, byte[]> classBytes = script.getClassBytes();
final String mainClassName = script.getMainClassName();
@@ -501,14 +506,25 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
for (final Map.Entry<String, byte[]> entry : classBytes.entrySet()) {
final String className = entry.getKey();
- final byte[] code = entry.getValue();
+ final byte[] bytecode = entry.getValue();
if (className.equals(mainClassName)) {
continue;
}
- installedClasses.put(className, installer.install(className, code));
+ installedClasses.put(className, installer.install(className, bytecode));
}
+ return installedClasses;
+ }
+
+ /**
+ * Install this script using the given {@code installer}.
+ *
+ * @param script the compiled script
+ * @return the function initializer
+ */
+ private FunctionInitializer installStoredScript(final StoredScript script, final CodeInstaller<ScriptEnvironment> newInstaller) {
+ final Map<String, Class<?>> installedClasses = installStoredScriptClasses(script, newInstaller);
final Map<Integer, FunctionInitializer> initializers = script.getInitializers();
assert initializers != null;
@@ -523,7 +539,7 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
}
}
- installer.initialize(installedClasses.values(), source, constants);
+ newInstaller.initialize(installedClasses.values(), source, constants);
initializer.setCode(installedClasses.get(initializer.getClassName()));
return initializer;
}
@@ -588,15 +604,19 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return lookupCodeMethod(fn.getCompileUnit().getCode(), type);
}
- MethodHandle lookupCodeMethod(final Class<?> code, final MethodType targetType) {
- log.info("Looking up ", DebugLogger.quote(name), " type=", targetType);
- return MH.findStatic(LOOKUP, code, functionName, targetType);
+ MethodHandle lookupCodeMethod(final Class<?> codeClass, final MethodType targetType) {
+ if (log.isEnabled()) {
+ log.info("Looking up ", DebugLogger.quote(name), " type=", targetType);
+ }
+ return MH.findStatic(LOOKUP, codeClass, functionName, targetType);
}
/**
* Initializes this function data with the eagerly generated version of the code. This method can only be invoked
* by the compiler internals in Nashorn and is public for implementation reasons only. Attempting to invoke it
* externally will result in an exception.
+ *
+ * @param initializer FunctionInitializer for this data
*/
public void initializeCode(final FunctionInitializer initializer) {
// Since the method is public, we double-check that we aren't invoked with an inappropriate compile unit.
@@ -658,8 +678,8 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
@Override
- synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
- CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope);
+ synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
+ CompiledFunction existingBest = super.getBest(callSiteType, runtimeScope, forbidden);
if (existingBest == null) {
existingBest = addCode(compileTypeSpecialization(callSiteType, runtimeScope, true), callSiteType);
}
@@ -723,6 +743,10 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp
return functionNodeId;
}
+ /**
+ * Get the source for the script
+ * @return source
+ */
public Source getSource() {
return source;
}
diff --git a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
index e6034bf5..cd528a01 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptEnvironment.java
@@ -181,9 +181,6 @@ public final class ScriptEnvironment {
/** print symbols and their contents for the script */
public final boolean _print_symbols;
- /** range analysis for known types */
- public final boolean _range_analysis;
-
/** is this environment in scripting mode? */
public final boolean _scripting;
@@ -255,7 +252,6 @@ public final class ScriptEnvironment {
_print_parse = options.getBoolean("print.parse");
_print_lower_parse = options.getBoolean("print.lower.parse");
_print_symbols = options.getBoolean("print.symbols");
- _range_analysis = options.getBoolean("range.analysis");
_scripting = options.getBoolean("scripting");
_strict = options.getBoolean("strict");
_version = options.getBoolean("version");
diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunction.java b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
index d9f71a2a..0ba06b3a 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptFunction.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunction.java
@@ -30,26 +30,29 @@ import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.SwitchPoint;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
-
+import java.util.HashSet;
+import java.util.List;
import jdk.internal.dynalink.CallSiteDescriptor;
import jdk.internal.dynalink.linker.GuardedInvocation;
import jdk.internal.dynalink.linker.LinkRequest;
import jdk.internal.dynalink.support.Guards;
import jdk.nashorn.internal.codegen.ApplySpecialization;
+import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.objects.NativeFunction;
-import jdk.nashorn.internal.runtime.ScriptFunctionData;
-import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.ScriptRuntime;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
+import jdk.nashorn.internal.runtime.logging.DebugLogger;
/**
* Runtime representation of a JavaScript function.
@@ -114,7 +117,7 @@ public abstract class ScriptFunction extends ScriptObject {
final MethodHandle methodHandle,
final PropertyMap map,
final ScriptObject scope,
- final MethodHandle[] specs,
+ final Specialization[] specs,
final int flags) {
this(new FinalScriptFunctionData(name, methodHandle, specs, flags), map, scope);
@@ -468,13 +471,12 @@ public abstract class ScriptFunction extends ScriptObject {
protected GuardedInvocation findNewMethod(final CallSiteDescriptor desc, final LinkRequest request) {
final MethodType type = desc.getMethodType();
assert desc.getMethodType().returnType() == Object.class && !NashornCallSiteDescriptor.isOptimistic(desc);
- final CompiledFunction cf = data.getBestConstructor(type, scope);
+ final CompiledFunction cf = data.getBestConstructor(type, scope, CompiledFunction.NO_FUNCTIONS);
final GuardedInvocation bestCtorInv = cf.createConstructorInvocation();
//TODO - ClassCastException
return new GuardedInvocation(pairArguments(bestCtorInv.getInvocation(), type), getFunctionGuard(this, cf.getFlags()), bestCtorInv.getSwitchPoints(), null);
}
- @SuppressWarnings("unused")
private static Object wrapFilter(final Object obj) {
if (obj instanceof ScriptObject || !ScriptFunctionData.isPrimitiveThis(obj)) {
return obj;
@@ -490,6 +492,35 @@ public abstract class ScriptFunction extends ScriptObject {
}
/**
+ * Some receivers are primitive, in that case, according to the Spec we create a new
+ * native object per callsite with the wrap filter. We can only apply optimistic builtins
+ * if there is no per instance state saved for these wrapped objects (e.g. currently NativeStrings),
+ * otherwise we can't create optimistic versions
+ *
+ * @param self receiver
+ * @param linkLogicClass linkLogicClass, or null if no link logic exists
+ * @return link logic instance, or null if one could not be constructed for this receiver
+ */
+ private static LinkLogic getLinkLogic(final Object self, final Class<? extends LinkLogic> linkLogicClass) {
+ if (linkLogicClass == null) {
+ return LinkLogic.EMPTY_INSTANCE; //always OK to link this, specialization but without special linking logic
+ }
+
+ if (!Context.getContextTrusted().getEnv()._optimistic_types) {
+ return null; //if optimistic types are off, optimistic builtins are too
+ }
+
+ final Object wrappedSelf = wrapFilter(self);
+ if (wrappedSelf instanceof OptimisticBuiltins) {
+ if (wrappedSelf != self && ((OptimisticBuiltins)wrappedSelf).hasPerInstanceAssumptions()) {
+ return null; //pessimistic - we created a wrapped object different from the primitive, but the assumptions have instance state
+ }
+ return ((OptimisticBuiltins)wrappedSelf).getLinkLogic(linkLogicClass);
+ }
+ return null;
+ }
+
+ /**
* dyn:call call site signature: (callee, thiz, [args...])
* generated method signature: (callee, thiz, [args...])
*
@@ -547,8 +578,53 @@ public abstract class ScriptFunction extends ScriptObject {
}
} //else just fall through and link as ordinary function or unstable apply
- final int programPoint = NashornCallSiteDescriptor.isOptimistic(desc) ? NashornCallSiteDescriptor.getProgramPoint(desc) : INVALID_PROGRAM_POINT;
- final CompiledFunction cf = data.getBestInvoker(type, scope);
+ int programPoint = INVALID_PROGRAM_POINT;
+ if (NashornCallSiteDescriptor.isOptimistic(desc)) {
+ programPoint = NashornCallSiteDescriptor.getProgramPoint(desc);
+ }
+
+ CompiledFunction cf = data.getBestInvoker(type, scope, CompiledFunction.NO_FUNCTIONS);
+ final Object self = request.getArguments()[1];
+ final Collection<CompiledFunction> forbidden = new HashSet<>();
+
+ //check for special fast versions of the compiled function
+ final List<SwitchPoint> sps = new ArrayList<>();
+ Class<? extends Throwable> exceptionGuard = null;
+
+ while (cf.isSpecialization()) {
+ final Class<? extends LinkLogic> linkLogicClass = cf.getLinkLogicClass();
+ //if linklogic is null, we can always link with the standard mechanism, it's still a specialization
+ final LinkLogic linkLogic = getLinkLogic(self, linkLogicClass);
+
+ if (linkLogic != null && linkLogic.checkLinkable(self, desc, request)) {
+ final DebugLogger log = Context.getContextTrusted().getLogger(Compiler.class);
+
+ if (log.isEnabled()) {
+ log.info("Linking optimistic builtin function: '", name, "' args=", Arrays.toString(request.getArguments()), " desc=", desc);
+ }
+
+ final SwitchPoint[] msps = linkLogic.getModificationSwitchPoints();
+ if (msps != null) {
+ for (final SwitchPoint sp : msps) {
+ if (sp != null) {
+ assert !sp.hasBeenInvalidated();
+ sps.add(sp);
+ }
+ }
+ }
+
+ exceptionGuard = linkLogic.getRelinkException();
+
+ break;
+ }
+
+ //could not link this specialization because link check failed
+ forbidden.add(cf);
+ final CompiledFunction oldCf = cf;
+ cf = data.getBestInvoker(type, scope, forbidden);
+ assert oldCf != cf;
+ }
+
final GuardedInvocation bestInvoker = cf.createFunctionInvocation(type.returnType(), programPoint);
final MethodHandle callHandle = bestInvoker.getInvocation();
@@ -588,7 +664,20 @@ public abstract class ScriptFunction extends ScriptObject {
boundHandle = pairArguments(boundHandle, type);
- return new GuardedInvocation(boundHandle, guard == null ? getFunctionGuard(this, cf.getFlags()) : guard, bestInvoker.getSwitchPoints(), null);
+ if (bestInvoker.getSwitchPoints() != null) {
+ sps.addAll(Arrays.asList(bestInvoker.getSwitchPoints()));
+ }
+ final SwitchPoint[] spsArray = sps.isEmpty() ? null : sps.toArray(new SwitchPoint[sps.size()]);
+
+ return new GuardedInvocation(
+ boundHandle,
+ guard == null ?
+ getFunctionGuard(
+ this,
+ cf.getFlags()) :
+ guard,
+ spsArray,
+ exceptionGuard);
}
private GuardedInvocation createApplyOrCallCall(final boolean isApply, final CallSiteDescriptor desc, final LinkRequest request, final Object[] args) {
@@ -610,7 +699,7 @@ public abstract class ScriptFunction extends ScriptObject {
//box call back to apply
CallSiteDescriptor appliedDesc = desc;
- final SwitchPoint applyToCallSwitchPoint = Global.instance().getChangeCallback("apply");
+ final SwitchPoint applyToCallSwitchPoint = Global.getBuiltinFunctionApplySwitchPoint();
//enough to change the proto switchPoint here
final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc);
@@ -656,7 +745,7 @@ public abstract class ScriptFunction extends ScriptObject {
}
}
- appliedDesc = appliedDesc.changeMethodType(appliedType);
+ appliedDesc = appliedDesc.changeMethodType(appliedType); //no extra args
// Create the same arguments for the delegate linking request that would be passed in an actual apply'd invocation
final Object[] appliedArgs = new Object[isApply ? 3 : appliedType.parameterCount()];
@@ -681,6 +770,7 @@ public abstract class ScriptFunction extends ScriptObject {
// Ask the linker machinery for an invocation of the target function
final LinkRequest appliedRequest = request.replaceArguments(appliedDesc, appliedArgs);
+
GuardedInvocation appliedInvocation;
try {
appliedInvocation = Bootstrap.getLinkerServices().getGuardedInvocation(appliedRequest);
@@ -742,7 +832,7 @@ public abstract class ScriptFunction extends ScriptObject {
// We need to account for the dropped (apply|call) function argument.
guard = MH.dropArguments(guard, 0, descType.parameterType(0));
// Take the "isApplyFunction" guard, and bind it to this function.
- MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this);
+ MethodHandle applyFnGuard = MH.insertArguments(IS_APPLY_FUNCTION, 2, this); //TODO replace this with switchpoint
// Adapt the guard to receive all the arguments that the original guard does.
applyFnGuard = MH.dropArguments(applyFnGuard, 2, guardType.parameterArray());
// Fold the original function guard into our apply guard.
@@ -894,6 +984,7 @@ public abstract class ScriptFunction extends ScriptObject {
return self instanceof ScriptFunction && ((ScriptFunction)self).data == data && arg instanceof ScriptObject;
}
+ //TODO this can probably be removed given that we have builtin switchpoints in the context
@SuppressWarnings("unused")
private static boolean isApplyFunction(final boolean appliedFnCondition, final Object self, final Object expectedSelf) {
// NOTE: we're using self == expectedSelf as we're only using this with built-in functions apply() and call()
diff --git a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
index d92b12d9..3472cda1 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptFunctionData.java
@@ -28,13 +28,13 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import jdk.nashorn.internal.runtime.linker.LinkerCallSite;
@@ -136,7 +136,7 @@ public abstract class ScriptFunctionData implements Serializable {
final MethodHandle boundInvoker = bindInvokeHandle(originalInv.createComposableInvoker(), fn, self, args);
if (isConstructor()) {
- return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.createComposableConstructor(), fn, args));
+ return new CompiledFunction(boundInvoker, bindConstructHandle(originalInv.createComposableConstructor(), fn, args), null);
}
return new CompiledFunction(boundInvoker);
@@ -224,18 +224,22 @@ public abstract class ScriptFunctionData implements Serializable {
* @param callSiteType callsite type
* @return compiled function object representing the best invoker.
*/
- final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope) {
- final CompiledFunction cf = getBest(callSiteType, runtimeScope);
+ final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope) {
+ return getBestInvoker(callSiteType, runtimeScope, CompiledFunction.NO_FUNCTIONS);
+ }
+
+ final CompiledFunction getBestInvoker(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
+ final CompiledFunction cf = getBest(callSiteType, runtimeScope, forbidden);
assert cf != null;
return cf;
}
- final CompiledFunction getBestConstructor(final MethodType callSiteType, final ScriptObject runtimeScope) {
+ final CompiledFunction getBestConstructor(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
if (!isConstructor()) {
throw typeError("not.a.constructor", toSource());
}
// Constructor call sites don't have a "this", but getBest is meant to operate on "callee, this, ..." style
- final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class), runtimeScope);
+ final CompiledFunction cf = getBest(callSiteType.insertParameterTypes(1, Object.class), runtimeScope, forbidden);
return cf;
}
@@ -350,7 +354,7 @@ public abstract class ScriptFunctionData implements Serializable {
* scope is not known, but that might cause compilation of code that will need more deoptimization passes.
* @return the best function for the specified call site type.
*/
- CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope) {
+ CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection<CompiledFunction> forbidden) {
assert callSiteType.parameterCount() >= 2 : callSiteType; // Must have at least (callee, this)
assert callSiteType.parameterType(0).isAssignableFrom(ScriptFunction.class) : callSiteType; // Callee must be assignable from script function
@@ -363,8 +367,8 @@ public abstract class ScriptFunctionData implements Serializable {
}
CompiledFunction best = null;
- for(final CompiledFunction candidate: code) {
- if(candidate.betterThanFinal(best, callSiteType)) {
+ for (final CompiledFunction candidate: code) {
+ if (!forbidden.contains(candidate) && candidate.betterThanFinal(best, callSiteType)) {
best = candidate;
}
}
@@ -376,7 +380,7 @@ public abstract class ScriptFunctionData implements Serializable {
abstract boolean isRecompilable();
CompiledFunction getGeneric(final ScriptObject runtimeScope) {
- return getBest(getGenericType(), runtimeScope);
+ return getBest(getGenericType(), runtimeScope, CompiledFunction.NO_FUNCTIONS);
}
/**
@@ -420,7 +424,7 @@ public abstract class ScriptFunctionData implements Serializable {
final List<CompiledFunction> boundList = new LinkedList<>();
final ScriptObject runtimeScope = fn.getScope();
- final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(runtimeScope), getGenericConstructor(runtimeScope));
+ final CompiledFunction bindTarget = new CompiledFunction(getGenericInvoker(runtimeScope), getGenericConstructor(runtimeScope), null);
boundList.add(bind(bindTarget, fn, self, allArgs));
return new FinalScriptFunctionData(name, Math.max(0, getArity() - length), boundList, boundFlags);
diff --git a/src/jdk/nashorn/internal/runtime/ScriptObject.java b/src/jdk/nashorn/internal/runtime/ScriptObject.java
index 8d616b58..87d28976 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java
@@ -47,7 +47,6 @@ 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;
import java.lang.invoke.MethodType;
@@ -820,6 +819,19 @@ public abstract class ScriptObject implements PropertyAccess {
return false;
}
+ private SwitchPoint findBuiltinSwitchPoint(final String key) {
+ for (ScriptObject myProto = getProto(); myProto != null; myProto = myProto.getProto()) {
+ final Property prop = myProto.getMap().findProperty(key);
+ if (prop != null) {
+ final SwitchPoint sp = prop.getBuiltinSwitchPoint();
+ if (sp != null && !sp.hasBeenInvalidated()) {
+ return sp;
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Add a new property to the object.
* <p>
@@ -923,10 +935,10 @@ public abstract class ScriptObject implements PropertyAccess {
* creating setters that probably aren't used. Inject directly into the spill pool
* the defaults for "arguments" and "caller"
*
- * @param key
- * @param propertyFlags
- * @param getter
- * @param setter
+ * @param key property key
+ * @param propertyFlags flags
+ * @param getter getter for {@link UserAccessorProperty}, null if not present or N/A
+ * @param setter setter for {@link UserAccessorProperty}, null if not present or N/A
*/
protected final void initUserAccessors(final String key, final int propertyFlags, final ScriptFunction getter, final ScriptFunction setter) {
final int slot = spillLength;
@@ -1514,6 +1526,23 @@ public abstract class ScriptObject implements PropertyAccess {
}
/**
+ * Get the {@link ArrayData}, for this ScriptObject, ensuring it is of a type
+ * that can handle elementType
+ * @param elementType elementType
+ * @return array data
+ */
+ public final ArrayData getArray(final Class<?> elementType) {
+ if (elementType == null) {
+ return arrayData;
+ }
+ final ArrayData newArrayData = arrayData.convert(elementType);
+ if (newArrayData != arrayData) {
+ arrayData = newArrayData;
+ }
+ return newArrayData;
+ }
+
+ /**
* Get the {@link ArrayData} for this ScriptObject if it is an array
* @return array data
*/
@@ -1720,8 +1749,8 @@ public abstract class ScriptObject implements PropertyAccess {
*/
public Object put(final Object key, final Object value, final boolean strict) {
final Object oldValue = get(key);
- final int flags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
- set(key, value, flags);
+ final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
+ set(key, value, scriptObjectFlags);
return oldValue;
}
@@ -1734,9 +1763,9 @@ public abstract class ScriptObject implements PropertyAccess {
* @param strict strict mode or not
*/
public void putAll(final Map<?, ?> otherMap, final boolean strict) {
- final int flags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
+ final int scriptObjectFlags = strict ? NashornCallSiteDescriptor.CALLSITE_STRICT : 0;
for (final Map.Entry<?, ?> entry : otherMap.entrySet()) {
- set(entry.getKey(), entry.getValue(), flags);
+ set(entry.getKey(), entry.getValue(), scriptObjectFlags);
}
}
@@ -1916,17 +1945,6 @@ public abstract class ScriptObject implements PropertyAccess {
return MH.filterArguments(methodHandle, 0, filter.asType(filter.type().changeReturnType(methodHandle.type().parameterType(0))));
}
- //this will only return true if apply is still builtin
- private static SwitchPoint checkReservedName(final CallSiteDescriptor desc, final LinkRequest request) {
- final boolean isApplyToCall = NashornCallSiteDescriptor.isApplyToCall(desc);
- final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
- if ("apply".equals(name) && isApplyToCall && Global.instance().isSpecialNameValid(name)) {
- assert Global.instance().getChangeCallback("apply") == Global.instance().getChangeCallback("call");
- return Global.instance().getChangeCallback("apply");
- }
- return null;
- }
-
/**
* Find the appropriate GET method for an invoke dynamic call.
*
@@ -1938,14 +1956,13 @@ public abstract class ScriptObject implements PropertyAccess {
*/
protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
final boolean explicitInstanceOfCheck = explicitInstanceOfCheck(desc, request);
- final String name;
- final SwitchPoint reservedNameSwitchPoint;
- reservedNameSwitchPoint = checkReservedName(desc, request);
- if (reservedNameSwitchPoint != null) {
- name = "call"; //turn apply into call, it is the builtin apply and has been modified to explode args
- } else {
- name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ String name;
+ name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+ if (NashornCallSiteDescriptor.isApplyToCall(desc)) {
+ if (Global.isBuiltinFunctionPrototypeApply()) {
+ name = "call";
+ }
}
if (request.isCallSiteUnstable() || hasWithScope()) {
@@ -2006,7 +2023,7 @@ public abstract class ScriptObject implements PropertyAccess {
assert OBJECT_FIELDS_ONLY || guard != null : "we always need a map guard here";
final GuardedInvocation inv = new GuardedInvocation(mh, guard, protoSwitchPoint, exception);
- return inv.addSwitchPoint(reservedNameSwitchPoint);
+ return inv.addSwitchPoint(findBuiltinSwitchPoint(name));
}
private static GuardedInvocation findMegaMorphicGetMethod(final CallSiteDescriptor desc, final String name, final boolean isMethod) {
@@ -2029,7 +2046,7 @@ public abstract class ScriptObject implements PropertyAccess {
// Marks a property as declared and sets its value. Used as slow path for block-scoped LET and CONST
@SuppressWarnings("unused")
private void declareAndSet(final String key, final Object value) {
- final PropertyMap map = getMap();
+ final PropertyMap oldMap = getMap();
final FindProperty find = findProperty(key, false);
assert find != null;
@@ -2037,7 +2054,7 @@ public abstract class ScriptObject implements PropertyAccess {
assert property != null;
assert property.needsDeclaration();
- final PropertyMap newMap = map.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION));
+ final PropertyMap newMap = oldMap.replaceProperty(property, property.removeFlags(Property.NEEDS_DECLARATION));
setMap(newMap);
set(key, value, 0);
}
@@ -2166,7 +2183,7 @@ public abstract class ScriptObject implements PropertyAccess {
}
}
- final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation();
+ final GuardedInvocation inv = new SetMethodCreator(this, find, desc, request).createGuardedInvocation(findBuiltinSwitchPoint(name));
final GuardedInvocation cinv = Global.getConstants().findSetMethod(find, this, inv, desc, request);
if (cinv != null) {
@@ -2429,7 +2446,7 @@ public abstract class ScriptObject implements PropertyAccess {
@Override
public void remove() {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("remove");
}
}
diff --git a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java
index 47b2a8da..e927d4fc 100644
--- a/src/jdk/nashorn/internal/runtime/ScriptRuntime.java
+++ b/src/jdk/nashorn/internal/runtime/ScriptRuntime.java
@@ -32,9 +32,9 @@ import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.ECMAErrors.syntaxError;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.isRepresentableAsInt;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
+import java.lang.invoke.SwitchPoint;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Iterator;
@@ -46,6 +46,7 @@ import java.util.Objects;
import jdk.internal.dynalink.beans.StaticClass;
import jdk.nashorn.api.scripting.JSObject;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import jdk.nashorn.internal.codegen.ApplySpecialization;
import jdk.nashorn.internal.codegen.CompilerConstants;
import jdk.nashorn.internal.codegen.CompilerConstants.Call;
import jdk.nashorn.internal.ir.debug.JSONWriter;
@@ -113,6 +114,11 @@ public final class ScriptRuntime {
public static final Call THROW_REFERENCE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwReferenceError", void.class, String.class);
/**
+ * Used to invalidate builtin names, e.g "Function" mapping to all properties in Function.prototype and Function.prototype itself.
+ */
+ public static final Call INVALIDATE_RESERVED_BUILTIN_NAME = staticCallNoLookup(ScriptRuntime.class, "invalidateReservedBuiltinName", void.class, String.class);
+
+ /**
* Converts a switch tag value to a simple integer. deflt value if it can't.
*
* @param tag Switch statement tag value.
@@ -290,7 +296,7 @@ public final class ScriptRuntime {
@Override
public void remove() {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("remove");
}
}
@@ -328,7 +334,7 @@ public final class ScriptRuntime {
@Override
public void remove() {
- throw new UnsupportedOperationException();
+ throw new UnsupportedOperationException("remove");
}
};
}
@@ -998,4 +1004,19 @@ public final class ScriptRuntime {
return nx < ny;
}
+ /**
+ * Tag a reserved name as invalidated - used when someone writes
+ * to a property with this name - overly conservative, but link time
+ * is too late to apply e.g. apply-&gt;call specialization
+ * @param name property name
+ */
+ public static void invalidateReservedBuiltinName(final String name) {
+ final Context context = Context.getContextTrusted();
+ final SwitchPoint sp = context.getBuiltinSwitchPoint(name);
+ assert sp != null;
+ if (sp != null) {
+ context.getLogger(ApplySpecialization.class).info("Overwrote special name '" + name +"' - invalidating switchpoint");
+ SwitchPoint.invalidateAll(new SwitchPoint[] { sp });
+ }
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
index 99ec3135..90bbf43c 100644
--- a/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
+++ b/src/jdk/nashorn/internal/runtime/SetMethodCreator.java
@@ -28,7 +28,6 @@ package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.referenceError;
import static jdk.nashorn.internal.runtime.JSType.getAccessorTypeIndex;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.SwitchPoint;
import jdk.internal.dynalink.CallSiteDescriptor;
@@ -81,8 +80,8 @@ final class SetMethodCreator {
* Creates the actual guarded invocation that represents the dynamic setter method for the property.
* @return the actual guarded invocation that represents the dynamic setter method for the property.
*/
- GuardedInvocation createGuardedInvocation() {
- return createSetMethod().createGuardedInvocation();
+ GuardedInvocation createGuardedInvocation(final SwitchPoint builtinSwitchPoint) {
+ return createSetMethod(builtinSwitchPoint).createGuardedInvocation();
}
/**
@@ -119,7 +118,7 @@ final class SetMethodCreator {
}
}
- private SetMethod createSetMethod() {
+ private SetMethod createSetMethod(final SwitchPoint builtinSwitchPoint) {
if (find != null) {
return createExistingPropertySetter();
}
@@ -130,7 +129,7 @@ final class SetMethodCreator {
return createGlobalPropertySetter();
}
- return createNewPropertySetter();
+ return createNewPropertySetter(builtinSwitchPoint);
}
private void checkStrictCreateNewVariable() {
@@ -185,8 +184,8 @@ final class SetMethodCreator {
return new SetMethod(MH.filterArguments(global.addSpill(type, getName()), 0, ScriptObject.GLOBALFILTER), null);
}
- private SetMethod createNewPropertySetter() {
- final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter() : createNewSpillPropertySetter();
+ private SetMethod createNewPropertySetter(final SwitchPoint builtinSwitchPoint) {
+ final SetMethod sm = map.getFreeFieldSlot() > -1 ? createNewFieldSetter(builtinSwitchPoint) : createNewSpillPropertySetter(builtinSwitchPoint);
final PropertyListeners listeners = map.getListeners();
if (listeners != null) {
listeners.propertyAdded(sm.property);
@@ -194,7 +193,9 @@ final class SetMethodCreator {
return sm;
}
- private SetMethod createNewSetter(final Property property) {
+ private SetMethod createNewSetter(final Property property, final SwitchPoint builtinSwitchPoint) {
+ property.setBuiltinSwitchPoint(builtinSwitchPoint);
+
final PropertyMap oldMap = getMap();
final PropertyMap newMap = getNewMap(property);
final boolean isStrict = NashornCallSiteDescriptor.isStrict(desc);
@@ -230,12 +231,12 @@ final class SetMethodCreator {
return new SetMethod(MH.asType(MH.guardWithTest(extCheck, casGuard, nop), fastSetter.type()), property);
}
- private SetMethod createNewFieldSetter() {
- return createNewSetter(new AccessorProperty(getName(), 0, sobj.getClass(), getMap().getFreeFieldSlot(), type));
+ private SetMethod createNewFieldSetter(final SwitchPoint builtinSwitchPoint) {
+ return createNewSetter(new AccessorProperty(getName(), 0, sobj.getClass(), getMap().getFreeFieldSlot(), type), builtinSwitchPoint);
}
- private SetMethod createNewSpillPropertySetter() {
- return createNewSetter(new SpillProperty(getName(), 0, getMap().getFreeSpillSlot(), type));
+ private SetMethod createNewSpillPropertySetter(final SwitchPoint builtinSwitchPoint) {
+ return createNewSetter(new SpillProperty(getName(), 0, getMap().getFreeSpillSlot(), type), builtinSwitchPoint);
}
private PropertyMap getNewMap(final Property property) {
diff --git a/src/jdk/nashorn/internal/runtime/Specialization.java b/src/jdk/nashorn/internal/runtime/Specialization.java
new file mode 100644
index 00000000..9807cc61
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/Specialization.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import java.lang.invoke.MethodHandle;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction;
+import jdk.nashorn.internal.objects.annotations.SpecializedFunction.LinkLogic;
+
+/**
+ * Specialization info for a {@link SpecializedFunction}
+ */
+public final class Specialization {
+ private final MethodHandle mh;
+ private final Class<? extends LinkLogic> linkLogicClass;
+ private final boolean isOptimistic;
+
+ /**
+ * Constructor
+ *
+ * @param mh invoker method handler
+ */
+ public Specialization(final MethodHandle mh) {
+ this(mh, false);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param mh invoker method handler
+ * @param isOptimistic is this an optimistic native method, i.e. can it throw {@link UnwarrantedOptimismException}
+ * which would have to lead to a relink and return value processing
+ */
+ public Specialization(final MethodHandle mh, final boolean isOptimistic) {
+ this(mh, null, isOptimistic);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param mh invoker method handler
+ * @param linkLogicClass extra link logic needed for this function. Instances of this class also contains logic for checking
+ * if this can be linked on its first encounter, which is needed as per our standard linker semantics
+ * @param isOptimistic is this an optimistic native method, i.e. can it throw {@link UnwarrantedOptimismException}
+ * which would have to lead to a relink and return value processing
+ */
+ public Specialization(final MethodHandle mh, final Class<? extends LinkLogic> linkLogicClass, final boolean isOptimistic) {
+ this.mh = mh;
+ this.isOptimistic = isOptimistic;
+ if (linkLogicClass != null) {
+ //null out the "empty" link logic class for optimization purposes
+ //we only use the empty instance because we can't default class annotations
+ //to null
+ this.linkLogicClass = LinkLogic.isEmpty(linkLogicClass) ? null : linkLogicClass;
+ } else {
+ this.linkLogicClass = null;
+ }
+ }
+
+ /**
+ * Get the method handle for the invoker of this ScriptFunction
+ * @return the method handle
+ */
+ public MethodHandle getMethodHandle() {
+ return mh;
+ }
+
+ /**
+ * Get the link logic class for this ScriptFunction
+ * @return link logic class info, i.e. one whose instance contains stuff like
+ * "do we need exception check for every call", and logic to check if we may link
+ */
+ public Class<? extends LinkLogic> getLinkLogicClass() {
+ return linkLogicClass;
+ }
+
+ /**
+ * An optimistic specialization is one that can throw UnwarrantedOptimismException.
+ * This is allowed for native methods, as long as they are functional, i.e. don't change
+ * any state between entering and throwing the UOE. Then we can re-execute a wider version
+ * of the method in the continuation. Rest-of method generation for optimistic builtins is
+ * of course not possible, but this approach works and fits into the same relinking
+ * framework
+ *
+ * @return true if optimistic
+ */
+ public boolean isOptimistic() {
+ return isOptimistic;
+ }
+
+}
+
diff --git a/src/jdk/nashorn/internal/runtime/StoredScript.java b/src/jdk/nashorn/internal/runtime/StoredScript.java
index 5b6a77b2..14a0ced0 100644
--- a/src/jdk/nashorn/internal/runtime/StoredScript.java
+++ b/src/jdk/nashorn/internal/runtime/StoredScript.java
@@ -55,8 +55,10 @@ public final class StoredScript implements Serializable {
/**
* Constructor.
*
+ * @param compilationId compilation id
* @param mainClassName main class name
* @param classBytes map of class names to class bytes
+ * @param initializers initializer map, id -> FunctionInitializer
* @param constants constants array
*/
public StoredScript(final int compilationId, final String mainClassName, final Map<String, byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers, final Object[] constants) {
@@ -67,6 +69,10 @@ public final class StoredScript implements Serializable {
this.initializers = initializers;
}
+ /**
+ * Get the compilation id for this StoredScript
+ * @return compilation id
+ */
public int getCompilationId() {
return compilationId;
}
diff --git a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
index 7350ff43..5fdec009 100644
--- a/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
+++ b/src/jdk/nashorn/internal/runtime/UserAccessorProperty.java
@@ -25,7 +25,6 @@
package jdk.nashorn.internal.runtime;
-import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.JSType.CONVERT_OBJECT_OPTIMISTIC;
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java
index 897636eb..9e606ee6 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayData.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.staticCall;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.nio.ByteBuffer;
@@ -58,7 +57,7 @@ public abstract class ArrayData {
/**
* Length of the array data. Not necessarily length of the wrapped array.
*/
- private long length;
+ protected long length;
/**
* Method handle to throw an {@link UnwarrantedOptimismException} when getting an element
@@ -520,7 +519,7 @@ public abstract class ArrayData {
* @param type new element type
* @return new array data
*/
- protected abstract ArrayData convert(Class<?> type);
+ public abstract ArrayData convert(Class<?> type);
/**
* Push an array of items to the end of the array
@@ -537,7 +536,7 @@ public abstract class ArrayData {
final Class<?> widest = widestType(items);
ArrayData newData = convert(widest);
- long pos = newData.length();
+ long pos = newData.length;
for (final Object item : items) {
newData = newData.ensure(pos); //avoid sparse array
newData.set((int)pos++, item, strict);
@@ -655,7 +654,7 @@ public abstract class ArrayData {
* @param size current size
* @return next size to allocate for internal array
*/
- protected static int nextSize(final int size) {
+ public static int nextSize(final int size) {
return alignUp(size + 1) * 2;
}
@@ -681,6 +680,18 @@ public abstract class ArrayData {
}
}
+ /**
+ * Find a fast call if one exists
+ *
+ * @param clazz array data class
+ * @param desc callsite descriptor
+ * @param request link request
+ * @return fast property getter if one is found
+ */
+ public GuardedInvocation findFastCallMethod(final Class<? extends ArrayData> clazz, final CallSiteDescriptor desc, final LinkRequest request) {
+ return null;
+ }
+
/**
* Find a fast property getter if one exists
*
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java
index be41673b..8d71cc04 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ArrayFilter.java
@@ -39,7 +39,7 @@ abstract class ArrayFilter extends ArrayData {
protected ArrayData underlying;
ArrayFilter(final ArrayData underlying) {
- super(underlying.length());
+ super(underlying.length);
this.underlying = underlying;
}
@@ -70,13 +70,13 @@ abstract class ArrayFilter extends ArrayData {
@Override
public void shiftLeft(final int by) {
underlying.shiftLeft(by);
- setLength(underlying.length());
+ setLength(underlying.length);
}
@Override
public ArrayData shiftRight(final int by) {
underlying = underlying.shiftRight(by);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -84,7 +84,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData ensure(final long safeIndex) {
underlying = underlying.ensure(safeIndex);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -92,7 +92,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData shrink(final long newLength) {
underlying = underlying.shrink(newLength);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -100,7 +100,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData set(final int index, final Object value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -108,7 +108,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -116,7 +116,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -124,7 +124,7 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData set(final int index, final double value, final boolean strict) {
underlying = underlying.set(index, value, strict);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@@ -189,28 +189,28 @@ abstract class ArrayFilter extends ArrayData {
@Override
public ArrayData delete(final int index) {
underlying = underlying.delete(index);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@Override
public ArrayData delete(final long from, final long to) {
underlying = underlying.delete(from, to);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@Override
- protected ArrayData convert(final Class<?> type) {
+ public ArrayData convert(final Class<?> type) {
underlying = underlying.convert(type);
- setLength(underlying.length());
+ setLength(underlying.length);
return this;
}
@Override
public Object pop() {
final Object value = underlying.pop();
- setLength(underlying.length());
+ setLength(underlying.length);
return value;
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
index f6e4d38c..dfa6133d 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java
@@ -30,7 +30,6 @@ 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;
@@ -77,11 +76,11 @@ public abstract class ContinuousArrayData extends ArrayData {
* array without reallocating, or if we are overwriting an already
* allocated element
*
- * @param index
+ * @param index index to check
* @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 has(index) || (index == length && ensure(index) == this);
}
/**
@@ -116,6 +115,12 @@ public abstract class ContinuousArrayData extends ArrayData {
}
/**
+ * Returns the type used to store an element in this array
+ * @return element type
+ */
+ public abstract Class<?> getElementType();
+
+ /**
* 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
@@ -175,11 +180,6 @@ public abstract class ContinuousArrayData extends ArrayData {
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 =
@@ -269,4 +269,72 @@ public abstract class ContinuousArrayData extends ArrayData {
return null;
}
+
+ /**
+ * Specialization - fast push implementation
+ * @param arg argument
+ * @return new array length
+ */
+ public long fastPush(final int arg) {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast push implementation
+ * @param arg argument
+ * @return new array length
+ */
+ public long fastPush(final long arg) {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast push implementation
+ * @param arg argument
+ * @return new array length
+ */
+ public long fastPush(final double arg) {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast push implementation
+ * @param arg argument
+ * @return new array length
+ */
+ public long fastPush(final Object arg) {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast pop implementation
+ * @return element value
+ */
+ public int fastPopInt() {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast pop implementation
+ * @return element value
+ */
+ public long fastPopLong() {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast pop implementation
+ * @return element value
+ */
+ public double fastPopDouble() {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
+
+ /**
+ * Specialization - fast pop implementation
+ * @return element value
+ */
+ public Object fastPopObject() {
+ throw new ClassCastException(String.valueOf(getClass())); //type is wrong, relink
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java
index bf120eac..4f54b639 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/DeletedArrayFilter.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.lang.reflect.Array;
import jdk.nashorn.internal.runtime.BitVector;
@@ -40,7 +39,7 @@ final class DeletedArrayFilter extends ArrayFilter {
DeletedArrayFilter(final ArrayData underlying) {
super(underlying);
- this.deleted = new BitVector(underlying.length());
+ this.deleted = new BitVector(underlying.length);
}
@Override
@@ -80,25 +79,25 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public void shiftLeft(final int by) {
super.shiftLeft(by);
- deleted.shiftLeft(by, length());
+ deleted.shiftLeft(by, length);
}
@Override
public ArrayData shiftRight(final int by) {
super.shiftRight(by);
- deleted.shiftRight(by, length());
+ deleted.shiftRight(by, length);
return this;
}
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) {
+ if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) {
return new SparseArrayData(this, safeIndex + 1);
}
super.ensure(safeIndex);
- deleted.resize(length());
+ deleted.resize(length);
return this;
}
@@ -106,7 +105,7 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public ArrayData shrink(final long newLength) {
super.shrink(newLength);
- deleted.resize(length());
+ deleted.resize(length);
return this;
}
@@ -147,7 +146,7 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public ArrayData delete(final int index) {
final long longIndex = ArrayIndex.toLongIndex(index);
- assert longIndex >= 0 && longIndex < length();
+ assert longIndex >= 0 && longIndex < length;
deleted.set(longIndex);
underlying.setEmpty(index);
return this;
@@ -155,7 +154,7 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public ArrayData delete(final long fromIndex, final long toIndex) {
- assert fromIndex >= 0 && fromIndex <= toIndex && toIndex < length();
+ assert fromIndex >= 0 && fromIndex <= toIndex && toIndex < length;
deleted.setRange(fromIndex, toIndex + 1);
underlying.setEmpty(fromIndex, toIndex);
return this;
@@ -163,7 +162,7 @@ final class DeletedArrayFilter extends ArrayFilter {
@Override
public Object pop() {
- final long index = length() - 1;
+ final long index = length - 1;
if (super.has((int)index)) {
final boolean isDeleted = deleted.isSet(index);
@@ -180,7 +179,7 @@ final class DeletedArrayFilter extends ArrayFilter {
final ArrayData newArray = underlying.slice(from, to);
final DeletedArrayFilter newFilter = new DeletedArrayFilter(newArray);
newFilter.getDeleted().copy(deleted);
- newFilter.getDeleted().shiftLeft(from, newFilter.length());
+ newFilter.getDeleted().shiftLeft(from, newFilter.length);
return newFilter;
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
index b74d0782..8732add9 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java
@@ -45,7 +45,7 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
if(hi < SparseArrayData.MAX_DENSE_LENGTH || underlying instanceof SparseArrayData) {
return underlying;
}
- return new SparseArrayData(underlying, underlying.length());
+ return new SparseArrayData(underlying, underlying.length);
}
private boolean isEmpty() {
@@ -93,7 +93,7 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) {
+ if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) {
return new SparseArrayData(this, safeIndex + 1);
}
@@ -110,8 +110,9 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
@Override
public ArrayData shiftRight(final int by) {
super.shiftRight(by);
- lo = Math.min(length(), lo + by);
- hi = Math.min(length() - 1, hi + by);
+ final long len = length;
+ lo = Math.min(len, lo + by);
+ hi = Math.min(len - 1, hi + by);
return isEmpty() ? getUnderlying() : this;
}
@@ -237,7 +238,7 @@ final class DeletedRangeArrayFilter extends ArrayFilter {
@Override
public Object pop() {
- final int index = (int)(length() - 1);
+ final int index = (int)length - 1;
if (super.has(index)) {
final boolean isDeleted = isDeleted(index);
final Object value = super.pop();
diff --git a/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
index 57d0cd90..48dd088e 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/IntArrayData.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
@@ -38,7 +37,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 ContinuousArrayData {
+final class IntArrayData extends ContinuousArrayData implements IntElements {
/**
* The wrapped array
*/
@@ -64,9 +63,19 @@ final class IntArrayData extends ContinuousArrayData {
this.array = array;
}
+ @Override
+ public Class<?> getElementType() {
+ return int.class;
+ }
+
private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), IntArrayData.class, "getElem", int.class, int.class).methodHandle();
private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), IntArrayData.class, "setElem", void.class, int.class, int.class).methodHandle();
+ @Override
+ public Object[] asObjectArray() {
+ return toObjectArray();
+ }
+
@SuppressWarnings("unused")
private int getElem(final int index) {
if (has(index)) {
@@ -96,23 +105,18 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public ArrayData copy() {
- return new IntArrayData(array.clone(), (int) length());
- }
-
- @Override
- public Object[] asObjectArray() {
- return toObjectArray(array, (int) length());
+ return new IntArrayData(array.clone(), (int)length);
}
@Override
public Object asArrayOfType(final Class<?> componentType) {
if (componentType == int.class) {
- return array.length == length() ? array.clone() : Arrays.copyOf(array, (int) length());
+ return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length);
}
return super.asArrayOfType(componentType);
}
- private static Object[] toObjectArray(final int[] array, final int length) {
+ private Object[] toObjectArray() {
assert length <= array.length : "length exceeds internal array size";
final Object[] oarray = new Object[array.length];
@@ -123,7 +127,7 @@ final class IntArrayData extends ContinuousArrayData {
return oarray;
}
- private static double[] toDoubleArray(final int[] array, final int length) {
+ private double[] toDoubleArray() {
assert length <= array.length : "length exceeds internal array size";
final double[] darray = new double[array.length];
@@ -134,7 +138,7 @@ final class IntArrayData extends ContinuousArrayData {
return darray;
}
- private static long[] toLongArray(final int[] array, final int length) {
+ private long[] toLongArray() {
assert length <= array.length : "length exceeds internal array size";
final long[] larray = new long[array.length];
@@ -145,18 +149,30 @@ final class IntArrayData extends ContinuousArrayData {
return larray;
}
+ private LongArrayData convertToLong() {
+ return new LongArrayData(toLongArray(), (int)length);
+ }
+
+ private NumberArrayData convertToDouble() {
+ return new NumberArrayData(toDoubleArray(), (int)length);
+ }
+
+ private ObjectArrayData convertToObject() {
+ return new ObjectArrayData(toObjectArray(), (int)length);
+ }
+
@Override
public ArrayData convert(final Class<?> type) {
if (type == Integer.class) {
return this;
}
- final int length = (int) length();
if (type == Long.class) {
- return new LongArrayData(IntArrayData.toLongArray(array, length), length);
+ return convertToLong();
} else if (type == Double.class) {
- return new NumberArrayData(IntArrayData.toDoubleArray(array, length), length);
+ return convertToDouble();
} else {
- return new ObjectArrayData(IntArrayData.toObjectArray(array, length), length);
+ assert type == null || (!Number.class.isAssignableFrom(type) && !type.isPrimitive());
+ return convertToObject();
}
}
@@ -167,7 +183,7 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length() - 1);
+ final ArrayData newData = ensure(by + length - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -213,7 +229,7 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@@ -222,7 +238,7 @@ final class IntArrayData extends ContinuousArrayData {
public ArrayData set(final int index, final long value, final boolean strict) {
if (JSType.isRepresentableAsInt(value)) {
array[index] = JSType.toInt32(value);
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@@ -233,7 +249,7 @@ final class IntArrayData extends ContinuousArrayData {
public ArrayData set(final int index, final double value, final boolean strict) {
if (JSType.isRepresentableAsInt(value)) {
array[index] = (int)(long)value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@@ -282,7 +298,7 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public boolean has(final int index) {
- return 0 <= index && index < length();
+ return 0 <= index && index < length;
}
@Override
@@ -297,11 +313,11 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public Object pop() {
- if (length() == 0) {
+ if (length == 0) {
return ScriptRuntime.UNDEFINED;
}
- final int newLength = (int) length() - 1;
+ final int newLength = (int)length - 1;
final int elem = array[newLength];
array[newLength] = 0;
setLength(newLength);
@@ -311,7 +327,7 @@ final class IntArrayData extends ContinuousArrayData {
@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);
@@ -319,18 +335,18 @@ final class IntArrayData extends ContinuousArrayData {
@Override
public final ArrayData push(final boolean strict, final int item) {
- final long length = length();
- final ArrayData newData = ensure(length);
+ final long len = length;
+ final ArrayData newData = ensure(len);
if (newData == this) {
- array[(int)length] = item;
+ array[(int)len] = item;
return this;
}
- return newData.set((int)length, item, strict);
+ return newData.set((int)len, item, strict);
}
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length();
+ final long oldLength = length;
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
@@ -355,4 +371,41 @@ final class IntArrayData extends ContinuousArrayData {
return returnValue;
}
+
+ @Override
+ public long fastPush(final int arg) {
+ final int len = (int)length;
+ if (len == array.length) {
+ array = Arrays.copyOf(array, nextSize(len));
+ }
+ array[len] = arg;
+ return ++length;
+ }
+
+ //length must not be zero
+ @Override
+ public int fastPopInt() {
+ if (length == 0) {
+ throw new ClassCastException(); //relink
+ }
+ final int newLength = (int)--length;
+ final int elem = array[newLength];
+ array[newLength] = 0;
+ return elem;
+ }
+
+ @Override
+ public long fastPopLong() {
+ return fastPopInt();
+ }
+
+ @Override
+ public double fastPopDouble() {
+ return fastPopInt();
+ }
+
+ @Override
+ public Object fastPopObject() {
+ return fastPopInt();
+ }
}
diff --git a/src/jdk/nashorn/internal/objects/annotations/SpecializedConstructor.java b/src/jdk/nashorn/internal/runtime/arrays/IntElements.java
index 9e6bd0ad..5bd26843 100644
--- a/src/jdk/nashorn/internal/objects/annotations/SpecializedConstructor.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/IntElements.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,26 +22,13 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-
-package jdk.nashorn.internal.objects.annotations;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
+package jdk.nashorn.internal.runtime.arrays;
/**
- * The SpecializedConstructor annotation is used to flag more type specific constructors than the standard one in
- * Native objects. For example {@link jdk.nashorn.internal.objects.NativeArray#construct} takes an arbitrary number of
- * Object elements as an array. Call this constructor, including the varargs collector that allocates the array
- * upon each invocation, is much more expensive than calling a specialized constructor that takes one arguments
- * of, e.g. int type from the call site, such as
- * {@link jdk.nashorn.internal.objects.NativeArray#construct(boolean, Object, int)}.
- * {@link jdk.nashorn.internal.runtime.ScriptFunction} will try to look up the most specific function when
- * linking the callsite.
+ * Marker interface for any ContinuousArray with int elements
+ * Used for type checks that throw ClassCastExceptions and force relinks
+ * for fast NativeArray specializations of builtin methods
*/
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.METHOD)
-public @interface SpecializedConstructor {
+public interface IntElements extends IntOrLongElements {
//empty
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/IntOrLongElements.java b/src/jdk/nashorn/internal/runtime/arrays/IntOrLongElements.java
new file mode 100644
index 00000000..f176d757
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/arrays/IntOrLongElements.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.runtime.arrays;
+
+/**
+ * Marker interface for any ContinuousArray with int or long elements
+ * Used for type checks that throw ClassCastExceptions and force relinks
+ * for fast NativeArray specializations of builtin methods
+ */
+public interface IntOrLongElements extends NumericElements {
+ //empty
+}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
index 4fc0c63e..ad050d69 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/LongArrayData.java
@@ -27,7 +27,6 @@ package jdk.nashorn.internal.runtime.arrays;
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.util.Arrays;
@@ -39,7 +38,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 ContinuousArrayData {
+final class LongArrayData extends ContinuousArrayData implements IntOrLongElements {
/**
* The wrapped array
*/
@@ -57,13 +56,18 @@ final class LongArrayData extends ContinuousArrayData {
}
@Override
+ public Class<?> getElementType() {
+ return long.class;
+ }
+
+ @Override
public ArrayData copy() {
- return new LongArrayData(array.clone(), (int)length());
+ return new LongArrayData(array.clone(), (int)length);
}
@Override
public Object[] asObjectArray() {
- return toObjectArray(array, (int)length());
+ return toObjectArray(array, (int)length);
}
private static Object[] toObjectArray(final long[] array, final int length) {
@@ -80,7 +84,7 @@ final class LongArrayData extends ContinuousArrayData {
@Override
public Object asArrayOfType(final Class<?> componentType) {
if (componentType == long.class) {
- return array.length == length() ? array.clone() : Arrays.copyOf(array, (int)length());
+ return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length);
}
return super.asArrayOfType(componentType);
}
@@ -101,11 +105,11 @@ final class LongArrayData extends ContinuousArrayData {
if (type == Integer.class || type == Long.class) {
return this;
}
- final int length = (int) length();
+ final int len = (int)length;
if (type == Double.class) {
- return new NumberArrayData(LongArrayData.toDoubleArray(array, length), length);
+ return new NumberArrayData(LongArrayData.toDoubleArray(array, len), len);
}
- return new ObjectArrayData(LongArrayData.toObjectArray(array, length), length);
+ return new ObjectArrayData(LongArrayData.toObjectArray(array, len), len);
}
@Override
@@ -115,7 +119,7 @@ final class LongArrayData extends ContinuousArrayData {
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length() - 1);
+ final ArrayData newData = ensure(by + length - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -161,14 +165,14 @@ final class LongArrayData extends ContinuousArrayData {
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@@ -176,7 +180,7 @@ final class LongArrayData extends ContinuousArrayData {
public ArrayData set(final int index, final double value, final boolean strict) {
if (JSType.isRepresentableAsLong(value)) {
array[index] = (long)value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
return convert(Double.class).set(index, value, strict);
@@ -252,7 +256,7 @@ final class LongArrayData extends ContinuousArrayData {
@Override
public boolean has(final int index) {
- return 0 <= index && index < length();
+ return 0 <= index && index < length;
}
@Override
@@ -267,11 +271,11 @@ final class LongArrayData extends ContinuousArrayData {
@Override
public Object pop() {
- if (length() == 0) {
+ if (length == 0) {
return ScriptRuntime.UNDEFINED;
}
- final int newLength = (int) (length() - 1);
+ final int newLength = (int)length - 1;
final long elem = array[newLength];
array[newLength] = 0;
setLength(newLength);
@@ -281,25 +285,25 @@ final class LongArrayData extends ContinuousArrayData {
@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);
+ final long len = length;
+ final ArrayData newData = ensure(len);
if (newData == this) {
- array[(int)length] = item;
+ array[(int)len] = item;
return this;
}
- return newData.set((int)length, item, strict);
+ return newData.set((int)len, item, strict);
}
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length();
+ final long oldLength = length;
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
@@ -324,4 +328,40 @@ final class LongArrayData extends ContinuousArrayData {
return returnValue;
}
+
+ @Override
+ public long fastPush(final int arg) {
+ return fastPush((long)arg);
+ }
+
+ @Override
+ public long fastPush(final long arg) {
+ final int len = (int)length;
+ if (len == array.length) {
+ array = Arrays.copyOf(array, nextSize(len));
+ }
+ array[len] = arg;
+ return ++length;
+ }
+
+ @Override
+ public long fastPopLong() {
+ if (length == 0) {
+ throw new ClassCastException();
+ }
+ final int newLength = (int)--length;
+ final long elem = array[newLength];
+ array[newLength] = 0;
+ return elem;
+ }
+
+ @Override
+ public double fastPopDouble() {
+ return fastPopLong();
+ }
+
+ @Override
+ public Object fastPopObject() {
+ return fastPopLong();
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java
index 4e0ce373..143cd221 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/NoTypeArrayData.java
@@ -58,19 +58,19 @@ final class NoTypeArrayData extends ArrayData {
@Override
public ArrayData convert(final Class<?> type) {
- final long length = length();
+ final long len = length;
final ArrayData arrayData;
if (type == Long.class) {
- arrayData = new LongArrayData(new long[ArrayData.nextSize((int)length)], (int)length);
+ arrayData = new LongArrayData(new long[ArrayData.nextSize((int)len)], (int)len);
} else if (type == Double.class) {
- arrayData = new NumberArrayData(new double[ArrayData.nextSize((int)length)], (int)length);
+ arrayData = new NumberArrayData(new double[ArrayData.nextSize((int)len)], (int)len);
} else if (type == Integer.class) {
- arrayData = new IntArrayData(new int[ArrayData.nextSize((int)length)], (int)length);
+ arrayData = new IntArrayData(new int[ArrayData.nextSize((int)len)], (int)len);
} else {
assert !type.isPrimitive();
- arrayData = new ObjectArrayData(new Object[ArrayData.nextSize((int)length)], (int)length);
+ arrayData = new ObjectArrayData(new Object[ArrayData.nextSize((int)len)], (int)len);
}
- return length == 0 ? arrayData : new DeletedRangeArrayFilter(arrayData, 0, length - 1);
+ return length == 0 ? arrayData : new DeletedRangeArrayFilter(arrayData, 0, len - 1);
}
@Override
@@ -90,11 +90,11 @@ final class NoTypeArrayData extends ArrayData {
}
// Don't trample the shared EMPTY_ARRAY.
- if (length() == 0) {
- return new NoTypeArrayData(Math.max(safeIndex + 1, length()));
+ if (length == 0) {
+ return new NoTypeArrayData(Math.max(safeIndex + 1, length));
}
- setLength(Math.max(safeIndex + 1, length()));
+ setLength(Math.max(safeIndex + 1, length));
return this;
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
index 0739ae8e..b2d843e6 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java
@@ -28,7 +28,6 @@ package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
import static jdk.nashorn.internal.lookup.Lookup.MH;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
@@ -38,7 +37,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 ContinuousArrayData {
+final class NumberArrayData extends ContinuousArrayData implements NumericElements {
/**
* The wrapped array
*/
@@ -56,13 +55,18 @@ final class NumberArrayData extends ContinuousArrayData {
}
@Override
+ public Class<?> getElementType() {
+ return double.class;
+ }
+
+ @Override
public ArrayData copy() {
- return new NumberArrayData(array.clone(), (int) length());
+ return new NumberArrayData(array.clone(), (int)length);
}
@Override
public Object[] asObjectArray() {
- return toObjectArray(array, (int) length());
+ return toObjectArray(array, (int)length);
}
private static Object[] toObjectArray(final double[] array, final int length) {
@@ -78,7 +82,7 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public Object asArrayOfType(final Class<?> componentType) {
if(componentType == double.class) {
- return array.length == length() ? array.clone() : Arrays.copyOf(array, (int) length());
+ return array.length == length ? array.clone() : Arrays.copyOf(array, (int)length);
}
return super.asArrayOfType(componentType);
}
@@ -86,8 +90,8 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public ArrayData convert(final Class<?> type) {
if (type != Double.class && type != Integer.class && type != Long.class) {
- final int length = (int) length();
- return new ObjectArrayData(NumberArrayData.toObjectArray(array, length), length);
+ final int len = (int)length;
+ return new ObjectArrayData(NumberArrayData.toObjectArray(array, len), len);
}
return this;
}
@@ -99,7 +103,7 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length() - 1);
+ final ArrayData newData = ensure(by + length - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -144,21 +148,21 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final double value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@@ -227,7 +231,7 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public boolean has(final int index) {
- return 0 <= index && index < length();
+ return 0 <= index && index < length;
}
@Override
@@ -242,11 +246,11 @@ final class NumberArrayData extends ContinuousArrayData {
@Override
public Object pop() {
- if (length() == 0) {
+ if (length == 0) {
return UNDEFINED;
}
- final int newLength = (int) (length() - 1);
+ final int newLength = (int)length - 1;
final double elem = array[newLength];
array[newLength] = 0;
setLength(newLength);
@@ -255,25 +259,25 @@ final class NumberArrayData extends ContinuousArrayData {
@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);
+ final long len = length;
+ final ArrayData newData = ensure(len);
if (newData == this) {
- array[(int)length] = item;
+ array[(int)len] = item;
return this;
}
- return newData.set((int)length, item, strict);
+ return newData.set((int)len, item, strict);
}
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length();
+ final long oldLength = length;
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
@@ -298,4 +302,41 @@ final class NumberArrayData extends ContinuousArrayData {
return returnValue;
}
+
+ @Override
+ public long fastPush(final int arg) {
+ return fastPush((double)arg);
+ }
+
+ @Override
+ public long fastPush(final long arg) {
+ return fastPush((double)arg);
+ }
+
+ @Override
+ public long fastPush(final double arg) {
+ final int len = (int)length;
+ if (len == array.length) {
+ //note that fastpush never creates spares arrays, there is nothing to gain by that - it will just use even more memory
+ array = Arrays.copyOf(array, nextSize(len));
+ }
+ array[len] = arg;
+ return ++length;
+ }
+
+ @Override
+ public double fastPopDouble() {
+ if (length == 0) {
+ throw new ClassCastException();
+ }
+ final int newLength = (int)--length;
+ final double elem = array[newLength];
+ array[newLength] = 0;
+ return elem;
+ }
+
+ @Override
+ public Object fastPopObject() {
+ return fastPopDouble();
+ }
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/NumericElements.java b/src/jdk/nashorn/internal/runtime/arrays/NumericElements.java
new file mode 100644
index 00000000..ad940e2a
--- /dev/null
+++ b/src/jdk/nashorn/internal/runtime/arrays/NumericElements.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.nashorn.internal.runtime.arrays;
+
+/**
+ * Marker interface for any ContinuousArray with numeric elements
+ * (int, long or double)
+ * Used for type checks that throw ClassCastExceptions and force relinks
+ * for fast NativeArray specializations of builtin methods
+ */
+public interface NumericElements {
+ //empty
+}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
index 636c8c25..8fd1a453 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.codegen.CompilerConstants.specialCall;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
@@ -57,20 +56,25 @@ final class ObjectArrayData extends ContinuousArrayData {
}
@Override
+ public Class<?> getElementType() {
+ return Object.class;
+ }
+
+ @Override
public ArrayData copy() {
- return new ObjectArrayData(array.clone(), (int) length());
+ return new ObjectArrayData(array.clone(), (int)length);
}
@Override
public Object[] asObjectArray() {
- return array.length == length() ? array.clone() : asObjectArrayCopy();
+ return array.length == length ? array.clone() : asObjectArrayCopy();
}
private Object[] asObjectArrayCopy() {
- final long l = length();
- assert l <= Integer.MAX_VALUE;
- final Object[] copy = new Object[(int)l];
- System.arraycopy(array, 0, copy, 0, (int)l);
+ final long len = length;
+ assert len <= Integer.MAX_VALUE;
+ final Object[] copy = new Object[(int)len];
+ System.arraycopy(array, 0, copy, 0, (int)len);
return copy;
}
@@ -86,7 +90,7 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public ArrayData shiftRight(final int by) {
- final ArrayData newData = ensure(by + length() - 1);
+ final ArrayData newData = ensure(by + length - 1);
if (newData != this) {
newData.shiftRight(by);
return newData;
@@ -118,28 +122,28 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public ArrayData set(final int index, final Object value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final int value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final long value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@Override
public ArrayData set(final int index, final double value, final boolean strict) {
array[index] = value;
- setLength(Math.max(index + 1, length()));
+ setLength(Math.max(index + 1, length));
return this;
}
@@ -216,7 +220,7 @@ final class ObjectArrayData extends ContinuousArrayData {
@Override
public boolean has(final int index) {
- return 0 <= index && index < length();
+ return 0 <= index && index < length;
}
@Override
@@ -232,12 +236,48 @@ final class ObjectArrayData extends ContinuousArrayData {
}
@Override
+ public long fastPush(final int arg) {
+ return fastPush((Object)arg);
+ }
+
+ @Override
+ public long fastPush(final long arg) {
+ return fastPush((Object)arg);
+ }
+
+ @Override
+ public long fastPush(final double arg) {
+ return fastPush((Object)arg);
+ }
+
+ @Override
+ public long fastPush(final Object arg) {
+ final int len = (int)length;
+ if (len == array.length) {
+ array = Arrays.copyOf(array, nextSize(len));
+ }
+ array[len] = arg;
+ return ++length;
+ }
+
+ @Override
+ public Object fastPopObject() {
+ if (length == 0) {
+ return ScriptRuntime.UNDEFINED;
+ }
+ final int newLength = (int)--length;
+ final Object elem = array[newLength];
+ array[newLength] = ScriptRuntime.EMPTY;
+ return elem;
+ }
+
+ @Override
public Object pop() {
- if (length() == 0) {
+ if (length == 0) {
return ScriptRuntime.UNDEFINED;
}
- final int newLength = (int) (length() - 1);
+ final int newLength = (int)length - 1;
final Object elem = array[newLength];
setEmpty(newLength);
setLength(newLength);
@@ -246,25 +286,25 @@ final class ObjectArrayData extends ContinuousArrayData {
@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);
+ final long len = length;
+ final ArrayData newData = ensure(len);
if (newData == this) {
- array[(int)length] = item;
+ array[(int)len] = item;
return this;
}
- return newData.set((int)length, item, strict);
+ return newData.set((int)len, item, strict);
}
@Override
public ArrayData fastSplice(final int start, final int removed, final int added) throws UnsupportedOperationException {
- final long oldLength = length();
+ final long oldLength = length;
final long newLength = oldLength - removed + added;
if (newLength > SparseArrayData.MAX_DENSE_LENGTH && newLength > array.length) {
throw new UnsupportedOperationException();
diff --git a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
index 623e3159..d28b731c 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java
@@ -53,28 +53,28 @@ class SparseArrayData extends ArrayData {
SparseArrayData(final ArrayData underlying, final long length, final TreeMap<Long, Object> sparseMap) {
super(length);
- assert underlying.length() <= length;
+ assert underlying.length <= length;
this.underlying = underlying;
- this.maxDenseLength = Math.max(MAX_DENSE_LENGTH, underlying.length());
+ this.maxDenseLength = Math.max(MAX_DENSE_LENGTH, underlying.length);
this.sparseMap = sparseMap;
}
@Override
public ArrayData copy() {
- return new SparseArrayData(underlying.copy(), length(), new TreeMap<>(sparseMap));
+ return new SparseArrayData(underlying.copy(), length, new TreeMap<>(sparseMap));
}
@Override
public Object[] asObjectArray() {
- final int length = (int) Math.min(length(), Integer.MAX_VALUE);
- final int underlyingLength = (int) Math.min(length, underlying.length());
- final Object[] objArray = new Object[length];
+ final int len = (int)Math.min(length, Integer.MAX_VALUE);
+ final int underlyingLength = (int)Math.min(len, underlying.length);
+ final Object[] objArray = new Object[len];
for (int i = 0; i < underlyingLength; i++) {
objArray[i] = underlying.getObject(i);
}
- Arrays.fill(objArray, underlyingLength, length, ScriptRuntime.UNDEFINED);
+ Arrays.fill(objArray, underlyingLength, len, ScriptRuntime.UNDEFINED);
for (final Map.Entry<Long, Object> entry : sparseMap.entrySet()) {
final long key = entry.getKey();
@@ -104,14 +104,14 @@ class SparseArrayData extends ArrayData {
}
sparseMap = newSparseMap;
- setLength(Math.max(length() - by, 0));
+ setLength(Math.max(length - by, 0));
}
@Override
public ArrayData shiftRight(final int by) {
final TreeMap<Long, Object> newSparseMap = new TreeMap<>();
- if (underlying.length() + by > maxDenseLength) {
- for (long i = maxDenseLength - by; i < underlying.length(); i++) {
+ if (underlying.length + by > maxDenseLength) {
+ for (long i = maxDenseLength - by; i < underlying.length; i++) {
if (underlying.has((int) i)) {
newSparseMap.put(Long.valueOf(i + by), underlying.getObject((int) i));
}
@@ -127,23 +127,23 @@ class SparseArrayData extends ArrayData {
}
sparseMap = newSparseMap;
- setLength(length() + by);
+ setLength(length + by);
return this;
}
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex < maxDenseLength && underlying.length() <= safeIndex) {
+ if (safeIndex < maxDenseLength && underlying.length <= safeIndex) {
underlying = underlying.ensure(safeIndex);
}
- setLength(Math.max(safeIndex + 1, length()));
+ setLength(Math.max(safeIndex + 1, length));
return this;
}
@Override
public ArrayData shrink(final long newLength) {
- if (newLength < underlying.length()) {
+ if (newLength < underlying.length) {
underlying = underlying.shrink(newLength);
underlying.setLength(newLength);
sparseMap.clear();
@@ -160,11 +160,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length(), length()));
+ setLength(Math.max(underlying.length, length));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length()));
+ setLength(Math.max(longIndex + 1, length));
}
return this;
@@ -175,11 +175,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length(), length()));
+ setLength(Math.max(underlying.length, length));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length()));
+ setLength(Math.max(longIndex + 1, length));
}
return this;
}
@@ -189,11 +189,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length(), length()));
+ setLength(Math.max(underlying.length, length));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length()));
+ setLength(Math.max(longIndex + 1, length));
}
return this;
}
@@ -203,11 +203,11 @@ class SparseArrayData extends ArrayData {
if (index >= 0 && index < maxDenseLength) {
ensure(index);
underlying = underlying.set(index, value, strict);
- setLength(Math.max(underlying.length(), length()));
+ setLength(Math.max(underlying.length, length));
} else {
final Long longIndex = indexToKey(index);
sparseMap.put(longIndex, value);
- setLength(Math.max(longIndex + 1, length()));
+ setLength(Math.max(longIndex + 1, length));
}
return this;
}
@@ -294,7 +294,7 @@ class SparseArrayData extends ArrayData {
@Override
public boolean has(final int index) {
if (index >= 0 && index < maxDenseLength) {
- return index < underlying.length() && underlying.has(index);
+ return index < underlying.length && underlying.has(index);
}
return sparseMap.containsKey(indexToKey(index));
@@ -303,7 +303,7 @@ class SparseArrayData extends ArrayData {
@Override
public ArrayData delete(final int index) {
if (index >= 0 && index < maxDenseLength) {
- if (index < underlying.length()) {
+ if (index < underlying.length) {
underlying = underlying.delete(index);
}
} else {
@@ -315,8 +315,8 @@ class SparseArrayData extends ArrayData {
@Override
public ArrayData delete(final long fromIndex, final long toIndex) {
- if (fromIndex < maxDenseLength && fromIndex < underlying.length()) {
- underlying = underlying.delete(fromIndex, Math.min(toIndex, underlying.length() - 1));
+ if (fromIndex < maxDenseLength && fromIndex < underlying.length) {
+ underlying = underlying.delete(fromIndex, Math.min(toIndex, underlying.length - 1));
}
if (toIndex >= maxDenseLength) {
sparseMap.subMap(fromIndex, true, toIndex, true).clear();
@@ -329,37 +329,37 @@ class SparseArrayData extends ArrayData {
}
@Override
- protected ArrayData convert(final Class<?> type) {
+ public ArrayData convert(final Class<?> type) {
underlying = underlying.convert(type);
return this;
}
@Override
public Object pop() {
- if (length() == 0) {
+ if (length == 0) {
return ScriptRuntime.UNDEFINED;
}
- if (length() == underlying.length()) {
+ if (length == underlying.length) {
final Object result = underlying.pop();
- setLength(underlying.length());
+ setLength(underlying.length);
return result;
}
- setLength(length() - 1);
- final Long key = Long.valueOf(length());
+ setLength(length - 1);
+ final Long key = Long.valueOf(length);
return sparseMap.containsKey(key) ? sparseMap.remove(key) : ScriptRuntime.UNDEFINED;
}
@Override
public ArrayData slice(final long from, final long to) {
- assert to <= length();
- final long start = from < 0 ? (from + length()) : from;
+ assert to <= length;
+ final long start = from < 0 ? (from + length) : from;
final long newLength = to - start;
if (start >= 0 && to <= maxDenseLength) {
- if (newLength <= underlying.length()) {
+ if (newLength <= underlying.length) {
return underlying.slice(from, to);
}
- return underlying.slice(from, to).ensure(newLength - 1).delete(underlying.length(), newLength);
+ return underlying.slice(from, to).ensure(newLength - 1).delete(underlying.length, newLength);
}
ArrayData sliced = EMPTY_ARRAY;
@@ -369,13 +369,13 @@ class SparseArrayData extends ArrayData {
sliced = sliced.set((int)(i - start), getObject((int)i), false);
}
}
- assert sliced.length() == newLength;
+ assert sliced.length == newLength;
return sliced;
}
@Override
public long nextIndex(final long index) {
- if (index < underlying.length() - 1) {
+ if (index < underlying.length - 1) {
return underlying.nextIndex(index);
}
@@ -383,6 +383,6 @@ class SparseArrayData extends ArrayData {
if (nextKey != null) {
return nextKey;
}
- return length();
+ return length;
}
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java b/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java
index 291e59e0..428678d0 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/TypedArrayData.java
@@ -26,7 +26,6 @@
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;
@@ -55,11 +54,11 @@ public abstract class TypedArrayData<T extends Buffer> extends ContinuousArrayDa
}
/**
- * Length in elements. Accessed from {@code ArrayBufferView}
+ * Length in number of elements. Accessed from {@code ArrayBufferView}
* @return element length
*/
public final int getElementLength() {
- return (int)length();
+ return (int)length;
}
/**
@@ -120,7 +119,7 @@ public abstract class TypedArrayData<T extends Buffer> extends ContinuousArrayDa
@Override
public final boolean has(final int index) {
- return 0 <= index && index < length();
+ return 0 <= index && index < length;
}
@Override
@@ -134,7 +133,7 @@ public abstract class TypedArrayData<T extends Buffer> extends ContinuousArrayDa
}
@Override
- protected ArrayData convert(final Class<?> type) {
+ public ArrayData convert(final Class<?> type) {
throw new UnsupportedOperationException();
}
diff --git a/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java b/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java
index e2488d34..f744aacd 100644
--- a/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java
+++ b/src/jdk/nashorn/internal/runtime/arrays/UndefinedArrayFilter.java
@@ -26,7 +26,6 @@
package jdk.nashorn.internal.runtime.arrays;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
-
import java.lang.reflect.Array;
import jdk.nashorn.internal.runtime.BitVector;
import jdk.nashorn.internal.runtime.UnwarrantedOptimismException;
@@ -41,7 +40,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
UndefinedArrayFilter(final ArrayData underlying) {
super(underlying);
- this.undefined = new BitVector(underlying.length());
+ this.undefined = new BitVector(underlying.length);
}
@Override
@@ -81,25 +80,25 @@ final class UndefinedArrayFilter extends ArrayFilter {
@Override
public void shiftLeft(final int by) {
super.shiftLeft(by);
- undefined.shiftLeft(by, length());
+ undefined.shiftLeft(by, length);
}
@Override
public ArrayData shiftRight(final int by) {
super.shiftRight(by);
- undefined.shiftRight(by, length());
+ undefined.shiftRight(by, length);
return this;
}
@Override
public ArrayData ensure(final long safeIndex) {
- if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length()) {
+ if (safeIndex >= SparseArrayData.MAX_DENSE_LENGTH && safeIndex >= length) {
return new SparseArrayData(this, safeIndex + 1);
}
super.ensure(safeIndex);
- undefined.resize(length());
+ undefined.resize(length);
return this;
}
@@ -107,7 +106,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
@Override
public ArrayData shrink(final long newLength) {
super.shrink(newLength);
- undefined.resize(length());
+ undefined.resize(length);
return this;
}
@@ -217,7 +216,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
@Override
public Object pop() {
- final long index = length() - 1;
+ final long index = length - 1;
if (super.has((int)index)) {
final boolean isUndefined = undefined.isSet(index);
@@ -234,7 +233,7 @@ final class UndefinedArrayFilter extends ArrayFilter {
final ArrayData newArray = underlying.slice(from, to);
final UndefinedArrayFilter newFilter = new UndefinedArrayFilter(newArray);
newFilter.getUndefined().copy(undefined);
- newFilter.getUndefined().shiftLeft(from, newFilter.length());
+ newFilter.getUndefined().shiftLeft(from, newFilter.length);
return newFilter;
}
diff --git a/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java b/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java
index f377c9d8..2a536bbf 100644
--- a/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java
+++ b/src/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java
@@ -26,14 +26,16 @@
package jdk.nashorn.internal.runtime.linker;
import static jdk.nashorn.internal.lookup.Lookup.MH;
-
import java.lang.invoke.MethodHandle;
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;
import jdk.internal.dynalink.support.Guards;
+import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.FindProperty;
+import jdk.nashorn.internal.runtime.GlobalConstants;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.UserAccessorProperty;
@@ -86,27 +88,41 @@ public final class PrimitiveLookup {
final MethodHandle protoFilter) {
final CallSiteDescriptor desc = request.getCallSiteDescriptor();
- if(desc.getNameTokenCount() > 2) {
+ //checks whether the property name is hard-coded in the call-site (i.e. a getProp vs a getElem, or setProp vs setElem)
+ //if it is we can make assumptions on the property: that if it is not defined on primitive wrapper itself it never will be.
+ //so in that case we can skip creation of primitive wrapper and start our search with the prototype.
+ if (desc.getNameTokenCount() > 2) {
final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
final FindProperty find = wrappedReceiver.findProperty(name, true);
- if(find == null) {
+
+ if (find == null) {
// Give up early, give chance to BeanLinker and NashornBottomLinker to deal with it.
return null;
- } else if (find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
+ }
+
+ final SwitchPoint sp = find.getProperty().getBuiltinSwitchPoint(); //can use this instead of proto filter
+ if (sp instanceof Context.BuiltinSwitchPoint && !sp.hasBeenInvalidated()) {
+ return new GuardedInvocation(GlobalConstants.staticConstantGetter(find.getObjectValue()), guard, sp, null);
+ }
+
+ if (find.isInherited() && !(find.getProperty() instanceof UserAccessorProperty)) {
// If property is found in the prototype object bind the method handle directly to
// the proto filter instead of going through wrapper instantiation below.
final ScriptObject proto = wrappedReceiver.getProto();
final GuardedInvocation link = proto.lookup(desc, request);
if (link != null) {
- final MethodHandle invocation = link.getInvocation();
+ final MethodHandle invocation = link.getInvocation(); //this contains the builtin switchpoint
+
final MethodHandle adaptedInvocation = MH.asType(invocation, invocation.type().changeParameterType(0, Object.class));
final MethodHandle method = MH.filterArguments(adaptedInvocation, 0, protoFilter);
final MethodHandle protoGuard = MH.filterArguments(link.getGuard(), 0, protoFilter);
+
return new GuardedInvocation(method, NashornGuards.combineGuards(guard, protoGuard));
}
}
}
+
final GuardedInvocation link = wrappedReceiver.lookup(desc, request);
if (link != null) {
MethodHandle method = link.getInvocation();
@@ -116,8 +132,10 @@ public final class PrimitiveLookup {
assert receiverType.isAssignableFrom(wrapType.returnType());
method = MH.filterArguments(method, 0, MH.asType(wrapFilter, wrapType.changeReturnType(receiverType)));
}
+
return new GuardedInvocation(method, guard, link.getSwitchPoints(), null);
}
+
return null;
}
}
diff --git a/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java b/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java
index 08168b63..77b1b3dc 100644
--- a/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java
+++ b/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java
@@ -25,6 +25,9 @@
package jdk.nashorn.internal.runtime.regexp;
+import java.util.Collections;
+import java.util.Set;
+import java.util.WeakHashMap;
import jdk.nashorn.internal.runtime.ParserException;
import jdk.nashorn.internal.runtime.options.Options;
@@ -39,6 +42,15 @@ public class RegExpFactory {
private final static String JDK = "jdk";
private final static String JONI = "joni";
+ /** Weak cache of already validated regexps - when reparsing, we don't, for example
+ * need to recompile (reverify) all regexps that have previously been parsed by this
+ * RegExpFactory in a previous compilation. This saves significant time in e.g. avatar
+ * startup */
+ private static final Set<String> VALID_CACHE_SET =
+ Collections.newSetFromMap(
+ Collections.synchronizedMap(
+ new WeakHashMap<String, Boolean>()));
+
static {
final String impl = Options.getStringProperty("nashorn.regexp.impl", JONI);
switch (impl) {
@@ -88,7 +100,9 @@ public class RegExpFactory {
*/
// @SuppressWarnings({"unused"})
public static void validate(final String pattern, final String flags) throws ParserException {
- instance.compile(pattern, flags);
+ if (VALID_CACHE_SET.add(pattern + flags)) {
+ instance.compile(pattern, flags);
+ }
}
/**
diff --git a/src/jdk/nashorn/internal/runtime/resources/Options.properties b/src/jdk/nashorn/internal/runtime/resources/Options.properties
index a5e8cd5a..57943e01 100644
--- a/src/jdk/nashorn/internal/runtime/resources/Options.properties
+++ b/src/jdk/nashorn/internal/runtime/resources/Options.properties
@@ -203,9 +203,9 @@ nashorn.option.lazy.compilation = {
nashorn.option.optimistic.types = { \
name="--optimistic-types", \
- is_undocumented=true, \
- desc="Use optimistic type assumptions with deoptimizing recompilation.", \
- default=true \
+ short_name="-ot", \
+ desc="Use optimistic type assumptions with deoptimizing recompilation. This makes the compiler try, for any program symbol whose type cannot be proven at compile time, to type it as narrow and primitive as possible. If the runtime encounters an error because symbol type is too narrow, a wider method will be generated until steady stage is reached. While this produces as optimal Java Bytecode as possible, erroneous type guesses will lead to longer warmup. Optimistic typing is currently disabled by default, but can be enabled for significantly better peak performance.", \
+ default=false \
}
nashorn.option.loader.per.compile = { \
diff --git a/test/examples/charcodeat-benchmark.js b/test/examples/charcodeat-benchmark.js
new file mode 100644
index 00000000..308e2a07
--- /dev/null
+++ b/test/examples/charcodeat-benchmark.js
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Simple benchmark to measure charCodeAt specialized method performance
+ */
+
+var str = "sghjkdsfkjghsdfjkfkjdfkjdfjkdfjkfdjkfdkfldjfhdfpkjdhafgksdjfgldfgjldfkjgdlfjgldkfjgkldfj";
+var RESULT1 = 9187;
+var RESULT2 = 1496;
+
+function f() {
+ var len = str.length;
+ var c = 0;
+ for (var i = 0; i < len; i++) {
+ c += str.charCodeAt(i);
+ }
+ return c;
+}
+
+function bench(res) {
+ var d = new Date;
+ var sum = 0;
+ for (var i = 0; i < 1e6; i++) {
+ sum |= f();
+ }
+ if (sum == res) {
+ print("Verified OK");
+ } else {
+ print("Verification failed " + sum + " should be " + res);
+ }
+ print((new Date - d) + " ms");
+}
+
+bench(RESULT1);
+
+print("Replacing charCodeAt... ");
+
+String.prototype.charCodeAt = function() { return 17; }
+
+bench(RESULT2);
+
+print("Done");
diff --git a/test/examples/push-pop-benchmark.js b/test/examples/push-pop-benchmark.js
new file mode 100644
index 00000000..16f4c940
--- /dev/null
+++ b/test/examples/push-pop-benchmark.js
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Simple benchmark to measure push/pop specialized method performance
+ */
+
+var a = [];
+
+var RESULT = 15;
+
+function bench() {
+ var sum = 0;
+ for (var i=0;i<10;i++) {
+ a.push(i);
+ }
+ for (var i=0;i<10;i++) {
+ sum |= a.pop();
+ }
+ return sum;
+}
+
+function runbench() {
+ var sum = 0;
+ for (var iters = 0; iters<1e8; iters++) {
+ sum |= bench();
+ }
+ return sum;
+}
+
+var d = new Date;
+var res = runbench();
+print((new Date - d) + " ms");
+print();
+if (res != RESULT) {
+ print("ERROR: Wrong result - should be " + RESULT);
+} else {
+ print("Verified OK - result is correct");
+}
diff --git a/test/script/basic/apply_to_call/apply_to_call5.js b/test/script/basic/apply_to_call/apply_to_call5.js
new file mode 100644
index 00000000..adb9401e
--- /dev/null
+++ b/test/script/basic/apply_to_call/apply_to_call5.js
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * apply_to_call5.js - do one apply to call specialization, then override, apply and make sure it reverts (i.e. stops
+ * calling call)
+ *
+ * @test
+ * @run
+ */
+
+print("start");
+
+var x = {
+ a : 0,
+ b : 0,
+ c : 0,
+ initialize : function(x,y,z) {
+ this.a = x;
+ this.b = y;
+ this.c = z;
+ }
+};
+
+function test() {
+ x.initialize.apply(x, arguments);
+}
+
+test(4711,23,17);
+print(x.a);
+print(x.b);
+print(x.c);
+
+print("Overwriting apply now");
+x.initialize.apply = function() { print("New function for apply - not a property"); }
+
+test(4712);
+print(x.a);
+
+
+var x2 = {
+ a : 0,
+ b : 0,
+ c : 0,
+ initialize : function(x,y,z) {
+ this.a = x;
+ this.b = y;
+ this.c = z;
+ }
+};
+
+function test2() {
+ x2.initialize.apply(x2, arguments);
+}
+
+test2(4711,23,17);
+print(x2.a);
+print(x2.b);
+print(x2.c);
+
+print("Overwriting apply now");
+x2.initialize['apply'] = function() { print("New function for apply - not a property"); }
+
+test(4712);
+print(x2.a);
+
+var x3 = {
+ a : 0,
+ b : 0,
+ c : 0,
+ initialize : function(x,y,z) {
+ this.a = x;
+ this.b = y;
+ this.c = z;
+ }
+};
+
+function test3() {
+ x3.initialize.apply(x3, arguments);
+}
+
+test3(4711,23,17);
+print(x3.a);
+print(x3.b);
+print(x3.c);
+
+print("Overwriting apply now");
+eval("x3.initialize['apply'] = function() { print('New function for apply - not a property'); }");
+
+test(4712);
+print(x3.a);
diff --git a/test/script/basic/apply_to_call/apply_to_call5.js.EXPECTED b/test/script/basic/apply_to_call/apply_to_call5.js.EXPECTED
new file mode 100644
index 00000000..4d678e90
--- /dev/null
+++ b/test/script/basic/apply_to_call/apply_to_call5.js.EXPECTED
@@ -0,0 +1,19 @@
+start
+4711
+23
+17
+Overwriting apply now
+New function for apply - not a property
+4711
+4711
+23
+17
+Overwriting apply now
+New function for apply - not a property
+4711
+4711
+23
+17
+Overwriting apply now
+New function for apply - not a property
+4711
diff --git a/test/script/basic/fastpushpop.js b/test/script/basic/fastpushpop.js
new file mode 100644
index 00000000..fffd3f23
--- /dev/null
+++ b/test/script/basic/fastpushpop.js
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * fastpushpop.js: make sure guards work for fast push implementation
+ * and normal one
+ *
+ * @test
+ * @run
+ */
+
+var a = [1,2,3];
+a.push(4);
+a.push(5);
+a.push(6);
+print(a);
+
+var a2 = Object.defineProperty(a,"length", { writable: false });
+try {
+ a2.push(7);
+} catch (e) {
+ print("first: " + (e instanceof TypeError));
+}
+
+print(a2);
+
+var b = [1,2,3,,,,4711.17,"dingo!"];
+b.push(4);
+b.push(5);
+b.push(6);
+print(b);
+
+var b2 = Object.defineProperty(b,"length", { writable: false });
+try {
+ b2.push(7);
+} catch (e) {
+ print("second: " + (e instanceof TypeError));
+}
+
+print(b2);
+
diff --git a/test/script/basic/fastpushpop.js.EXPECTED b/test/script/basic/fastpushpop.js.EXPECTED
new file mode 100644
index 00000000..0120e1d9
--- /dev/null
+++ b/test/script/basic/fastpushpop.js.EXPECTED
@@ -0,0 +1,6 @@
+1,2,3,4,5,6
+first: true
+1,2,3,4,5,6,7
+1,2,3,,,,4711.17,dingo!,4,5,6
+second: true
+1,2,3,,,,4711.17,dingo!,4,5,6,7
diff --git a/test/script/basic/octane-payload.js b/test/script/basic/octane-payload.js
index 36c780d7..f2be6b12 100644
--- a/test/script/basic/octane-payload.js
+++ b/test/script/basic/octane-payload.js
@@ -40,7 +40,7 @@ var tests = [
{name:"gbemu", files:["gbemu-part1.js", "gbemu-part2.js"], suite:"GameboyBenchmark"},
{name:"mandreel", files:["mandreel.js"], suite:"MandreelBenchmark"},
{name:"navier-stokes", files:["navier-stokes.js"], suite:"NavierStokes"},
- {name:"pdfjs", files:["pdfjs.js"], suite:"PdfJS"},
+ {name:"pdfjs", files:["pdfjs.js"], suite:"PdfJS", cleanUpIteration: function() { canvas_logs = []; }},
{name:"raytrace", files:["raytrace.js"], suite:"RayTrace"},
{name:"regexp", files:["regexp.js"], suite:"RegExpSuite"},
{name:"richards", files:["richards.js"], suite:"Richards"},
diff --git a/test/script/basic/run-octane.js b/test/script/basic/run-octane.js
index 0c666f2f..a0a9bdc8 100644
--- a/test/script/basic/run-octane.js
+++ b/test/script/basic/run-octane.js
@@ -24,8 +24,8 @@
/**
* @subtest
*/
-var payload = __DIR__ + "octane-payload.js";
-load(payload);
+var dir = typeof(__DIR__) == 'undefined' ? "test/script/basic/" : __DIR__;
+load(dir + "octane-payload.js");
var runtime = undefined;
var verbose = false;
@@ -43,27 +43,27 @@ function should_compile_only(name) {
function load_bench(arg) {
for (var idx = 0; idx < arg.files.length; idx++) {
- var f = arg.files[idx];
- var file = f.split('/');
- var file_name = path + file[file.length - 1];
-
- var compile_and_return = should_compile_only(file_name);
- if (compile_and_return) {
- if (typeof compile_only === 'undefined') { //for a run, skip compile onlies, don't even compile them
- return true;
+ var f = arg.files[idx];
+ var file = f.split('/');
+ var file_name = path + file[file.length - 1];
+
+ var compile_and_return = should_compile_only(file_name);
+ if (compile_and_return) {
+ if (typeof compile_only === 'undefined') { //for a run, skip compile onlies, don't even compile them
+ return true;
+ }
}
- }
- print_verbose(arg, "loading '" + arg.name + "' [" + f + "]...");
- load(file_name);
+ print_verbose(arg, "loading '" + arg.name + "' [" + f + "]... " + file_name);
+ load(file_name);
}
if (typeof arg.before !== 'undefined') {
- arg.before();
+ arg.before();
}
if (compile_and_return) {
- print_always(arg, "Compiled OK");
+ print_always(arg, "Compiled OK");
}
return !compile_and_return;
@@ -73,16 +73,16 @@ function load_bench(arg) {
function run_one_benchmark(arg, iters) {
if (!load_bench(arg)) {
- return;
+ return;
}
var success = true;
var current_name;
if (iters == undefined) {
- iters = numberOfIterations;
+ iters = numberOfIterations;
} else {
- numberOfIterations = iters;
+ numberOfIterations = iters;
}
var benchmarks = eval(arg.suite + ".benchmarks");
@@ -91,64 +91,69 @@ function run_one_benchmark(arg, iters) {
var mean_score = 0;
try {
- for (var x = 0; x < benchmarks.length ; x++) {
- //do warmup run
- //reset random number generator needed as of octane 9 before each run
+ for (var x = 0; x < benchmarks.length ; x++) {
+ //do warmup run
+ //reset random number generator needed as of octane 9 before each run
+ BenchmarkSuite.ResetRNG();
+ benchmarks[x].Setup();
+ }
BenchmarkSuite.ResetRNG();
- benchmarks[x].Setup();
- }
- BenchmarkSuite.ResetRNG();
- print_verbose(arg, "running '" + arg.name + "' for " + iters + " iterations of no less than " + min_time + " seconds");
-
- var scores = [];
-
- var min_time_ms = min_time * 1000;
- var len = benchmarks.length;
-
- for (var it = 0; it < iters + 1; it++) {
- //every iteration must take a minimum of 10 secs
- var ops = 0;
- var elapsed = 0;
- var start = new Date;
- do {
- for (var i = 0; i < len; i++) {
- benchmarks[i].run();
- //important - no timing here like elapsed = new Date() - start, as in the
- //original harness. This will make timing very non-deterministic.
- //NOTHING else must live in this loop
+ print_verbose(arg, "running '" + arg.name + "' for " + iters + " iterations of no less than " + min_time + " seconds");
+
+ var scores = [];
+
+ var min_time_ms = min_time * 1000;
+ var len = benchmarks.length;
+
+ for (var it = 0; it < iters + 1; it++) {
+ //every iteration must take a minimum of 10 secs
+ var ops = 0;
+ var elapsed = 0;
+ var start = new Date;
+ do {
+ for (var i = 0; i < len; i++) {
+ benchmarks[i].run();
+ //important - no timing here like elapsed = new Date() - start, as in the
+ //original harness. This will make timing very non-deterministic.
+ //NOTHING else must live in this loop
+ }
+ ops += len;
+ elapsed = new Date - start;
+ } while (elapsed < min_time * 1000);
+
+ var score = ops / elapsed * 1000 * 60;
+ scores.push(score);
+ var name = it == 0 ? "warmup" : "iteration " + it;
+ print_verbose(arg, name + " finished " + score.toFixed(0) + " ops/minute");
+
+ // optional per-iteration cleanup hook
+ if (typeof arg.cleanUpIteration == "function") {
+ arg.cleanUpIteration();
+ }
}
- ops += len;
- elapsed = new Date - start;
- } while (elapsed < min_time * 1000);
-
- var score = ops / elapsed * 1000 * 60;
- scores.push(score);
- var name = it == 0 ? "warmup" : "iteration " + it;
- print_verbose(arg, name + " finished " + score.toFixed(0) + " ops/minute");
- }
- for (var x = 0; x < benchmarks.length ; x++) {
- benchmarks[x].TearDown();
- }
+ for (var x = 0; x < benchmarks.length ; x++) {
+ benchmarks[x].TearDown();
+ }
- for (var x = 1; x < iters + 1 ; x++) {
- mean_score += scores[x];
- min_score = Math.min(min_score, scores[x]);
- max_score = Math.max(max_score, scores[x]);
- }
- mean_score /= iters;
+ for (var x = 1; x < iters + 1 ; x++) {
+ mean_score += scores[x];
+ min_score = Math.min(min_score, scores[x]);
+ max_score = Math.max(max_score, scores[x]);
+ }
+ mean_score /= iters;
} catch (e) {
- print_always(arg, "*** Aborted and setting score to zero. Reason: " + e);
- if (e instanceof java.lang.Throwable) {
- e.printStackTrace();
- }
- mean_score = min_score = max_score = 0;
- scores = [0];
+ print_always(arg, "*** Aborted and setting score to zero. Reason: " + e);
+ if (is_this_nashorn() && e instanceof java.lang.Throwable) {
+ e.printStackTrace();
+ }
+ mean_score = min_score = max_score = 0;
+ scores = [0];
}
var res = mean_score.toFixed(0);
if (verbose) {
- res += " ops/minute (" + min_score.toFixed(0) + "-" + max_score.toFixed(0) + "), warmup=" + scores[0].toFixed(0);
+ res += " ops/minute (" + min_score.toFixed(0) + "-" + max_score.toFixed(0) + "), warmup=" + scores[0].toFixed(0);
}
print_always(arg, res);
}
@@ -163,13 +168,13 @@ function print_always(arg, x) {
function print_verbose(arg, x) {
if (verbose) {
- print_always(arg, x)
+ print_always(arg, x)
}
}
function run_suite(tests, iters) {
for (var idx = 0; idx < tests.length; idx++) {
- run_one_benchmark(tests[idx], iters);
+ run_one_benchmark(tests[idx], iters);
}
}
@@ -184,13 +189,13 @@ if (typeof $ARGS !== 'undefined') {
var new_args = [];
for (i in args) {
if (args[i].toString().indexOf(' ') != -1) {
- args[i] = args[i].replace(/\/$/, '');
- var s = args[i].split(' ');
- for (j in s) {
- new_args.push(s[j]);
- }
+ args[i] = args[i].replace(/\/$/, '');
+ var s = args[i].split(' ');
+ for (j in s) {
+ new_args.push(s[j]);
+ }
} else {
- new_args.push(args[i]);
+ new_args.push(args[i]);
}
}
@@ -205,46 +210,46 @@ var min_time = 5;
for (var i = 0; i < args.length; i++) {
arg = args[i];
if (arg == "--iterations") {
- iters = +args[++i];
- if (isNaN(iters)) {
- throw "'--iterations' must be followed by integer";
- }
+ iters = +args[++i];
+ if (isNaN(iters)) {
+ throw "'--iterations' must be followed by integer";
+ }
} else if (arg == "--runtime") {
- runtime = args[++i];
+ runtime = args[++i];
} else if (arg == "--verbose") {
- verbose = true;
+ verbose = true;
} else if (arg == "--min-time") {
- min_time = +args[++i];
- if (isNaN(iters)) {
- throw "'--min-time' must be followed by integer";
- }
+ min_time = +args[++i];
+ if (isNaN(iters)) {
+ throw "'--min-time' must be followed by integer";
+ }
} else if (arg == "") {
- continue; //skip
+ continue; //skip
} else {
- var found = false;
- for (j in tests) {
- if (tests[j].name === arg) {
- tests_found.push(tests[j]);
- found = true;
- break;
- }
- }
- if (!found) {
- var str = "unknown test name: '" + arg + "' -- valid names are: ";
+ var found = false;
for (j in tests) {
- if (j != 0) {
- str += ", ";
+ if (tests[j].name === arg) {
+ tests_found.push(tests[j]);
+ found = true;
+ break;
+ }
}
- str += "'" + tests[j].name + "'";
+ if (!found) {
+ var str = "unknown test name: '" + arg + "' -- valid names are: ";
+ for (j in tests) {
+ if (j != 0) {
+ str += ", ";
+ }
+ str += "'" + tests[j].name + "'";
+ }
+ throw str;
}
- throw str;
- }
}
}
if (tests_found.length == 0) {
for (i in tests) {
- tests_found.push(tests[i]);
+ tests_found.push(tests[i]);
}
}
@@ -255,21 +260,21 @@ function is_this_nashorn() {
if (is_this_nashorn()) {
try {
- read = readFully;
+ read = readFully;
} catch (e) {
- print("ABORTING: Cannot find 'readFully'. You must have scripting enabled to use this test harness. (-scripting)");
- throw e;
+ print("ABORTING: Cannot find 'readFully'. You must have scripting enabled to use this test harness. (-scripting)");
+ throw e;
}
}
// run tests in alphabetical order by name
tests_found.sort(function(a, b) {
if (a.name < b.name) {
- return -1;
+ return -1;
} else if (a.name > b.name) {
- return 1;
+ return 1;
} else {
- return 0;
+ return 0;
}
});
diff --git a/test/src/jdk/nashorn/internal/runtime/ClassFilterTest.java b/test/src/jdk/nashorn/internal/runtime/ClassFilterTest.java
index 558833eb..7471731d 100644
--- a/test/src/jdk/nashorn/internal/runtime/ClassFilterTest.java
+++ b/test/src/jdk/nashorn/internal/runtime/ClassFilterTest.java
@@ -25,10 +25,10 @@
package jdk.nashorn.internal.runtime;
-
import jdk.nashorn.api.scripting.ClassFilter;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import jdk.nashorn.api.scripting.URLReader;
+import jdk.nashorn.internal.test.framework.TestFinder;
import org.testng.annotations.Test;
import javax.script.ScriptEngine;
@@ -126,9 +126,9 @@ public class ClassFilterTest {
private void persistentCacheTestImpl() {
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine engine = factory.getScriptEngine(
- new String[]{"--persistent-code-cache"},
- getClass().getClassLoader(),
- getClassFilter()
+ TestFinder.addExplicitOptimisticTypes(new String[]{"--persistent-code-cache", "--optimistic-types=true"}),
+ getClass().getClassLoader(),
+ getClassFilter()
);
String testScript = "var a = Java.type('java.lang.String');" + generateCodeForPersistentStore();
try {
@@ -137,7 +137,7 @@ public class ClassFilterTest {
fail(exc.getMessage());
}
ScriptEngine engineSafe = factory.getScriptEngine(
- new String[]{"--persistent-code-cache"},
+ TestFinder.addExplicitOptimisticTypes(new String[]{"--persistent-code-cache", "--optimistic-types=true"}),
getClass().getClassLoader(),
new ClassFilter() {
@Override
@@ -151,7 +151,7 @@ public class ClassFilterTest {
fail("ClassNotFoundException should have been thrown");
} catch (final Exception exc) {
if (!(exc.getCause() instanceof ClassNotFoundException)) {
- fail("ClassNotFoundException expected");
+ fail("ClassNotFoundException expected, got " + exc.getClass());
}
}
}
diff --git a/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java b/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java
index f8aef77c..ee26061e 100644
--- a/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java
+++ b/test/src/jdk/nashorn/internal/test/framework/ScriptRunnable.java
@@ -179,7 +179,7 @@ public final class ScriptRunnable extends AbstractScriptRunnable implements ITes
for (final String str : forkJVMOptions) {
if(!str.isEmpty()) {
cmd.add(str);
- }
+ }
}
cmd.add(Shell.class.getName());
// now add the rest of the "in process" runtime arguments
diff --git a/test/src/jdk/nashorn/internal/test/framework/TestFinder.java b/test/src/jdk/nashorn/internal/test/framework/TestFinder.java
index 4a96a4ae..018f4c7c 100644
--- a/test/src/jdk/nashorn/internal/test/framework/TestFinder.java
+++ b/test/src/jdk/nashorn/internal/test/framework/TestFinder.java
@@ -56,6 +56,7 @@ import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
@@ -75,7 +76,7 @@ import org.xml.sax.InputSource;
* Utility class to find/parse script test files and to create 'test' instances.
* Actual 'test' object type is decided by clients of this class.
*/
-final class TestFinder {
+public final class TestFinder {
private TestFinder() {}
interface TestFactory<T> {
@@ -215,6 +216,8 @@ final class TestFinder {
final List<String> scriptArguments = new ArrayList<>();
boolean inComment = false;
+ boolean explicitOptimistic = false;
+
try (Scanner scanner = new Scanner(testFile)) {
while (scanner.hasNext()) {
// TODO: Scan for /ref=file qualifiers, etc, to determine run
@@ -287,7 +290,11 @@ final class TestFinder {
scriptArguments.add(scanner.next());
break;
case "@option":
- engineOptions.add(scanner.next());
+ final String next = scanner.next();
+ engineOptions.add(next);
+ if (next.startsWith("--optimistic-types")) {
+ explicitOptimistic = true;
+ }
break;
case "@fork":
fork = true;
@@ -336,12 +343,61 @@ final class TestFinder {
testOptions.put(OPTIONS_FORK, "true");
}
+ //if there are explicit optimistic type settings, use those - do not override
+ //the test might only work with optimistic types on or off.
+ if (!explicitOptimistic) {
+ addExplicitOptimisticTypes(engineOptions);
+ }
+
tests.add(factory.createTest(framework, testFile.toFile(), engineOptions, testOptions, scriptArguments));
} else if (!isNotTest) {
orphans.add(name);
}
}
+ //the reverse of the default setting for optimistic types, if enabled, false, otherwise true
+ //thus, true for 8u40, false for 9
+ private static final boolean OPTIMISTIC_OVERRIDE = true;
+
+ /**
+ * Check if there is an optimistic override, that disables the default
+ * false optimistic types and sets them to true, for testing purposes
+ *
+ * @return true if optimistic type override has been set by test suite
+ */
+ public static boolean hasOptimisticOverride() {
+ return Boolean.valueOf(OPTIMISTIC_OVERRIDE).toString().equals(System.getProperty("optimistic.override"));
+ }
+
+ /**
+ * Add an optimistic-types=true option to an argument list if this
+ * is set to override the default false. Add an optimistic-types=true
+ * options to an argument list if this is set to override the default
+ * true
+ *
+ * @args new argument list array
+ */
+ public static String[] addExplicitOptimisticTypes(String[] args) {
+ if (hasOptimisticOverride()) {
+ final List<String> newList = new ArrayList<>(Arrays.asList(args));
+ newList.add("--optimistic-types=" + Boolean.valueOf(OPTIMISTIC_OVERRIDE));
+ return newList.toArray(new String[0]);
+ }
+ return args;
+ }
+
+ /**
+ * Add an optimistic-types=true option to an argument list if this
+ * is set to override the default false
+ *
+ * @args argument list
+ */
+ public static void addExplicitOptimisticTypes(List<String> args) {
+ if (hasOptimisticOverride()) {
+ args.add("--optimistic-types=" + Boolean.valueOf(OPTIMISTIC_OVERRIDE));
+ }
+ }
+
private static boolean strictModeEnabled() {
return Boolean.getBoolean(TEST_JS_ENABLE_STRICT_MODE);
}