From f2913f0270a10cb7b98ef2b78223281af6550e8c Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan Date: Fri, 10 Oct 2014 17:59:22 +0530 Subject: [PATCH 01/11] 8060101: AssertionError: __noSuchProperty__ placeholder called from NativeJavaImporter Reviewed-by: attila, jlaskey --- .../internal/objects/NativeJavaImporter.java | 24 ++++----- .../nashorn/internal/runtime/WithObject.java | 13 +++++ .../runtime/resources/Messages.properties | 1 + nashorn/test/script/basic/JDK-8060101.js | 54 +++++++++++++++++++ 4 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 nashorn/test/script/basic/JDK-8060101.js diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJavaImporter.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJavaImporter.java index d1aa8cb1653..498c263cba6 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJavaImporter.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJavaImporter.java @@ -25,6 +25,7 @@ package jdk.nashorn.internal.objects; +import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.isValid; import jdk.internal.dynalink.CallSiteDescriptor; @@ -36,9 +37,11 @@ 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.runtime.Context; +import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.NativeJavaPackage; import jdk.nashorn.internal.runtime.PropertyMap; import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.ScriptRuntime; import jdk.nashorn.internal.runtime.UnwarrantedOptimismException; /** @@ -94,33 +97,30 @@ public final class NativeJavaImporter extends ScriptObject { } /** - * "No such property" call placeholder. - * - * This can never be called as we override {@link ScriptObject#noSuchProperty}. We do declare it here as it's a signal - * to {@link jdk.nashorn.internal.runtime.WithObject} that it's worth trying doing a {@code noSuchProperty} on this object. + * "No such property" handler. * * @param self self reference * @param name property name - * @return never returns + * @return value of the missing property */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object __noSuchProperty__(final Object self, final Object name) { - throw new AssertionError("__noSuchProperty__ placeholder called"); + if (! (self instanceof NativeJavaImporter)) { + throw typeError("not.a.java.importer", ScriptRuntime.safeToString(self)); + } + return ((NativeJavaImporter)self).createProperty(JSType.toString(name)); } /** - * "No such method call" placeholder - * - * This can never be called as we override {@link ScriptObject#noSuchMethod}. We do declare it here as it's a signal - * to {@link jdk.nashorn.internal.runtime.WithObject} that it's worth trying doing a noSuchProperty on this object. + * "No such method call" handler * * @param self self reference * @param args arguments to method - * @return never returns + * @return never returns always throw TypeError */ @Function(attributes = Attribute.NOT_ENUMERABLE) public static Object __noSuchMethod__(final Object self, final Object... args) { - throw new AssertionError("__noSuchMethod__ placeholder called"); + throw typeError("not.a.function", ScriptRuntime.safeToString(args[0])); } @Override diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/WithObject.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/WithObject.java index 54ec06f5319..20510dfd36b 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/WithObject.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/WithObject.java @@ -209,6 +209,19 @@ public final class WithObject extends ScriptObject implements Scope { return super.findProperty(key, deep, start); } + @Override + protected Object invokeNoSuchProperty(final String name, final int programPoint) { + FindProperty find = expression.findProperty(NO_SUCH_PROPERTY_NAME, true); + if (find != null) { + final Object func = find.getObjectValue(); + if (func instanceof ScriptFunction) { + return ScriptRuntime.apply((ScriptFunction)func, expression, name); + } + } + + return getProto().invokeNoSuchProperty(name, programPoint); + } + @Override public void setSplitState(final int state) { getNonWithParent().setSplitState(state); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties index 056dc87deed..3a161c8d352 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties @@ -73,6 +73,7 @@ type.error.strict.getter.setter.poison=In strict mode, "caller", "callee", and " type.error.not.an.object={0} is not an Object type.error.not.a.boolean={0} is not a Boolean type.error.not.a.date={0} is not a Date +type.error.not.a.java.importer={0} is not a JavaImporter object type.error.not.a.number={0} is not a Number type.error.not.a.regexp={0} is not a RegExp type.error.not.a.string={0} is not a String diff --git a/nashorn/test/script/basic/JDK-8060101.js b/nashorn/test/script/basic/JDK-8060101.js new file mode 100644 index 00000000000..617bbccde72 --- /dev/null +++ b/nashorn/test/script/basic/JDK-8060101.js @@ -0,0 +1,54 @@ +/* + * Copyright (c) 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. + */ + +/** + * JDK-8060101: AssertionError: __noSuchProperty__ placeholder called from NativeJavaImporter + * + * @test + * @run + */ + +var constant = 0.50; +var ind = 0.0; + +// make sure callsites are exercised quite a few times +// to induce megamorphic callsite for with/JavaImporter +// combo - which triggered that AssertionError. +for (var i = 0; i < 50; i++) { + var math = new JavaImporter(java.lang.StrictMath); + ind += 10.0; + with (math) { + StrictMath.exp(-constant*ind); + } +} + +for (var i = 0; i < 50; i++) { + var math = new JavaImporter(java.lang.StrictMath); + try { + math.Foo(); + } catch (e) { + if (! (e instanceof TypeError)) { + throw e; + } + } +} From 4fcc6c8f4f0c42b0c07ec323c87fe88da6b40fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hannes=20Walln=C3=B6fer?= Date: Mon, 13 Oct 2014 17:16:32 +0200 Subject: [PATCH 02/11] 8060011: Concatenating an array and converting it to Java gives wrong result Reviewed-by: lagergren, attila --- .../arrays/DeletedRangeArrayFilter.java | 12 ++-- .../internal/runtime/arrays/IntArrayData.java | 8 +-- .../runtime/arrays/LongArrayData.java | 12 ++-- .../runtime/arrays/NumberArrayData.java | 8 +-- .../runtime/arrays/ObjectArrayData.java | 3 +- .../runtime/arrays/SparseArrayData.java | 2 +- nashorn/test/script/basic/JDK-8060011.js | 58 +++++++++++++++++++ 7 files changed, 81 insertions(+), 22 deletions(-) create mode 100644 nashorn/test/script/basic/JDK-8060011.js diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java index 8732add9348..cd5cadb962e 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/DeletedRangeArrayFilter.java @@ -66,9 +66,9 @@ final class DeletedRangeArrayFilter extends ArrayFilter { public Object[] asObjectArray() { final Object[] value = super.asObjectArray(); - if (lo <= Integer.MAX_VALUE) { - final int intHi = (int)Math.min(hi, Integer.MAX_VALUE); - for (int i = (int)lo; i <= intHi; i++) { + if (lo < Integer.MAX_VALUE) { + final int end = (int)Math.min(hi + 1, Integer.MAX_VALUE); + for (int i = (int)lo; i < end; i++) { value[i] = ScriptRuntime.UNDEFINED; } } @@ -81,9 +81,9 @@ final class DeletedRangeArrayFilter extends ArrayFilter { final Object value = super.asArrayOfType(componentType); final Object undefValue = convertUndefinedValue(componentType); - if (lo <= Integer.MAX_VALUE) { - final int intHi = (int)Math.min(hi, Integer.MAX_VALUE); - for (int i = (int)lo; i <= intHi; i++) { + if (lo < Integer.MAX_VALUE) { + final int end = (int)Math.min(hi + 1, Integer.MAX_VALUE); + for (int i = (int)lo; i < end; i++) { Array.set(value, i, undefValue); } } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IntArrayData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IntArrayData.java index 48dd088e01c..fece3a6ef64 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IntArrayData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IntArrayData.java @@ -73,7 +73,7 @@ final class IntArrayData extends ContinuousArrayData implements IntElements { @Override public Object[] asObjectArray() { - return toObjectArray(); + return toObjectArray(true); } @SuppressWarnings("unused") @@ -116,9 +116,9 @@ final class IntArrayData extends ContinuousArrayData implements IntElements { return super.asArrayOfType(componentType); } - private Object[] toObjectArray() { + private Object[] toObjectArray(final boolean trim) { assert length <= array.length : "length exceeds internal array size"; - final Object[] oarray = new Object[array.length]; + final Object[] oarray = new Object[trim ? (int)length : array.length]; for (int index = 0; index < length; index++) { oarray[index] = Integer.valueOf(array[index]); @@ -158,7 +158,7 @@ final class IntArrayData extends ContinuousArrayData implements IntElements { } private ObjectArrayData convertToObject() { - return new ObjectArrayData(toObjectArray(), (int)length); + return new ObjectArrayData(toObjectArray(false), (int)length); } @Override diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/LongArrayData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/LongArrayData.java index ad050d69170..31a4484e2ae 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/LongArrayData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/LongArrayData.java @@ -67,12 +67,12 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen @Override public Object[] asObjectArray() { - return toObjectArray(array, (int)length); + return toObjectArray(true); } - private static Object[] toObjectArray(final long[] array, final int length) { + private Object[] toObjectArray(final boolean trim) { assert length <= array.length : "length exceeds internal array size"; - final Object[] oarray = new Object[array.length]; + final Object[] oarray = new Object[trim ? (int)length : array.length]; for (int index = 0; index < length; index++) { oarray[index] = Long.valueOf(array[index]); @@ -89,7 +89,7 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen return super.asArrayOfType(componentType); } - private static double[] toDoubleArray(final long[] array, final int length) { + private double[] toDoubleArray() { assert length <= array.length : "length exceeds internal array size"; final double[] darray = new double[array.length]; @@ -107,9 +107,9 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen } final int len = (int)length; if (type == Double.class) { - return new NumberArrayData(LongArrayData.toDoubleArray(array, len), len); + return new NumberArrayData(toDoubleArray(), len); } - return new ObjectArrayData(LongArrayData.toObjectArray(array, len), len); + return new ObjectArrayData(toObjectArray(false), len); } @Override diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java index b2d843e6fb4..4d27f51df3c 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java @@ -66,12 +66,12 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen @Override public Object[] asObjectArray() { - return toObjectArray(array, (int)length); + return toObjectArray(true); } - private static Object[] toObjectArray(final double[] array, final int length) { + private Object[] toObjectArray(final boolean trim) { assert length <= array.length : "length exceeds internal array size"; - final Object[] oarray = new Object[array.length]; + final Object[] oarray = new Object[trim ? (int)length : array.length]; for (int index = 0; index < length; index++) { oarray[index] = Double.valueOf(array[index]); @@ -91,7 +91,7 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen public ArrayData convert(final Class type) { if (type != Double.class && type != Integer.class && type != Long.class) { final int len = (int)length; - return new ObjectArrayData(NumberArrayData.toObjectArray(array, len), len); + return new ObjectArrayData(toObjectArray(false), len); } return this; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java index 8fd1a453077..001ea61c7c8 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java @@ -155,7 +155,8 @@ final class ObjectArrayData extends ContinuousArrayData { @Override public ArrayData setEmpty(final long lo, final long hi) { - Arrays.fill(array, (int)Math.max(lo, 0L), (int)Math.min(hi, Integer.MAX_VALUE), ScriptRuntime.EMPTY); + // hi parameter is inclusive, but Arrays.fill toIndex parameter is exclusive + Arrays.fill(array, (int)Math.max(lo, 0L), (int)Math.min(hi + 1, Integer.MAX_VALUE), ScriptRuntime.EMPTY); return this; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java index d28b731c44c..ef47345173f 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/SparseArrayData.java @@ -78,7 +78,7 @@ class SparseArrayData extends ArrayData { for (final Map.Entry entry : sparseMap.entrySet()) { final long key = entry.getKey(); - if (key <= Integer.MAX_VALUE) { + if (key < Integer.MAX_VALUE) { objArray[(int)key] = entry.getValue(); } else { break; // ascending key order diff --git a/nashorn/test/script/basic/JDK-8060011.js b/nashorn/test/script/basic/JDK-8060011.js new file mode 100644 index 00000000000..7514ff290b6 --- /dev/null +++ b/nashorn/test/script/basic/JDK-8060011.js @@ -0,0 +1,58 @@ +/* + * Copyright (c) 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. + */ + +/** + * JDK-8060011: Concatenating an array and converting it to Java gives wrong result + * + * @test + * @run + */ + + +function compareAsJavaArrays(a1, a2) { + var ja1 = Java.to(a1); + var ja2 = Java.to(a2); + if (ja1.length !== ja2.length) { + throw "different length"; + } + for (var i = 0; i < ja1.length; i++) { + if (ja1[i] !== ja2[i]) { + throw "different element at " + i; + } + } + if (java.util.Arrays.toString(ja1) !== java.util.Arrays.toString(ja2)) { + throw "different string representation"; + } +} + +compareAsJavaArrays([0, 1, 2, 3], + [0].concat([1, 2, 3])); +compareAsJavaArrays([1000000000, 2000000000, 3000000000, 4000000000], + [1000000000].concat([2000000000, 3000000000, 4000000000])); +compareAsJavaArrays([0.5, 1.5, 2.5, 3.5], + [0.5].concat([1.5, 2.5, 3.5])); +compareAsJavaArrays(["0", "1", "2", "3"], + ["0"].concat(["1", "2", "3"])); + + + From 2d6eed916f15524814cd0db806ce35a36654b543 Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan Date: Mon, 13 Oct 2014 23:38:49 +0530 Subject: [PATCH 03/11] 8050977: Java8 Javascript Nashorn exception: no current Global instance for nashorn Reviewed-by: attila, lagergren, hannesw --- .../api/scripting/NashornScriptEngine.java | 28 +----- .../nashorn/api/scripting/ScriptUtils.java | 20 ++-- .../internal/codegen/MethodEmitter.java | 10 +- .../nashorn/internal/objects/NativeJava.java | 6 +- .../linker/JavaAdapterBytecodeGenerator.java | 33 ++++++- .../linker/JavaAdapterClassLoader.java | 3 +- .../runtime/linker/JavaAdapterServices.java | 3 +- .../runtime/linker/NashornBeansLinker.java | 91 ++++++++++++++++--- .../runtime/linker/NashornLinker.java | 2 +- .../runtime/resources/mozilla_compat.js | 4 +- nashorn/test/script/basic/convert.js | 2 +- nashorn/test/script/nosecurity/JDK-8044798.js | 2 + .../scripting/ScriptEngineSecurityTest.java | 36 -------- .../api/scripting/ScriptEngineTest.java | 35 +++++++ 14 files changed, 177 insertions(+), 98 deletions(-) diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/NashornScriptEngine.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/NashornScriptEngine.java index bbf1eecfb49..2f000954815 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/NashornScriptEngine.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/NashornScriptEngine.java @@ -229,6 +229,8 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C } private T getInterfaceInner(final Object thiz, final Class clazz) { + assert !(thiz instanceof ScriptObject) : "raw ScriptObject not expected here"; + if (clazz == null || !clazz.isInterface()) { throw new IllegalArgumentException(getMessage("interface.class.expected")); } @@ -251,17 +253,6 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C final ScriptObjectMirror mirror = (ScriptObjectMirror)thiz; realSelf = mirror.getScriptObject(); realGlobal = mirror.getHomeGlobal(); - if (! isOfContext(realGlobal, nashornContext)) { - throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); - } - } else if (thiz instanceof ScriptObject) { - // called from script code. - realSelf = (ScriptObject)thiz; - realGlobal = Context.getGlobal(); - if (realGlobal == null) { - throw new IllegalArgumentException(getMessage("no.current.nashorn.global")); - } - if (! isOfContext(realGlobal, nashornContext)) { throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); } @@ -368,6 +359,7 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C private Object invokeImpl(final Object selfObject, final String name, final Object... args) throws ScriptException, NoSuchMethodException { name.getClass(); // null check + assert !(selfObject instanceof ScriptObject) : "raw ScriptObject not expected here"; Global invokeGlobal = null; ScriptObjectMirror selfMirror = null; @@ -377,20 +369,6 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); } invokeGlobal = selfMirror.getHomeGlobal(); - } else if (selfObject instanceof ScriptObject) { - // invokeMethod called from script code - in which case we may get 'naked' ScriptObject - // Wrap it with oldGlobal to make a ScriptObjectMirror for the same. - final Global oldGlobal = Context.getGlobal(); - invokeGlobal = oldGlobal; - if (oldGlobal == null) { - throw new IllegalArgumentException(getMessage("no.current.nashorn.global")); - } - - if (! isOfContext(oldGlobal, nashornContext)) { - throw new IllegalArgumentException(getMessage("script.object.from.another.engine")); - } - - selfMirror = (ScriptObjectMirror)ScriptObjectMirror.wrap(selfObject, oldGlobal); } else if (selfObject == null) { // selfObject is null => global function call final Global ctxtGlobal = getNashornGlobalFrom(context); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java index 4de2cbf5516..b6c4c97e547 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java @@ -75,11 +75,8 @@ public final class ScriptUtils { * @param sync the object to synchronize on * @return a synchronizing wrapper function */ - public static Object makeSynchronizedFunction(final Object func, final Object sync) { - if (func instanceof ScriptFunction) { - return ((ScriptFunction)func).makeSynchronizedFunction(sync); - } - throw typeError("not.a.function", ScriptRuntime.safeToString(func)); + public static Object makeSynchronizedFunction(final ScriptFunction func, final Object sync) { + return func.makeSynchronizedFunction(unwrap(sync)); } /** @@ -88,12 +85,8 @@ public final class ScriptUtils { * @param obj object to be wrapped * @return wrapped object */ - public static Object wrap(final Object obj) { - if (obj instanceof ScriptObject) { - return ScriptObjectMirror.wrap(obj, Context.getGlobal()); - } - - return obj; + public static ScriptObjectMirror wrap(final ScriptObject obj) { + return (ScriptObjectMirror) ScriptObjectMirror.wrap(obj, Context.getGlobal()); } /** @@ -160,14 +153,15 @@ public final class ScriptUtils { } final LinkerServices linker = Bootstrap.getLinkerServices(); - final MethodHandle converter = linker.getTypeConverter(obj.getClass(), clazz); + final Object objToConvert = unwrap(obj); + final MethodHandle converter = linker.getTypeConverter(objToConvert.getClass(), clazz); if (converter == null) { // no supported conversion! throw new UnsupportedOperationException("conversion not supported"); } try { - return converter.invoke(obj); + return converter.invoke(objToConvert); } catch (final RuntimeException | Error e) { throw e; } catch (final Throwable t) { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java index 20914472ce6..2df1ae90b88 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/MethodEmitter.java @@ -98,6 +98,7 @@ import jdk.nashorn.internal.ir.LocalVariableConversion; import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.Symbol; import jdk.nashorn.internal.ir.TryNode; +import jdk.nashorn.internal.objects.NativeArray; import jdk.nashorn.internal.runtime.ArgumentSetter; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.Debug; @@ -2125,7 +2126,14 @@ public class MethodEmitter implements Emitter { int pos = 0; for (int i = argCount - 1; i >= 0; i--) { - paramTypes[i] = stack.peek(pos++); + Type pt = stack.peek(pos++); + // "erase" specific ScriptObject subtype info - except for NativeArray. + // NativeArray is used for array/List/Deque conversion for Java calls. + if (ScriptObject.class.isAssignableFrom(pt.getTypeClass()) && + !NativeArray.class.isAssignableFrom(pt.getTypeClass())) { + pt = Type.SCRIPT_OBJECT; + } + paramTypes[i] = pt; } final String descriptor = Type.getMethodDescriptor(returnType, paramTypes); for (int i = 0; i < argCount; i++) { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java index 4395e081e70..7c6e60a967d 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java @@ -90,7 +90,11 @@ public final class NativeJava { */ @Function(name="synchronized", attributes = Attribute.NOT_ENUMERABLE, where = Where.CONSTRUCTOR) public static Object synchronizedFunc(final Object self, final Object func, final Object obj) { - return ScriptUtils.makeSynchronizedFunction(func, obj); + if (func instanceof ScriptFunction) { + return ((ScriptFunction)func).makeSynchronizedFunction(obj); + } + + throw typeError("not.a.function", ScriptRuntime.safeToString(func)); } /** diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java index 44ea184656d..7986b8b4c7c 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java @@ -152,6 +152,7 @@ final class JavaAdapterBytecodeGenerator { static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE); static final String VOID_NOARG_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE); + private static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class); private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class); private static final Type STRING_TYPE = Type.getType(String.class); private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class); @@ -536,8 +537,8 @@ final class JavaAdapterBytecodeGenerator { final int argLen = originalArgTypes.length; final Type[] newArgTypes = new Type[argLen + 1]; - // Insert ScriptFunction|Object as the last argument to the constructor - final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : OBJECT_TYPE; + // Insert ScriptFunction|ScriptObject as the last argument to the constructor + final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : SCRIPT_OBJECT_TYPE; newArgTypes[argLen] = extraArgumentType; System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen); @@ -588,6 +589,34 @@ final class JavaAdapterBytecodeGenerator { // Initialize converters generateConverterInit(mv, fromFunction); endInitMethod(mv); + + if (! fromFunction) { + newArgTypes[argLen] = OBJECT_TYPE; + final InstructionAdapter mv2 = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT, + Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null)); + generateOverridingConstructorWithObjectParam(mv2, ctor, originalCtorType.getDescriptor()); + } + } + + // Object additional param accepting constructor - generated to handle null and undefined value + // for script adapters. This is effectively to throw TypeError on such script adapters. See + // JavaAdapterServices.getHandle as well. + private void generateOverridingConstructorWithObjectParam(final InstructionAdapter mv, final Constructor ctor, final String ctorDescriptor) { + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + final Class[] argTypes = ctor.getParameterTypes(); + int offset = 1; // First arg is at position 1, after this. + for (int i = 0; i < argTypes.length; ++i) { + final Type argType = Type.getType(argTypes[i]); + mv.load(offset, argType); + offset += argType.getSize(); + } + mv.invokespecial(superClassName, INIT, ctorDescriptor, false); + mv.visitVarInsn(ALOAD, offset); + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ACONST_NULL); + mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR, false); + endInitMethod(mv); } private static void endInitMethod(final InstructionAdapter mv) { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java index f5ba8b12c6d..77004978eb7 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterClassLoader.java @@ -39,6 +39,7 @@ import jdk.nashorn.internal.codegen.DumpBytecode; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptFunction; +import jdk.nashorn.internal.runtime.ScriptObject; /** * This class encapsulates the bytecode of the adapter class and can be used to load it into the JVM as an actual Class. @@ -51,7 +52,7 @@ final class JavaAdapterClassLoader { private static final AccessControlContext CREATE_LOADER_ACC_CTXT = ClassAndLoader.createPermAccCtxt("createClassLoader"); private static final AccessControlContext GET_CONTEXT_ACC_CTXT = ClassAndLoader.createPermAccCtxt(Context.NASHORN_GET_CONTEXT); private static final Collection VISIBLE_INTERNAL_CLASS_NAMES = Collections.unmodifiableCollection(new HashSet<>( - Arrays.asList(JavaAdapterServices.class.getName(), ScriptFunction.class.getName(), JSType.class.getName()))); + Arrays.asList(JavaAdapterServices.class.getName(), ScriptObject.class.getName(), ScriptFunction.class.getName(), JSType.class.getName()))); private final String className; private final byte[] classBytes; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java index ef91f35c097..08e4682181d 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterServices.java @@ -47,7 +47,6 @@ import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.Opcodes; import jdk.internal.org.objectweb.asm.Type; import jdk.internal.org.objectweb.asm.commons.InstructionAdapter; -import jdk.nashorn.api.scripting.ScriptUtils; import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; @@ -220,7 +219,7 @@ public final class JavaAdapterServices { * @return the filtered return value. */ public static Object exportReturnValue(final Object obj) { - return ScriptUtils.wrap(NashornBeansLinker.exportArgument(obj)); + return NashornBeansLinker.exportArgument(obj, true); } /** diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java index f802e039607..25ba619b7ad 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornBeansLinker.java @@ -35,17 +35,28 @@ import jdk.internal.dynalink.linker.GuardingDynamicLinker; import jdk.internal.dynalink.linker.LinkRequest; import jdk.internal.dynalink.linker.LinkerServices; import jdk.internal.dynalink.support.Lookup; +import jdk.nashorn.api.scripting.ScriptUtils; +import jdk.nashorn.internal.objects.NativeArray; import jdk.nashorn.internal.runtime.ConsString; +import jdk.nashorn.internal.runtime.ScriptObject; +import jdk.nashorn.internal.runtime.options.Options; /** * This linker delegates to a {@code BeansLinker} but passes it a special linker services object that has a modified * {@code asType} method that will ensure that we never pass internal engine objects that should not be externally - * observable (currently only ConsString) to Java APIs, but rather that we flatten it into a String. We can't just add + * observable (currently ConsString and ScriptObject) to Java APIs, but rather that we flatten it into a String. We can't just add * this functionality as custom converters via {@code GuaardingTypeConverterFactory}, since they are not consulted when * the target method handle parameter signature is {@code Object}. */ public class NashornBeansLinker implements GuardingDynamicLinker { + // System property to control whether to wrap ScriptObject->ScriptObjectMirror for + // Object type arguments of Java method calls, field set and array set. + private static final boolean MIRROR_ALWAYS = Options.getBooleanProperty("nashorn.mirror.always", true); + private static final MethodHandle EXPORT_ARGUMENT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportArgument", Object.class, Object.class); + private static final MethodHandle EXPORT_NATIVE_ARRAY = new Lookup(MethodHandles.lookup()).findOwnStatic("exportNativeArray", Object.class, NativeArray.class); + private static final MethodHandle EXPORT_SCRIPT_OBJECT = new Lookup(MethodHandles.lookup()).findOwnStatic("exportScriptObject", Object.class, ScriptObject.class); + private static final MethodHandle IMPORT_RESULT = new Lookup(MethodHandles.lookup()).findOwnStatic("importResult", Object.class, Object.class); private final BeansLinker beansLinker = new BeansLinker(); @@ -67,8 +78,39 @@ public class NashornBeansLinker implements GuardingDynamicLinker { return delegateLinker.getGuardedInvocation(linkRequest, new NashornBeansLinkerServices(linkerServices)); } - static Object exportArgument(final Object arg) { - return arg instanceof ConsString ? arg.toString() : arg; + @SuppressWarnings("unused") + private static Object exportArgument(final Object arg) { + return exportArgument(arg, MIRROR_ALWAYS); + } + + @SuppressWarnings("unused") + private static Object exportNativeArray(final NativeArray arg) { + return exportArgument(arg, MIRROR_ALWAYS); + } + + @SuppressWarnings("unused") + private static Object exportScriptObject(final ScriptObject arg) { + return exportArgument(arg, MIRROR_ALWAYS); + } + + @SuppressWarnings("unused") + private static Object exportScriptArray(final NativeArray arg) { + return exportArgument(arg, MIRROR_ALWAYS); + } + + static Object exportArgument(final Object arg, final boolean mirrorAlways) { + if (arg instanceof ConsString) { + return arg.toString(); + } else if (mirrorAlways && arg instanceof ScriptObject) { + return ScriptUtils.wrap((ScriptObject)arg); + } else { + return arg; + } + } + + @SuppressWarnings("unused") + private static Object importResult(final Object arg) { + return ScriptUtils.unwrap(arg); } private static class NashornBeansLinkerServices implements LinkerServices { @@ -80,23 +122,50 @@ public class NashornBeansLinker implements GuardingDynamicLinker { @Override public MethodHandle asType(final MethodHandle handle, final MethodType fromType) { - final MethodHandle typed = linkerServices.asType(handle, fromType); - final MethodType handleType = handle.type(); final int paramCount = handleType.parameterCount(); assert fromType.parameterCount() == handleType.parameterCount(); + MethodType newFromType = fromType; MethodHandle[] filters = null; for(int i = 0; i < paramCount; ++i) { - if(shouldConvert(handleType.parameterType(i), fromType.parameterType(i))) { - if(filters == null) { + final MethodHandle filter = argConversionFilter(handleType.parameterType(i), fromType.parameterType(i)); + if (filter != null) { + if (filters == null) { filters = new MethodHandle[paramCount]; } - filters[i] = EXPORT_ARGUMENT; + // "erase" specific type with Object type or else we'll get filter mismatch + newFromType = newFromType.changeParameterType(i, Object.class); + filters[i] = filter; } } - return filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed; + final MethodHandle typed = linkerServices.asType(handle, newFromType); + MethodHandle result = filters != null ? MethodHandles.filterArguments(typed, 0, filters) : typed; + // Filter Object typed return value for possible ScriptObjectMirror. We convert + // ScriptObjectMirror as ScriptObject (if it is mirror from current global). + if (MIRROR_ALWAYS && areBothObjects(handleType.returnType(), fromType.returnType())) { + result = MethodHandles.filterReturnValue(result, IMPORT_RESULT); + } + + return result; + } + + private static MethodHandle argConversionFilter(final Class handleType, final Class fromType) { + if (handleType == Object.class) { + if (fromType == Object.class) { + return EXPORT_ARGUMENT; + } else if (fromType == NativeArray.class) { + return EXPORT_NATIVE_ARRAY; + } else if (fromType == ScriptObject.class) { + return EXPORT_SCRIPT_OBJECT; + } + } + return null; + } + + private static boolean areBothObjects(final Class handleType, final Class fromType) { + return handleType == Object.class && fromType == Object.class; } @Override @@ -104,10 +173,6 @@ public class NashornBeansLinker implements GuardingDynamicLinker { return Implementation.asTypeLosslessReturn(this, handle, fromType); } - private static boolean shouldConvert(final Class handleType, final Class fromType) { - return handleType == Object.class && fromType == Object.class; - } - @Override public MethodHandle getTypeConverter(final Class sourceType, final Class targetType) { return linkerServices.getTypeConverter(sourceType, targetType); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornLinker.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornLinker.java index bde01567aa1..75af367f08d 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornLinker.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/NashornLinker.java @@ -292,7 +292,7 @@ final class NashornLinker implements TypeBasedGuardingDynamicLinker, GuardingTyp @SuppressWarnings("unused") private static Object createMirror(final Object obj) { - return ScriptUtils.wrap(obj); + return obj instanceof ScriptObject? ScriptUtils.wrap((ScriptObject)obj) : obj; } private static MethodHandle findOwnMH(final String name, final Class rtype, final Class... types) { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/mozilla_compat.js b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/mozilla_compat.js index 85e2161c004..6c27e2a6fbc 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/mozilla_compat.js +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/mozilla_compat.js @@ -105,7 +105,7 @@ Object.defineProperty(this, "sync", { if (arguments.length < 1 || arguments.length > 2 ) { throw "sync(function [,object]) parameter count mismatch"; } - return Packages.jdk.nashorn.api.scripting.ScriptUtils.makeSynchronizedFunction(func, syncobj); + return Java.synchronized(func, syncobj); } }); @@ -160,7 +160,7 @@ Object.defineProperty(Object.prototype, "toSource", { configurable: true, enumerable: false, writable: true, value: function(state) { if (! state) { - state = java.util.Collections.newSetFromMap(new java.util.IdentityHashMap()); + state = java.util.Collections.newSetFromMap(new java.util.HashMap()); } if (state.contains(this)) { return "{}"; diff --git a/nashorn/test/script/basic/convert.js b/nashorn/test/script/basic/convert.js index 3a1bca08771..2c87661c74a 100644 --- a/nashorn/test/script/basic/convert.js +++ b/nashorn/test/script/basic/convert.js @@ -42,7 +42,7 @@ print(list); // object to Map obj = { foo: 333, bar: 'hello'}; -var map = ScriptUtils.convert(obj, java.util.Map.class); +var map = ScriptUtils.wrap(obj); print(map instanceof java.util.Map); for (m in map) { print(m + " " + map[m]); diff --git a/nashorn/test/script/nosecurity/JDK-8044798.js b/nashorn/test/script/nosecurity/JDK-8044798.js index c3b6d4cb945..c24edf2181b 100644 --- a/nashorn/test/script/nosecurity/JDK-8044798.js +++ b/nashorn/test/script/nosecurity/JDK-8044798.js @@ -25,6 +25,8 @@ * JDK-8044798: API for debugging Nashorn * * @test + * @option -Dnashorn.mirror.always=false + * @fork * @run */ diff --git a/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java index 09199a52703..226832e7d82 100644 --- a/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java +++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineSecurityTest.java @@ -168,42 +168,6 @@ public class ScriptEngineSecurityTest { } } - @Test - /** - * Check that script can't implement sensitive package interfaces. - */ - public void checkSensitiveInterfaceImplTest() throws ScriptException { - if (System.getSecurityManager() == null) { - // pass vacuously - return; - } - - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - final Object[] holder = new Object[1]; - e.put("holder", holder); - // put an empty script object into array - e.eval("holder[0] = {}"); - // holder[0] is an object of some subclass of ScriptObject - final Class ScriptObjectClass = holder[0].getClass().getSuperclass(); - final Class PropertyAccessClass = ScriptObjectClass.getInterfaces()[0]; - // implementation methods for PropertyAccess class - e.eval("function set() {}; function get() {}; function getInt(){} " + - "function getDouble(){}; function getLong() {}; " + - "this.delete = function () {}; function has() {}; " + - "function hasOwnProperty() {}"); - - // get implementation of a restricted package interface - try { - log(Objects.toString(((Invocable)e).getInterface((Class)PropertyAccessClass))); - fail("should have thrown SecurityException"); - } catch (final Exception exp) { - if (! (exp instanceof SecurityException)) { - fail("SecurityException expected, got " + exp); - } - } - } - // @bug 8032948: Nashorn linkages awry public static class FakeProxy extends Proxy { public FakeProxy(final InvocationHandler ih) { diff --git a/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java index 291e8d424c4..124b5a92a80 100644 --- a/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java +++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java @@ -38,6 +38,7 @@ import java.lang.reflect.Proxy; import java.util.concurrent.Callable; import javax.script.Compilable; import javax.script.CompiledScript; +import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; @@ -629,6 +630,40 @@ public class ScriptEngineTest { assertEquals(enumerable, Boolean.FALSE); } + public static class Context { + private Object myobj; + + public void set(Object o) { + myobj = o; + } + + public Object get() { + return myobj; + } + } + + // @bug 8050977: Java8 Javascript Nashorn exception: + // no current Global instance for nashorn + @Test + public void currentGlobalMissingTest() throws Exception { + final ScriptEngineManager manager = new ScriptEngineManager(); + final ScriptEngine e = manager.getEngineByName("nashorn"); + + final Context ctx = new Context(); + e.put("ctx", ctx); + e.eval("var obj = { foo: function(str) { return str.toUpperCase() } }"); + e.eval("ctx.set(obj)"); + final Invocable inv = (Invocable)e; + assertEquals("HELLO", inv.invokeMethod(ctx.get(), "foo", "hello")); + // try object literal + e.eval("ctx.set({ bar: function(str) { return str.toLowerCase() } })"); + assertEquals("hello", inv.invokeMethod(ctx.get(), "bar", "HELLO")); + // try array literal + e.eval("var arr = [ 'hello', 'world' ]"); + e.eval("ctx.set(arr)"); + assertEquals("helloworld", inv.invokeMethod(ctx.get(), "join", "")); + } + private static void checkProperty(final ScriptEngine e, final String name) throws ScriptException { final String value = System.getProperty(name); From c03284412182d24d955ff53b1e54f8a33ef63e29 Mon Sep 17 00:00:00 2001 From: Attila Szegedi Date: Mon, 13 Oct 2014 20:10:14 +0200 Subject: [PATCH 04/11] 8059842: Creating symbols for declared functions shouldn't be a special case Reviewed-by: hannesw, lagergren --- .../internal/codegen/AssignSymbols.java | 34 ++++++++++++------- .../jdk/nashorn/internal/ir/VarNode.java | 8 +++++ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java index fa2b8a198bd..f83dc9ccdd1 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java @@ -511,16 +511,6 @@ final class AssignSymbols extends NodeVisitor implements Loggabl thisProperties.push(new HashSet()); - if (functionNode.isDeclared()) { - // Can't use lc.getCurrentBlock() as we can have an outermost function in our lexical context that - // is not a program - it is a function being compiled on-demand. - final Iterator blocks = lc.getBlocks(); - if (blocks.hasNext()) { - final IdentNode ident = functionNode.getIdent(); - defineSymbol(blocks.next(), ident.getName(), ident, IS_VAR | (functionNode.isAnonymous()? IS_INTERNAL : 0)); - } - } - // Every function has a body, even the ones skipped on reparse (they have an empty one). We're // asserting this as even for those, enterBlock() must be invoked to correctly process symbols that // are used in them. @@ -532,16 +522,36 @@ final class AssignSymbols extends NodeVisitor implements Loggabl @Override public boolean enterVarNode(final VarNode varNode) { start(varNode); + // Normally, a symbol assigned in a var statement is not live for its RHS. Since we also represent function + // declarations as VarNodes, they are exception to the rule, as they need to have the symbol visible to the + // body of the declared function for self-reference. + if (varNode.isFunctionDeclaration()) { + defineVarIdent(varNode); + } return true; } @Override public Node leaveVarNode(final VarNode varNode) { - final IdentNode ident = varNode.getName(); - defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | (lc.getCurrentFunction().isProgram() ? IS_SCOPE : 0)); + if (!varNode.isFunctionDeclaration()) { + defineVarIdent(varNode); + } return super.leaveVarNode(varNode); } + private void defineVarIdent(final VarNode varNode) { + final IdentNode ident = varNode.getName(); + final int flags; + if (varNode.isAnonymousFunctionDeclaration()) { + flags = IS_INTERNAL; + } else if (lc.getCurrentFunction().isProgram()) { + flags = IS_SCOPE; + } else { + flags = 0; + } + defineSymbol(lc.getCurrentBlock(), ident.getName(), ident, varNode.getSymbolFlags() | flags); + } + private Symbol exceptionSymbol() { return newObjectInternal(EXCEPTION_PREFIX); } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java index f9aef826fe6..a8db410042e 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/VarNode.java @@ -272,4 +272,12 @@ public final class VarNode extends Statement implements Assignment { public boolean isFunctionDeclaration() { return init instanceof FunctionNode && ((FunctionNode)init).isDeclared(); } + + /** + * Returns true if this is an anonymous function declaration. + * @return true if this is an anonymous function declaration. + */ + public boolean isAnonymousFunctionDeclaration() { + return isFunctionDeclaration() && ((FunctionNode)init).isAnonymous(); + } } From a891de31140740b036bd75ac5a1c063d3456829a Mon Sep 17 00:00:00 2001 From: Attila Szegedi Date: Tue, 14 Oct 2014 13:04:56 +0200 Subject: [PATCH 05/11] 8060238: Reports for optimistic test run overwrite those for pessimistic run Reviewed-by: hannesw, lagergren, sundar --- nashorn/make/build.xml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/nashorn/make/build.xml b/nashorn/make/build.xml index 27ae1b319fc..9e6f5b5558e 100644 --- a/nashorn/make/build.xml +++ b/nashorn/make/build.xml @@ -408,7 +408,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" { - @@ -431,7 +431,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" { - @@ -457,9 +457,11 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" { + + @@ -467,9 +469,11 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" { + + From fab85d4210cd99e9901a8bbfac85c9a9bdfc32ae Mon Sep 17 00:00:00 2001 From: Andreas Gabrielsson Date: Tue, 14 Oct 2014 15:28:24 +0200 Subject: [PATCH 06/11] 8012518: Reengineer Parser.java to make it play well with the copy-on-write IR Remove the kludges introduced to make the parser work with the copy on write IR. Now everything is done bottom up, finshing node children completely before node parents. The repeated non-functional pattern 'node = node.setSomething(something);' is gone. Resulting code is much more readable, and extensible for future work. The parser is now also consistent with the rest of the stateless copy-on-write world in code generation. Reviewed-by: lagergren, attila, hannesw, shade --- nashorn/.hgignore | 2 + nashorn/bin/runopt.sh | 137 +++++ .../nashorn/api/scripting/ScriptUtils.java | 2 - .../jdk/nashorn/internal/ir/Block.java | 48 +- .../jdk/nashorn/internal/ir/ForNode.java | 61 +-- .../jdk/nashorn/internal/ir/FunctionNode.java | 77 +-- .../jdk/nashorn/internal/ir/LoopNode.java | 5 +- .../classes/jdk/nashorn/internal/ir/Node.java | 22 +- .../jdk/nashorn/internal/ir/WhileNode.java | 12 +- .../jdk/nashorn/internal/ir/WithNode.java | 14 +- .../jdk/nashorn/internal/objects/Global.java | 2 - .../nashorn/internal/objects/NativeError.java | 2 - .../nashorn/internal/objects/NativeJava.java | 2 - .../jdk/nashorn/internal/parser/Parser.java | 487 ++++++++++-------- .../internal/parser/ParserContext.java | 334 ++++++++++++ .../parser/ParserContextBaseNode.java | 109 ++++ .../parser/ParserContextBlockNode.java | 56 ++ .../parser/ParserContextBreakableNode.java | 40 ++ .../parser/ParserContextFunctionNode.java | 197 +++++++ .../parser/ParserContextLabelNode.java | 52 ++ .../parser/ParserContextLoopNode.java | 37 ++ .../internal/parser/ParserContextNode.java | 67 +++ .../parser/ParserContextSwitchNode.java | 36 ++ .../jdk/nashorn/internal/runtime/Context.java | 35 +- 24 files changed, 1466 insertions(+), 370 deletions(-) create mode 100644 nashorn/bin/runopt.sh create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContext.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBaseNode.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBlockNode.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBreakableNode.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLabelNode.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLoopNode.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextNode.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextSwitchNode.java diff --git a/nashorn/.hgignore b/nashorn/.hgignore index 02ec40d7e8d..7ee241994a9 100644 --- a/nashorn/.hgignore +++ b/nashorn/.hgignore @@ -26,3 +26,5 @@ jcov2/* test/lib/testng.jar test/script/external/* .project +.externalToolBuilders/* +.settings/* diff --git a/nashorn/bin/runopt.sh b/nashorn/bin/runopt.sh new file mode 100644 index 00000000000..0a61491e624 --- /dev/null +++ b/nashorn/bin/runopt.sh @@ -0,0 +1,137 @@ +#!/bin/sh +# +# Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +########################################################################################### +# This is a helper script to evaluate nashorn with optimistic types +# it produces a flight recording for every run, and uses the best +# known flags for performance for the current configration +########################################################################################### + +# Flags to enable assertions, we need the system assertions too, since +# this script runs Nashorn in the BCP to override any nashorn.jar that might +# reside in your $JAVA_HOME/jre/lib/ext/nashorn.jar +# +ENABLE_ASSERTIONS_FLAGS="-ea -esa" + +# Flags to instrument lambdaform computation, caching, interpretation and compilation +# Default compile threshold for lambdaforms is 30 +# +#LAMBDAFORM_FLAGS="\ +# -Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=3 \ +# -Djava.lang.invoke.MethodHandle.DUMP_CLASS_FILES=true \ +# -Djava.lang.invoke.MethodHandle.TRACE_METHOD_LINKAGE=true \ +# -Djava.lang.invoke.MethodHandle.TRACE_INTERPRETER=true" + +# Flags to run trusted tests from the Nashorn test suite +# +#TRUSTED_TEST_FLAGS="\ +#-Djava.security.manager \ +#-Djava.security.policy=../build/nashorn.policy -Dnashorn.debug" + +# Testing out new code optimizations using the generic hotspot "new code" parameter +# +#USE_NEW_CODE_FLAGS=-XX:+UnlockDiagnosticVMOptions -XX:+UseNewCode + +# +#-Dnashorn.typeInfo.disabled=false \ +# and for Nashorn options: +# --class-cache-size=0 --persistent-code-cache=false + +# Unique timestamped file name for JFR recordings. For JFR, we also have to +# crank up the stack cutoff depth to 1024, because of ridiculously long lambda form +# stack traces. +# +# It is also recommended that you go into $JAVA_HOME/jre/lib/jfr/default.jfc and +# set the "method-sampling-interval" Normal and Maximum sample time as low as you +# can go (10 ms on most platforms). The default is normally higher. The increased +# sampling overhead is usually negligible for Nashorn runs, but the data is better + +if [ -z $JFR_FILENAME ]; then + JFR_FILENAME="./nashorn_$(date|sed "s/ /_/g"|sed "s/:/_/g").jfr" + echo "Using default JFR filename: ${JFR_FILENAME}..." +fi + +# Flight recorder +# +# see above - already in place, copy the flags down here to disable +ENABLE_FLIGHT_RECORDER_FLAGS="\ + -XX:+UnlockCommercialFeatures \ + -XX:+FlightRecorder \ + -XX:FlightRecorderOptions=defaultrecording=true,disk=true,dumponexit=true,dumponexitpath=$JFR_FILENAME,stackdepth=1024" + +# Type specialization and math intrinsic replacement should be enabled by default in 8u20 and nine, +# keeping this flag around for experimental reasons. Replace + with - to switch it off +# +#ENABLE_TYPE_SPECIALIZATION_FLAGS=-XX:+UseTypeSpeculation + +# Same with math intrinsics. They should be enabled by default in 8u20 and 9, so +# this disables them if needed +# +#DISABLE_MATH_INTRINSICS_FLAGS=-XX:-UseMathExactIntrinsics + +# Add timing to time the compilation phases. +#ENABLE_TIME_FLAGS=--log=time + +# Add ShowHiddenFrames to get lambda form internals on the stack traces +#ENABLE_SHOW_HIDDEN_FRAMES_FLAGS=-XX:+ShowHiddenFrames + +# Add print optoassembly to get an asm dump. This requires 1) a debug build, not product, +# That tired compilation is switched off, for C2 only output and that the number of +# compiler threads is set to 1 for determinsm. +# +#PRINT_ASM_FLAGS=-XX:+PrintOptoAssembly -XX:-TieredCompilation -XX:CICompilerCount=1 \ + +# Tier compile threasholds. Default value is 10. (1-100 is useful for experiments) +#TIER_COMPILATION_THRESHOLD_FLAGS=-XX:IncreaseFirstTierCompileThresholdAt=10 + +# Directory where to look for nashorn.jar in a dist folder. The default is "..", assuming +# that we run the script from the make dir +DIR=.. +NASHORN_JAR=$DIR/dist/nashorn.jar + + +# The built Nashorn jar is placed first in the bootclasspath to override the JDK +# nashorn.jar in $JAVA_HOME/jre/lib/ext. Thus, we also need -esa, as assertions in +# nashorn count as system assertions in this configuration + +# Type profiling default level is 111, 222 adds some compile time, but is faster + +$JAVA_HOME/bin/java \ +$ENABLE_ASSERTIONS_FLAGS \ +$LAMBDAFORM_FLAGS \ +$TRUSTED_FLAGS \ +$USE_NEW_CODE_FLAGS \ +$ENABLE_SHOW_HIDDEN_FRAMES_FLAGS \ +$ENABLE_FLIGHT_RECORDER_FLAGS \ +$ENABLE_TYPE_SPECIALIZATION_FLAGS \ +$TIERED_COMPILATION_THRESOLD_FLAGS \ +$DISABLE_MATH_INTRINSICS_FLAGS \ +$PRINT_ASM_FLAGS \ +-Xbootclasspath/p:$NASHORN_JAR \ +-Xms2G -Xmx2G \ +-XX:TypeProfileLevel=222 \ +-cp $CLASSPATH:../build/test/classes/ \ +jdk.nashorn.tools.Shell $ENABLE_TIME_FLAGS ${@} + + diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java index b6c4c97e547..e6e3915ab29 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/scripting/ScriptUtils.java @@ -25,8 +25,6 @@ package jdk.nashorn.api.scripting; -import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; - import java.lang.invoke.MethodHandle; import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.linker.LinkerServices; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java index 86a84ca6de8..e3a26893cb5 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Block.java @@ -80,11 +80,12 @@ public class Block extends Node implements BreakableNode, Terminal, Flags /** * Constructor * - * @param token token - * @param finish finish - * @param statements statements + * @param token The first token of the block + * @param finish The index of the last character + * @param flags The flags of the block + * @param statements All statements in the block */ - public Block(final long token, final int finish, final Statement... statements) { + public Block(final long token, final int finish, final int flags, final Statement... statements) { super(token, finish); this.statements = Arrays.asList(statements); @@ -92,29 +93,52 @@ public class Block extends Node implements BreakableNode, Terminal, Flags this.entryLabel = new Label("block_entry"); this.breakLabel = new Label("block_break"); final int len = statements.length; - this.flags = len > 0 && statements[len - 1].hasTerminalFlags() ? IS_TERMINAL : 0; + final int terminalFlags = len > 0 && statements[len - 1].hasTerminalFlags() ? IS_TERMINAL : 0; + this.flags = terminalFlags | flags; this.conversion = null; } + /** + * Constructs a new block + * + * @param token The first token of the block + * @param finish The index of the last character + * @param statements All statements in the block + */ + public Block(final long token, final int finish, final Statement...statements){ + this(token, finish, 0, statements); + } + + /** + * Constructs a new block + * + * @param token The first token of the block + * @param finish The index of the last character + * @param statements All statements in the block + */ + public Block(final long token, final int finish, final List statements){ + this(token, finish, 0, statements); + } + /** * Constructor * - * @param token token - * @param finish finish - * @param statements statements + * @param token The first token of the block + * @param finish The index of the last character + * @param flags The flags of the block + * @param statements All statements in the block */ - public Block(final long token, final int finish, final List statements) { - this(token, finish, statements.toArray(new Statement[statements.size()])); + public Block(final long token, final int finish, final int flags, final List statements) { + this(token, finish, flags, statements.toArray(new Statement[statements.size()])); } private Block(final Block block, final int finish, final List statements, final int flags, final Map symbols, final LocalVariableConversion conversion) { - super(block); + super(block, finish); this.statements = statements; this.flags = flags; this.symbols = new LinkedHashMap<>(symbols); //todo - symbols have no dependencies on any IR node and can as far as we understand it be shallow copied now this.entryLabel = new Label(block.entryLabel); this.breakLabel = new Label(block.breakLabel); - this.finish = finish; this.conversion = conversion; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java index 9b4cb6d6e1c..bc864eb68bb 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ForNode.java @@ -54,20 +54,37 @@ public final class ForNode extends LoopNode { private final int flags; + /** + * Constructs a ForNode + * + * @param lineNumber The line number of header + * @param token The for token + * @param finish The last character of the for node + * @param body The body of the for node + * @param flags The flags + */ + public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags){ + this(lineNumber, token, finish, body, flags, null, null, null); + } + /** * Constructor * - * @param lineNumber line number - * @param token token - * @param finish finish - * @param body body - * @param flags flags + * @param lineNumber The line number of header + * @param token The for token + * @param finish The last character of the for node + * @param body The body of the for node + * @param flags The flags + * @param init The initial expression + * @param test The test expression + * @param modify The modify expression */ - public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags) { - super(lineNumber, token, finish, body, false); + public ForNode(final int lineNumber, final long token, final int finish, final Block body, final int flags, final Expression init, final JoinPredecessorExpression test, final JoinPredecessorExpression modify) { + super(lineNumber, token, finish, body, test, false); this.flags = flags; - this.init = null; - this.modify = null; + this.init = init; + this.modify = modify; + } private ForNode(final ForNode forNode, final Expression init, final JoinPredecessorExpression test, @@ -166,16 +183,6 @@ public final class ForNode extends LoopNode { public boolean isForIn() { return (flags & IS_FOR_IN) != 0; } - - /** - * Flag this to be a for in construct - * @param lc lexical context - * @return new for node if changed or existing if not - */ - public ForNode setIsForIn(final LexicalContext lc) { - return setFlags(lc, flags | IS_FOR_IN); - } - /** * Is this a for each construct, known from e.g. Rhino. This will be a for of construct * in ECMAScript 6 @@ -185,15 +192,6 @@ public final class ForNode extends LoopNode { return (flags & IS_FOR_EACH) != 0; } - /** - * Flag this to be a for each construct - * @param lc lexical context - * @return new for node if changed or existing if not - */ - public ForNode setIsForEach(final LexicalContext lc) { - return setFlags(lc, flags | IS_FOR_EACH); - } - /** * If this is a for in or for each construct, there is an iterator symbol * @return the symbol for the iterator to be used, or null if none exists @@ -260,13 +258,6 @@ public final class ForNode extends LoopNode { return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); } - private ForNode setFlags(final LexicalContext lc, final int flags) { - if (this.flags == flags) { - return this; - } - return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); - } - @Override JoinPredecessor setLocalVariableConversionChanged(final LexicalContext lc, final LocalVariableConversion conversion) { return Node.replaceInLexicalContext(lc, this, new ForNode(this, init, test, body, modify, flags, controlFlowEscapes, conversion)); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java index 1bbc7ab0834..ad19f8d53a8 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java @@ -31,7 +31,6 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_ENTEREXIT; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_MISSES; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_TRACE_VALUES; - import java.util.Collections; import java.util.EnumSet; import java.util.Iterator; @@ -46,6 +45,7 @@ import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.annotations.Ignore; import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.parser.Token; import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.Source; @@ -299,12 +299,16 @@ public final class FunctionNode extends LexicalContextExpression implements Flag * @param token token * @param finish finish * @param firstToken first token of the function node (including the function declaration) + * @param lastToken lastToken * @param namespace the namespace * @param ident the identifier * @param name the name of the function * @param parameters parameter list * @param kind kind of function as in {@link FunctionNode.Kind} * @param flags initial flags + * @param body body of the function + * @param state The initial state from the parser. Must be one of {@link CompilationState#PARSED} and {@link CompilationState#PARSE_ERROR} + * @param endParserState The parser state at the end of the parsing. */ public FunctionNode( final Source source, @@ -312,12 +316,16 @@ public final class FunctionNode extends LexicalContextExpression implements Flag final long token, final int finish, final long firstToken, + final long lastToken, final Namespace namespace, final IdentNode ident, final String name, final List parameters, final FunctionNode.Kind kind, - final int flags) { + final int flags, + final Block body, + final CompilationState state, + final Object endParserState) { super(token, finish); this.source = source; @@ -327,15 +335,15 @@ public final class FunctionNode extends LexicalContextExpression implements Flag this.kind = kind; this.parameters = parameters; this.firstToken = firstToken; - this.lastToken = token; + this.lastToken = lastToken; this.namespace = namespace; - this.compilationState = EnumSet.of(CompilationState.INITIALIZED); + this.compilationState = EnumSet.of(CompilationState.INITIALIZED, state); this.flags = flags; this.compileUnit = null; - this.body = null; + this.body = body; this.thisProperties = 0; this.rootClass = null; - this.endParserState = null; + this.endParserState = endParserState; } private FunctionNode( @@ -439,7 +447,7 @@ public final class FunctionNode extends LexicalContextExpression implements Flag * @return the id */ public int getId() { - return position(); + return isProgram() ? -1: Token.descPosition(firstToken); } /** @@ -902,34 +910,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag return lastToken; } - /** - * Set the last token for this function's code - * @param lc lexical context - * @param lastToken the last token - * @return function node or a new one if state was changed - */ - public FunctionNode setLastToken(final LexicalContext lc, final long lastToken) { - if (this.lastToken == lastToken) { - return this; - } - return Node.replaceInLexicalContext( - lc, - this, - new FunctionNode( - this, - lastToken, - endParserState, - flags, - name, - returnType, - compileUnit, - compilationState, - body, - parameters, - thisProperties, - rootClass)); - } - /** * Returns the end parser state for this function. * @return the end parser state for this function. @@ -938,33 +918,6 @@ public final class FunctionNode extends LexicalContextExpression implements Flag return endParserState; } - /** - * Set the end parser state for this function. - * @param lc lexical context - * @param endParserState the parser state to set - * @return function node or a new one if state was changed - */ - public FunctionNode setEndParserState(final LexicalContext lc, final Object endParserState) { - if (this.endParserState == endParserState) { - return this; - } - return Node.replaceInLexicalContext( - lc, - this, - new FunctionNode( - this, - lastToken, - endParserState, - flags, - name, - returnType, - compileUnit, - compilationState, - body, - parameters, - thisProperties, rootClass)); - } - /** * Get the name of this function * @return the name diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java index 86ef3cdad49..bfc86a69a43 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/LoopNode.java @@ -53,14 +53,15 @@ public abstract class LoopNode extends BreakableStatement { * @param token token * @param finish finish * @param body loop body + * @param test test * @param controlFlowEscapes controlFlowEscapes */ - protected LoopNode(final int lineNumber, final long token, final int finish, final Block body, final boolean controlFlowEscapes) { + protected LoopNode(final int lineNumber, final long token, final int finish, final Block body, final JoinPredecessorExpression test, final boolean controlFlowEscapes) { super(lineNumber, token, finish, new Label("while_break")); this.continueLabel = new Label("while_continue"); - this.test = null; this.body = body; this.controlFlowEscapes = controlFlowEscapes; + this.test = test; } /** diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Node.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Node.java index 37ec4b96a39..861f12a05ff 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Node.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/Node.java @@ -39,7 +39,7 @@ public abstract class Node implements Cloneable { protected final int start; /** End of source range. */ - protected int finish; + protected final int finish; /** Token descriptor. */ private final long token; @@ -80,6 +80,18 @@ public abstract class Node implements Cloneable { this.finish = node.finish; } + /** + * Copy constructor that overrides finish + * + * @param node source node + * @param finish Last character + */ + protected Node(final Node node, final int finish) { + this.token = node.token; + this.start = node.start; + this.finish = finish; + } + /** * Is this a loop node? * @@ -151,14 +163,6 @@ public abstract class Node implements Cloneable { return finish; } - /** - * Set finish position for this node in the source string - * @param finish finish - */ - public void setFinish(final int finish) { - this.finish = finish; - } - /** * Get start position for node * @return start position diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java index 0cced567fbc..99bdce87c19 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WhileNode.java @@ -45,9 +45,11 @@ public final class WhileNode extends LoopNode { * @param token token * @param finish finish * @param isDoWhile is this a do while loop? + * @param test test expression + * @param body body of the while loop */ - public WhileNode(final int lineNumber, final long token, final int finish, final boolean isDoWhile) { - super(lineNumber, token, finish, null, false); + public WhileNode(final int lineNumber, final long token, final int finish, final boolean isDoWhile, final JoinPredecessorExpression test, final Block body) { + super(lineNumber, token, finish, body, test, false); this.isDoWhile = isDoWhile; } @@ -55,10 +57,10 @@ public final class WhileNode extends LoopNode { * Internal copy constructor * * @param whileNode while node - * @param test test - * @param body body + * @param test Test expression + * @param body body of the while loop * @param controlFlowEscapes control flow escapes? - * @param conversion TODO + * @param conversion local variable conversion info */ private WhileNode(final WhileNode whileNode, final JoinPredecessorExpression test, final Block body, final boolean controlFlowEscapes, final LocalVariableConversion conversion) { super(whileNode, test, body, controlFlowEscapes, conversion); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WithNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WithNode.java index 0ea52c98bb4..20e319294d2 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WithNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/WithNode.java @@ -42,14 +42,16 @@ public final class WithNode extends LexicalContextStatement { /** * Constructor * - * @param lineNumber line number - * @param token token - * @param finish finish + * @param lineNumber Line number of the header + * @param token First token + * @param finish Character index of the last token + * @param expression With expression + * @param body Body of with node */ - public WithNode(final int lineNumber, final long token, final int finish) { + public WithNode(final int lineNumber, final long token, final int finish, final Expression expression, final Block body) { super(lineNumber, token, finish); - this.expression = null; - this.body = null; + this.expression = expression; + this.body = body; } private WithNode(final WithNode node, final Expression expression, final Block body) { diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java index eb56de0e18f..61d7948f621 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java @@ -561,7 +561,6 @@ 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 @@ -1718,7 +1717,6 @@ public final class Global extends ScriptObject implements Scope { return func; } - @SuppressWarnings("hiding") private void init(final ScriptEngine engine) { assert Context.getGlobal() == this : "this global is not set as current"; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeError.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeError.java index e1d95ce097f..41ea9a5f4e2 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeError.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeError.java @@ -27,7 +27,6 @@ package jdk.nashorn.internal.objects; 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 jdk.nashorn.api.scripting.NashornException; @@ -131,7 +130,6 @@ public final class NativeError extends ScriptObject { // This is called NativeError, NativeTypeError etc. to // associate a ECMAException with the ECMA Error object. - @SuppressWarnings("unused") static void initException(final ScriptObject self) { // ECMAException constructor has side effects new ECMAException(self, null); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java index 7c6e60a967d..e9a4380dadc 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeJava.java @@ -27,7 +27,6 @@ package jdk.nashorn.internal.objects; import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; - import java.lang.invoke.MethodHandles; import java.lang.reflect.Array; import java.util.Collection; @@ -36,7 +35,6 @@ import java.util.List; import jdk.internal.dynalink.beans.StaticClass; import jdk.internal.dynalink.support.TypeUtilities; import jdk.nashorn.api.scripting.JSObject; -import jdk.nashorn.api.scripting.ScriptUtils; import jdk.nashorn.internal.objects.annotations.Attribute; import jdk.nashorn.internal.objects.annotations.Function; import jdk.nashorn.internal.objects.annotations.ScriptClass; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java index 3162e184468..32f1df63895 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java @@ -53,7 +53,6 @@ import static jdk.nashorn.internal.parser.TokenType.RPAREN; import static jdk.nashorn.internal.parser.TokenType.SEMICOLON; import static jdk.nashorn.internal.parser.TokenType.TERNARY; import static jdk.nashorn.internal.parser.TokenType.WHILE; - import java.io.Serializable; import java.util.ArrayDeque; import java.util.ArrayList; @@ -71,10 +70,8 @@ import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BaseNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; -import jdk.nashorn.internal.ir.BlockLexicalContext; import jdk.nashorn.internal.ir.BlockStatement; import jdk.nashorn.internal.ir.BreakNode; -import jdk.nashorn.internal.ir.BreakableNode; import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; @@ -90,9 +87,7 @@ import jdk.nashorn.internal.ir.IfNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.JoinPredecessorExpression; import jdk.nashorn.internal.ir.LabelNode; -import jdk.nashorn.internal.ir.LexicalContext; import jdk.nashorn.internal.ir.LiteralNode; -import jdk.nashorn.internal.ir.LoopNode; import jdk.nashorn.internal.ir.Node; import jdk.nashorn.internal.ir.ObjectNode; import jdk.nashorn.internal.ir.PropertyKey; @@ -138,8 +133,8 @@ public class Parser extends AbstractParser implements Loggable { private List functionDeclarations; - private final BlockLexicalContext lc = new BlockLexicalContext(); - private final Deque defaultNames = new ArrayDeque<>(); + private final ParserContext lc; + private final Deque defaultNames; /** Namespace for function names where not explicitly given */ private final Namespace namespace; @@ -187,6 +182,8 @@ public class Parser extends AbstractParser implements Loggable { */ public Parser(final ScriptEnvironment env, final Source source, final ErrorManager errors, final boolean strict, final int lineOffset, final DebugLogger log) { super(source, errors, strict, lineOffset); + this.lc = new ParserContext(); + this.defaultNames = new ArrayDeque<>(); this.env = env; this.namespace = new Namespace(env.getNamespace()); this.scripting = env._scripting; @@ -344,26 +341,35 @@ public class Parser extends AbstractParser implements Loggable { final long functionToken = Token.toDesc(FUNCTION, 0, source.getLength()); // Set up the function to append elements. - FunctionNode function = newFunctionNode( - functionToken, - new IdentNode(functionToken, Token.descPosition(functionToken), PROGRAM.symbolName()), - new ArrayList(), - FunctionNode.Kind.NORMAL, - functionLine); + final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), PROGRAM.symbolName()); + final ParserContextFunctionNode function = createParserContextFunctionNode(ident, functionToken, FunctionNode.Kind.NORMAL, functionLine, Collections.emptyList()); + lc.push(function); + + final ParserContextBlockNode body = newBlock(); functionDeclarations = new ArrayList<>(); sourceElements(false); addFunctionDeclarations(function); functionDeclarations = null; + restoreBlock(body); + body.setFlag(Block.NEEDS_SCOPE); + + final Block functionBody = new Block(functionToken, source.getLength() - 1, body.getFlags(), body.getStatements()); + lc.pop(function); + expect(EOF); - function.setFinish(source.getLength() - 1); - function = restoreFunctionNode(function, token); //commit code - function = function.setBody(lc, function.getBody().setNeedsScope(lc)); - - printAST(function); - return function; + final FunctionNode functionNode = createFunctionNode( + function, + functionToken, + ident, + Collections.emptyList(), + FunctionNode.Kind.NORMAL, + functionLine, + functionBody); + printAST(functionNode); + return functionNode; } catch (final Exception e) { handleParseException(e); return null; @@ -444,21 +450,15 @@ loop: * * @return New block. */ - private Block newBlock() { - return lc.push(new Block(token, Token.descPosition(token))); + private ParserContextBlockNode newBlock() { + return lc.push(new ParserContextBlockNode(token)); } - /** - * Set up a new function block. - * - * @param ident Name of function. - * @return New block. - */ - private FunctionNode newFunctionNode(final long startToken, final IdentNode ident, final List parameters, final FunctionNode.Kind kind, final int functionLine) { + private ParserContextFunctionNode createParserContextFunctionNode(final IdentNode ident, final long functionToken, final FunctionNode.Kind kind, final int functionLine, final List parameters) { // Build function name. final StringBuilder sb = new StringBuilder(); - final FunctionNode parentFunction = lc.getCurrentFunction(); + final ParserContextFunctionNode parentFunction = lc.getCurrentFunction(); if (parentFunction != null && !parentFunction.isProgram()) { sb.append(parentFunction.getName()).append('$'); } @@ -477,25 +477,33 @@ loop: flags |= FunctionNode.IS_PROGRAM; } + final ParserContextFunctionNode functionNode = new ParserContextFunctionNode(functionToken, ident, name, namespace, functionLine, kind, parameters); + functionNode.setFlag(flags); + return functionNode; + } + + private FunctionNode createFunctionNode(final ParserContextFunctionNode function, final long startToken, final IdentNode ident, final List parameters, final FunctionNode.Kind kind, final int functionLine, final Block body){ + final CompilationState state = errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED; // Start new block. final FunctionNode functionNode = new FunctionNode( source, functionLine, - token, - Token.descPosition(token), + body.getToken(), + Token.descPosition(body.getToken()), startToken, + function.getLastToken(), namespace, ident, - name, + function.getName(), parameters, kind, - flags); + function.getFlags(), + body, + state, + function.getEndParserState()); - lc.push(functionNode); - // Create new block, and just put it on the context stack, restoreFunctionNode() will associate it with the - // FunctionNode. - newBlock(); + printAST(functionNode); return functionNode; } @@ -503,27 +511,17 @@ loop: /** * Restore the current block. */ - private Block restoreBlock(final Block block) { + private ParserContextBlockNode restoreBlock(final ParserContextBlockNode block) { return lc.pop(block); } - - private FunctionNode restoreFunctionNode(final FunctionNode functionNode, final long lastToken) { - final Block newBody = restoreBlock(lc.getFunctionBody(functionNode)); - - return lc.pop(functionNode). - setBody(lc, newBody). - setLastToken(lc, lastToken). - setState(lc, errors.hasErrors() ? CompilationState.PARSE_ERROR : CompilationState.PARSED); - } - /** * Get the statements in a block. * @return Block statements. */ private Block getBlock(final boolean needsBraces) { - // Set up new block. Captures LBRACE. - Block newBlock = newBlock(); + final long blockToken = token; + final ParserContextBlockNode newBlock = newBlock(); try { // Block opening brace. if (needsBraces) { @@ -533,21 +531,18 @@ loop: statementList(); } finally { - newBlock = restoreBlock(newBlock); + restoreBlock(newBlock); } - final int possibleEnd = Token.descPosition(token) + Token.descLength(token); - // Block closing brace. if (needsBraces) { expect(RBRACE); } - newBlock.setFinish(possibleEnd); - - return newBlock; + return new Block(blockToken, finish, newBlock.getFlags(), newBlock.getStatements()); } + /** * Get all the statements generated by a single statement. * @return Statements. @@ -557,13 +552,13 @@ loop: return getBlock(true); } // Set up new block. Captures first token. - Block newBlock = newBlock(); + final ParserContextBlockNode newBlock = newBlock(); try { statement(); } finally { - newBlock = restoreBlock(newBlock); + restoreBlock(newBlock); } - return newBlock; + return new Block(newBlock.getToken(), finish, newBlock.getFlags(), newBlock.getStatements()); } /** @@ -584,7 +579,7 @@ loop: */ private void detectSpecialProperty(final IdentNode ident) { if (isArguments(ident)) { - lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_ARGUMENTS); + lc.getCurrentFunction().setFlag(FunctionNode.USES_ARGUMENTS); } } @@ -698,18 +693,19 @@ loop: // Make a pseudo-token for the script holding its start and length. final long functionToken = Token.toDesc(FUNCTION, Token.descPosition(Token.withDelimiter(token)), source.getLength()); final int functionLine = line; - // Set up the script to append elements. - - FunctionNode script = newFunctionNode( - functionToken, - new IdentNode(functionToken, Token.descPosition(functionToken), scriptName), - new ArrayList(), - FunctionNode.Kind.SCRIPT, - functionLine); + final IdentNode ident = new IdentNode(functionToken, Token.descPosition(functionToken), scriptName); + final ParserContextFunctionNode script = createParserContextFunctionNode( + ident, + functionToken, + FunctionNode.Kind.SCRIPT, + functionLine, + Collections.emptyList()); + lc.push(script); + final ParserContextBlockNode body = newBlock(); // If ES6 block scope is enabled add a per-script block for top-level LET and CONST declarations. final int startLine = start; - Block outer = useBlockScope() ? newBlock() : null; + final ParserContextBlockNode outer = useBlockScope() ? newBlock() : null; functionDeclarations = new ArrayList<>(); try { @@ -717,20 +713,25 @@ loop: addFunctionDeclarations(script); } finally { if (outer != null) { - outer = restoreBlock(outer); - appendStatement(new BlockStatement(startLine, outer)); + restoreBlock(outer); + appendStatement(new BlockStatement( + startLine, + new Block( + functionToken, + startLine, outer.getFlags(), + outer.getStatements()))); } } functionDeclarations = null; + restoreBlock(body); + body.setFlag(Block.NEEDS_SCOPE); + final Block programBody = new Block(functionToken, functionLine, body.getFlags(), body.getStatements()); + lc.pop(script); + script.setLastToken(token); expect(EOF); - script.setFinish(source.getLength() - 1); - - script = restoreFunctionNode(script, token); //commit code - script = script.setBody(lc, script.getBody().setNeedsScope(lc)); - - return script; + return createFunctionNode(script, functionToken, ident, Collections.emptyList(), FunctionNode.Kind.SCRIPT, functionLine, programBody); } /** @@ -789,7 +790,7 @@ loop: // check for directive prologues if (checkDirective) { // skip any debug statement like line number to get actual first line - final Node lastStatement = lc.getLastStatement(); + final Statement lastStatement = lc.getLastStatement(); // get directive prologue, if any final String directive = getDirective(lastStatement); @@ -809,8 +810,8 @@ loop: // handle use strict directive if ("use strict".equals(directive)) { isStrictMode = true; - final FunctionNode function = lc.getCurrentFunction(); - lc.setFlag(lc.getCurrentFunction(), FunctionNode.IS_STRICT); + final ParserContextFunctionNode function = lc.getCurrentFunction(); + function.setFlag(FunctionNode.IS_STRICT); // We don't need to check these, if lexical environment is already strict if (!oldStrictMode && directiveStmts != null) { @@ -831,8 +832,8 @@ loop: } else if (Context.DEBUG) { final int flag = FunctionNode.getDirectiveFlag(directive); if (flag != 0) { - final FunctionNode function = lc.getCurrentFunction(); - lc.setFlag(function, flag); + final ParserContextFunctionNode function = lc.getCurrentFunction(); + function.setFlag(flag); } } } @@ -1114,11 +1115,7 @@ loop: // If is a statement then handle end of line. if (isStatement) { - final boolean semicolon = type == SEMICOLON; endOfLine(); - if (semicolon) { - lc.getCurrentBlock().setFinish(finish); - } } return vars; @@ -1166,11 +1163,6 @@ loop: } endOfLine(); - - if (expressionStatement != null) { - expressionStatement.setFinish(finish); - lc.getCurrentBlock().setFinish(finish); - } } /** @@ -1216,13 +1208,23 @@ loop: * Parse a FOR statement. */ private void forStatement() { + final long forToken = token; + final int forLine = line; // When ES6 for-let is enabled we create a container block to capture the LET. final int startLine = start; - Block outer = useBlockScope() ? newBlock() : null; + final ParserContextBlockNode outer = useBlockScope() ? newBlock() : null; + // Create FOR node, capturing FOR token. - ForNode forNode = new ForNode(line, token, Token.descPosition(token), null, ForNode.IS_FOR); + final ParserContextLoopNode forNode = new ParserContextLoopNode(); lc.push(forNode); + Block body = null; + List vars = null; + Expression init = null; + JoinPredecessorExpression test = null; + JoinPredecessorExpression modify = null; + + int flags = 0; try { // FOR tested in caller. @@ -1231,13 +1233,12 @@ loop: // Nashorn extension: for each expression. // iterate property values rather than property names. if (!env._no_syntax_extensions && type == IDENT && "each".equals(getValue())) { - forNode = forNode.setIsForEach(lc); + flags |= ForNode.IS_FOR_EACH; next(); } expect(LPAREN); - List vars = null; switch (type) { case VAR: @@ -1258,8 +1259,7 @@ loop: break; } - final Expression expression = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true); - forNode = forNode.setInit(lc, expression); + init = expression(unaryExpression(), COMMARIGHT.getPrecedence(), true); break; } @@ -1268,26 +1268,27 @@ loop: // for (init; test; modify) // for each (init; test; modify) is invalid - if (forNode.isForEach()) { + if ((flags & ForNode.IS_FOR_EACH) != 0) { throw error(AbstractParser.message("for.each.without.in"), token); } expect(SEMICOLON); if (type != SEMICOLON) { - forNode = forNode.setTest(lc, joinPredecessorExpression()); + test = joinPredecessorExpression(); } expect(SEMICOLON); if (type != RPAREN) { - forNode = forNode.setModify(lc, joinPredecessorExpression()); + modify = joinPredecessorExpression(); } break; case IN: - forNode = forNode.setIsForIn(lc).setTest(lc, new JoinPredecessorExpression()); + flags |= ForNode.IS_FOR_IN; + test = new JoinPredecessorExpression(); if (vars != null) { // for (var i in obj) if (vars.size() == 1) { - forNode = forNode.setInit(lc, new IdentNode(vars.get(0).getName())); + init = new IdentNode(vars.get(0).getName()); } else { // for (var i, j in obj) is invalid throw error(AbstractParser.message("many.vars.in.for.in.loop"), vars.get(1).getToken()); @@ -1295,7 +1296,6 @@ loop: } else { // for (expr in obj) - final Node init = forNode.getInit(); assert init != null : "for..in init expression can not be null here"; // check if initial expression is a valid L-value @@ -1316,7 +1316,7 @@ loop: next(); // Get the collection expression. - forNode = forNode.setModify(lc, joinPredecessorExpression()); + modify = joinPredecessorExpression(); break; default: @@ -1327,37 +1327,27 @@ loop: expect(RPAREN); // Set the for body. - final Block body = getStatement(); - forNode = forNode.setBody(lc, body); - forNode.setFinish(body.getFinish()); - - appendStatement(forNode); + body = getStatement(); } finally { lc.pop(forNode); + if (vars != null) { + for (final VarNode var : vars) { + appendStatement(var); + } + } + if (body != null) { + appendStatement(new ForNode(forLine, forToken, body.getFinish(), body, (forNode.getFlags() | flags), init, test, modify)); + } if (outer != null) { - outer.setFinish(forNode.getFinish()); - outer = restoreBlock(outer); - appendStatement(new BlockStatement(startLine, outer)); + restoreBlock(outer); + appendStatement(new BlockStatement(startLine, new Block( + outer.getToken(), + body.getFinish(), + outer.getStatements()))); } } } - /** - * ... IterationStatement : - * ... - * Expression[NoIn]?; Expression? ; Expression? - * var VariableDeclarationList[NoIn]; Expression? ; Expression? - * LeftHandSideExpression in Expression - * var VariableDeclaration[NoIn] in Expression - * - * See 12.6 - * - * Parse the control section of a FOR statement. Also used for - * comprehensions. - * @param forNode Owning FOR. - */ - - /** * ...IterationStatement : * ... @@ -1371,25 +1361,26 @@ loop: private void whileStatement() { // Capture WHILE token. final long whileToken = token; + final int whileLine = line; // WHILE tested in caller. next(); - // Construct WHILE node. - WhileNode whileNode = new WhileNode(line, whileToken, Token.descPosition(whileToken), false); + final ParserContextLoopNode whileNode = new ParserContextLoopNode(); lc.push(whileNode); + JoinPredecessorExpression test = null; + Block body = null; + try { expect(LPAREN); - final int whileLine = line; - final JoinPredecessorExpression test = joinPredecessorExpression(); + test = joinPredecessorExpression(); expect(RPAREN); - final Block body = getStatement(); - appendStatement(whileNode = - new WhileNode(whileLine, whileToken, finish, false). - setTest(lc, test). - setBody(lc, body)); + body = getStatement(); } finally { lc.pop(whileNode); + if (body != null){ + appendStatement(new WhileNode(whileLine, whileToken, body.getFinish(), false, test, body)); + } } } @@ -1406,34 +1397,32 @@ loop: private void doStatement() { // Capture DO token. final long doToken = token; + int doLine = 0; // DO tested in the caller. next(); - WhileNode doWhileNode = new WhileNode(-1, doToken, Token.descPosition(doToken), true); + final ParserContextLoopNode doWhileNode = new ParserContextLoopNode(); lc.push(doWhileNode); + Block body = null; + JoinPredecessorExpression test = null; + try { // Get DO body. - final Block body = getStatement(); + body = getStatement(); expect(WHILE); expect(LPAREN); - final int doLine = line; - final JoinPredecessorExpression test = joinPredecessorExpression(); + doLine = line; + test = joinPredecessorExpression(); expect(RPAREN); if (type == SEMICOLON) { endOfLine(); } - doWhileNode.setFinish(finish); - - //line number is last - appendStatement(doWhileNode = - new WhileNode(doLine, doToken, finish, true). - setBody(lc, body). - setTest(lc, test)); } finally { lc.pop(doWhileNode); + appendStatement(new WhileNode(doLine, doToken, finish, true, test, body)); } } @@ -1452,7 +1441,7 @@ loop: // CONTINUE tested in caller. nextOrEOL(); - LabelNode labelNode = null; + ParserContextLabelNode labelNode = null; // SEMICOLON or label. switch (type) { @@ -1474,7 +1463,7 @@ loop: } final String labelName = labelNode == null ? null : labelNode.getLabelName(); - final LoopNode targetNode = lc.getContinueTo(labelName); + final ParserContextLoopNode targetNode = lc.getContinueTo(labelName); if (targetNode == null) { throw error(AbstractParser.message("illegal.continue.stmt"), continueToken); @@ -1500,7 +1489,7 @@ loop: // BREAK tested in caller. nextOrEOL(); - LabelNode labelNode = null; + ParserContextLabelNode labelNode = null; // SEMICOLON or label. switch (type) { @@ -1524,7 +1513,7 @@ loop: //either an explicit label - then get its node or just a "break" - get first breakable //targetNode is what we are breaking out from. final String labelName = labelNode == null ? null : labelNode.getLabelName(); - final BreakableNode targetNode = lc.getBreakable(labelName); + final ParserContextBreakableNode targetNode = lc.getBreakable(labelName); if (targetNode == null) { throw error(AbstractParser.message("illegal.break.stmt"), breakToken); } @@ -1632,20 +1621,17 @@ loop: throw error(AbstractParser.message("strict.no.with"), withToken); } - // Get WITH expression. - WithNode withNode = new WithNode(withLine, withToken, finish); - + Expression expression = null; + Block body = null; try { - lc.push(withNode); expect(LPAREN); - withNode = withNode.setExpression(lc, expression()); + expression = expression(); expect(RPAREN); - withNode = withNode.setBody(lc, getStatement()); + body = getStatement(); } finally { - lc.pop(withNode); + appendStatement(new WithNode(withLine, withToken, finish, expression, body)); } - appendStatement(withNode); } /** @@ -1677,19 +1663,22 @@ loop: next(); // Create and add switch statement. - SwitchNode switchNode = new SwitchNode(switchLine, switchToken, Token.descPosition(switchToken), null, new ArrayList(), null); + final ParserContextSwitchNode switchNode= new ParserContextSwitchNode(); lc.push(switchNode); + CaseNode defaultCase = null; + // Prepare to accumulate cases. + final List cases = new ArrayList<>(); + + Expression expression = null; + try { expect(LPAREN); - switchNode = switchNode.setExpression(lc, expression()); + expression = expression(); expect(RPAREN); expect(LBRACE); - // Prepare to accumulate cases. - final List cases = new ArrayList<>(); - CaseNode defaultCase = null; while (type != RBRACE) { // Prepare for next case. @@ -1720,7 +1709,6 @@ loop: // Get CASE body. final Block statements = getBlock(false); final CaseNode caseNode = new CaseNode(caseToken, finish, caseExpression, statements); - statements.setFinish(finish); if (caseExpression == null) { defaultCase = caseNode; @@ -1729,13 +1717,10 @@ loop: cases.add(caseNode); } - switchNode = switchNode.setCases(lc, cases, defaultCase); next(); - switchNode.setFinish(finish); - - appendStatement(switchNode); } finally { lc.pop(switchNode); + appendStatement(new SwitchNode(switchLine, switchToken, finish, expression, cases, defaultCase)); } } @@ -1759,15 +1744,17 @@ loop: throw error(AbstractParser.message("duplicate.label", ident.getName()), labelToken); } - LabelNode labelNode = new LabelNode(line, labelToken, finish, ident.getName(), null); + final ParserContextLabelNode labelNode = new ParserContextLabelNode(ident.getName()); + Block body = null; try { lc.push(labelNode); - labelNode = labelNode.setBody(lc, getStatement()); - labelNode.setFinish(finish); - appendStatement(labelNode); + body = getStatement(); } finally { - assert lc.peek() instanceof LabelNode; + assert lc.peek() instanceof ParserContextLabelNode; lc.pop(labelNode); + if (ident != null){ + appendStatement(new LabelNode(line, labelToken, finish, ident.getName(), body)); + } } } @@ -1835,8 +1822,7 @@ loop: // Container block needed to act as target for labeled break statements final int startLine = line; - Block outer = newBlock(); - + final ParserContextBlockNode outer = newBlock(); // Create try. try { @@ -1867,15 +1853,15 @@ loop: expect(RPAREN); - Block catchBlock = newBlock(); + final ParserContextBlockNode catchBlock = newBlock(); try { // Get CATCH body. final Block catchBody = getBlock(true); final CatchNode catchNode = new CatchNode(catchLine, catchToken, finish, exception, ifExpression, catchBody, false); appendStatement(catchNode); } finally { - catchBlock = restoreBlock(catchBlock); - catchBlocks.add(catchBlock); + restoreBlock(catchBlock); + catchBlocks.add(new Block(catchBlock.getToken(), finish, catchBlock.getFlags(), catchBlock.getStatements())); } // If unconditional catch then should to be the end. @@ -1897,19 +1883,15 @@ loop: throw error(AbstractParser.message("missing.catch.or.finally"), tryToken); } - final TryNode tryNode = new TryNode(tryLine, tryToken, Token.descPosition(tryToken), tryBody, catchBlocks, finallyStatements); + final TryNode tryNode = new TryNode(tryLine, tryToken, finish, tryBody, catchBlocks, finallyStatements); // Add try. assert lc.peek() == outer; appendStatement(tryNode); - - tryNode.setFinish(finish); - outer.setFinish(finish); - } finally { - outer = restoreBlock(outer); + restoreBlock(outer); } - appendStatement(new BlockStatement(startLine, outer)); + appendStatement(new BlockStatement(startLine, new Block(tryToken, finish, outer.getFlags(), outer.getStatements()))); } /** @@ -1927,7 +1909,7 @@ loop: // DEBUGGER tested in caller. next(); endOfLine(); - appendStatement(new ExpressionStatement(debuggerLine, debuggerToken, finish, new RuntimeNode(debuggerToken, finish, RuntimeNode.Request.DEBUGGER, new ArrayList()))); + appendStatement(new ExpressionStatement(debuggerLine, debuggerToken, finish, new RuntimeNode(debuggerToken, finish, RuntimeNode.Request.DEBUGGER, Collections.emptyList()))); } /** @@ -1954,7 +1936,7 @@ loop: case THIS: final String name = type.getName(); next(); - lc.setFlag(lc.getCurrentFunction(), FunctionNode.USES_THIS); + lc.getCurrentFunction().setFlag(FunctionNode.USES_THIS); return new IdentNode(primaryToken, finish, name); case IDENT: final IdentNode ident = getIdent(); @@ -2314,9 +2296,24 @@ loop: final IdentNode getNameNode = createIdentNode(((Node)getIdent).getToken(), finish, NameCodec.encode("get " + getterName)); expect(LPAREN); expect(RPAREN); - final FunctionNode functionNode = functionBody(getSetToken, getNameNode, new ArrayList(), FunctionNode.Kind.GETTER, functionLine); - return new PropertyFunction(getIdent, functionNode); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(getNameNode, getSetToken, FunctionNode.Kind.GETTER, functionLine, Collections.emptyList()); + lc.push(functionNode); + + final Block functionBody = functionBody(functionNode); + + lc.pop(functionNode); + + final FunctionNode function = createFunctionNode( + functionNode, + getSetToken, + getNameNode, + Collections.emptyList(), + FunctionNode.Kind.GETTER, + functionLine, + functionBody); + + return new PropertyFunction(getIdent, function); } private PropertyFunction propertySetterFunction(final long getSetToken, final int functionLine) { @@ -2338,9 +2335,25 @@ loop: if (argIdent != null) { parameters.add(argIdent); } - final FunctionNode functionNode = functionBody(getSetToken, setNameNode, parameters, FunctionNode.Kind.SETTER, functionLine); - return new PropertyFunction(setIdent, functionNode); + + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(setNameNode, getSetToken, FunctionNode.Kind.SETTER, functionLine, parameters); + lc.push(functionNode); + + final Block functionBody = functionBody(functionNode); + + lc.pop(functionNode); + + final FunctionNode function = createFunctionNode( + functionNode, + getSetToken, + setNameNode, + parameters, + FunctionNode.Kind.SETTER, + functionLine, + functionBody); + + return new PropertyFunction(setIdent, function); } private static class PropertyFunction { @@ -2655,11 +2668,18 @@ loop: final List parameters = formalParameterList(); expect(RPAREN); - FunctionNode functionNode = functionBody(functionToken, name, parameters, FunctionNode.Kind.NORMAL, functionLine); + final ParserContextFunctionNode functionNode = createParserContextFunctionNode(name, functionToken, FunctionNode.Kind.NORMAL, functionLine, parameters); + lc.push(functionNode); + Block functionBody = null; + try{ + functionBody = functionBody(functionNode); + } finally { + lc.pop(functionNode); + } if (isStatement) { if (topLevel || useBlockScope()) { - functionNode = functionNode.setFlag(lc, FunctionNode.IS_DECLARED); + functionNode.setFlag(FunctionNode.IS_DECLARED); } else if (isStrictMode) { throw error(JSErrorType.SYNTAX_ERROR, AbstractParser.message("strict.no.func.decl.here"), functionToken); } else if (env._function_statement == ScriptEnvironment.FunctionStatementBehavior.ERROR) { @@ -2668,12 +2688,12 @@ loop: warning(JSErrorType.SYNTAX_ERROR, AbstractParser.message("no.func.decl.here.warn"), functionToken); } if (isArguments(name)) { - lc.setFlag(lc.getCurrentFunction(), FunctionNode.DEFINES_ARGUMENTS); + lc.getCurrentFunction().setFlag(FunctionNode.DEFINES_ARGUMENTS); } } if (isAnonymous) { - functionNode = functionNode.setFlag(lc, FunctionNode.IS_ANONYMOUS); + functionNode.setFlag(FunctionNode.IS_ANONYMOUS); } final int arity = parameters.size(); @@ -2687,7 +2707,7 @@ loop: String parameterName = parameter.getName(); if (isArguments(parameterName)) { - functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS); + functionNode.setFlag(FunctionNode.DEFINES_ARGUMENTS); } if (parametersSet.contains(parameterName)) { @@ -2705,17 +2725,26 @@ loop: } } else if (arity == 1) { if (isArguments(parameters.get(0))) { - functionNode = functionNode.setFlag(lc, FunctionNode.DEFINES_ARGUMENTS); + functionNode.setFlag(FunctionNode.DEFINES_ARGUMENTS); } } + final FunctionNode function = createFunctionNode( + functionNode, + functionToken, + name, + parameters, + FunctionNode.Kind.NORMAL, + functionLine, + functionBody); + if (isStatement) { int varFlags = VarNode.IS_STATEMENT; if (!topLevel && useBlockScope()) { // mark ES6 block functions as lexically scoped varFlags |= VarNode.IS_LET; } - final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, functionNode, varFlags); + final VarNode varNode = new VarNode(functionLine, functionToken, finish, name, function, varFlags); if (topLevel) { functionDeclarations.add(varNode); } else if (useBlockScope()) { @@ -2725,7 +2754,7 @@ loop: } } - return functionNode; + return function; } private String getDefaultValidFunctionName(final int functionLine) { @@ -2832,15 +2861,19 @@ loop: * Parse function body. * @return function node (body.) */ - private FunctionNode functionBody(final long firstToken, final IdentNode ident, final List parameters, final FunctionNode.Kind kind, final int functionLine) { - FunctionNode functionNode = null; + private Block functionBody(final ParserContextFunctionNode functionNode) { long lastToken = 0L; + ParserContextBlockNode body = null; + final long bodyToken = token; + Block functionBody; + int bodyFinish = 0; + final boolean parseBody; Object endParserState = null; try { // Create a new function block. - functionNode = newFunctionNode(firstToken, ident, parameters, kind, functionLine); + body = newBlock(); assert functionNode != null; final int functionId = functionNode.getId(); parseBody = reparsedFunction == null || functionId <= reparsedFunction.getFunctionNodeId(); @@ -2856,6 +2889,7 @@ loop: // just expression as function body final Expression expr = assignmentExpression(true); lastToken = previousToken; + functionNode.setLastToken(previousToken); assert lc.getCurrentBlock() == lc.getFunctionBody(functionNode); // EOL uses length field to store the line number final int lastFinish = Token.descPosition(lastToken) + (Token.descType(lastToken) == EOL ? 0 : Token.descLength(lastToken)); @@ -2868,7 +2902,6 @@ loop: final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr); appendStatement(returnNode); } - functionNode.setFinish(lastFinish); } else { expectDontAdvance(LBRACE); if (parseBody || !skipFunctionBody(functionNode)) { @@ -2902,25 +2935,25 @@ loop: // we'll rather just restart parsing from this well-known, friendly token instead. } } + bodyFinish = finish; + functionNode.setLastToken(token); expect(RBRACE); - functionNode.setFinish(finish); } } finally { - functionNode = restoreFunctionNode(functionNode, lastToken); + restoreBlock(body); } // NOTE: we can only do alterations to the function node after restoreFunctionNode. if (parseBody) { - functionNode = functionNode.setEndParserState(lc, endParserState); - } else if (functionNode.getBody().getStatementCount() > 0){ + functionNode.setEndParserState(endParserState); + } else if (!body.getStatements().isEmpty()){ // This is to ensure the body is empty when !parseBody but we couldn't skip parsing it (see // skipFunctionBody() for possible reasons). While it is not strictly necessary for correctness to // enforce empty bodies in nested functions that were supposed to be skipped, we do assert it as // an invariant in few places in the compiler pipeline, so for consistency's sake we'll throw away // nested bodies early if we were supposed to skip 'em. - functionNode = functionNode.setBody(null, functionNode.getBody().setStatements(null, - Collections.emptyList())); + body.setStatements(Collections.emptyList()); } if (reparsedFunction != null) { @@ -2932,20 +2965,20 @@ loop: if (data != null) { // Data can be null if when we originally parsed the file, we removed the function declaration // as it was dead code. - functionNode = functionNode.setFlags(lc, data.getFunctionFlags()); + functionNode.setFlag(data.getFunctionFlags()); // This compensates for missing markEval() in case the function contains an inner function // that contains eval(), that now we didn't discover since we skipped the inner function. if (functionNode.hasNestedEval()) { assert functionNode.hasScopeBlock(); - functionNode = functionNode.setBody(lc, functionNode.getBody().setNeedsScope(null)); + body.setFlag(Block.NEEDS_SCOPE); } } } - printAST(functionNode); - return functionNode; + functionBody = new Block(bodyToken, bodyFinish, body.getFlags(), body.getStatements()); + return functionBody; } - private boolean skipFunctionBody(final FunctionNode functionNode) { + private boolean skipFunctionBody(final ParserContextFunctionNode functionNode) { if (reparsedFunction == null) { // Not reparsing, so don't skip any function body. return false; @@ -3008,13 +3041,13 @@ loop: } } - private void addFunctionDeclarations(final FunctionNode functionNode) { + private void addFunctionDeclarations(final ParserContextFunctionNode functionNode) { VarNode lastDecl = null; for (int i = functionDeclarations.size() - 1; i >= 0; i--) { Statement decl = functionDeclarations.get(i); if (lastDecl == null && decl instanceof VarNode) { decl = lastDecl = ((VarNode)decl).setFlag(VarNode.IS_LAST_FUNCTION_DECLARATION); - lc.setFlag(functionNode, FunctionNode.HAS_FUNCTION_DECLARATIONS); + functionNode.setFlag(FunctionNode.HAS_FUNCTION_DECLARATIONS); } prependStatement(decl); } @@ -3359,29 +3392,31 @@ loop: return "'JavaScript Parsing'"; } - private static void markEval(final LexicalContext lc) { - final Iterator iter = lc.getFunctions(); + private static void markEval(final ParserContext lc) { + final Iterator iter = lc.getFunctions(); boolean flaggedCurrentFn = false; while (iter.hasNext()) { - final FunctionNode fn = iter.next(); + final ParserContextFunctionNode fn = iter.next(); if (!flaggedCurrentFn) { - lc.setFlag(fn, FunctionNode.HAS_EVAL); + fn.setFlag(FunctionNode.HAS_EVAL); flaggedCurrentFn = true; } else { - lc.setFlag(fn, FunctionNode.HAS_NESTED_EVAL); + fn.setFlag(FunctionNode.HAS_NESTED_EVAL); } + final ParserContextBlockNode body = lc.getFunctionBody(fn); // NOTE: it is crucial to mark the body of the outer function as needing scope even when we skip // parsing a nested function. functionBody() contains code to compensate for the lack of invoking // this method when the parser skips a nested function. - lc.setBlockNeedsScope(lc.getFunctionBody(fn)); + body.setFlag(Block.NEEDS_SCOPE); + fn.setFlag(FunctionNode.HAS_SCOPE_BLOCK); } } private void prependStatement(final Statement statement) { - lc.prependStatement(statement); + lc.prependStatementToCurrentNode(statement); } private void appendStatement(final Statement statement) { - lc.appendStatement(statement); + lc.appendStatementToCurrentNode(statement); } } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContext.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContext.java new file mode 100644 index 00000000000..7a90adfb0dc --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContext.java @@ -0,0 +1,334 @@ +/* + * Copyright (c) 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.parser; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import jdk.nashorn.internal.ir.Statement; + +/** + * A class that tracks the current lexical context of node visitation as a stack of {@code ParserContextNode} nodes. Has special + * methods to retrieve useful subsets of the context. + * + * This is implemented with a primitive array and a stack pointer, because it really makes a difference + * performance wise. None of the collection classes were optimal + */ + +class ParserContext { + + private ParserContextNode[] stack; + private int sp; + + private static final int INITIAL_DEPTH = 16; + + /** + * Constructs a ParserContext, + * initializes the stack + */ + public ParserContext(){ + this.sp = 0; + this.stack = new ParserContextNode[INITIAL_DEPTH]; + } + + /** + * Pushes a new block on top of the context, making it the innermost open block. + * @param node the new node + * @return The node that was pushed + */ + public T push(final T node) { + assert !contains(node); + if (sp == stack.length) { + final ParserContextNode[] newStack = new ParserContextNode[sp * 2]; + System.arraycopy(stack, 0, newStack, 0, sp); + stack = newStack; + } + stack[sp] = node; + sp++; + + return node; + } + + /** + * The topmost node on the stack + * @return The topmost node on the stack + */ + public ParserContextNode peek() { + return stack[sp - 1]; + } + + /** + * Removes and returns the topmost Node from the stack. + * @param node The node expected to be popped, used for sanity check + * @return The removed node + */ + public T pop(final T node) { + --sp; + @SuppressWarnings("unchecked") + final T popped = (T)stack[sp]; + stack[sp] = null; + assert node == popped; + + return popped; + } + + /** + * Tests if a node is on the stack. + * @param node The node to test + * @return true if stack contains node, false otherwise + */ + public boolean contains(final ParserContextNode node) { + for (int i = 0; i < sp; i++) { + if (stack[i] == node) { + return true; + } + } + return false; + } + + /** + * Returns the topmost {@link ParserContextBreakableNode} on the stack, null if none on stack + * @return Returns the topmost {@link ParserContextBreakableNode} on the stack, null if none on stack + */ + private ParserContextBreakableNode getBreakable() { + for (final NodeIterator iter = new NodeIterator<>(ParserContextBreakableNode.class, getCurrentFunction()); iter.hasNext(); ) { + final ParserContextBreakableNode next = iter.next(); + if (next.isBreakableWithoutLabel()) { + return next; + } + } + return null; + } + + + + /** + * Find the breakable node corresponding to this label. + * @param labelName name of the label to search for. If null, the closest breakable node will be returned + * unconditionally, e.g. a while loop with no label + * @return closest breakable node + */ + public ParserContextBreakableNode getBreakable(final String labelName) { + if (labelName != null) { + final ParserContextLabelNode foundLabel = findLabel(labelName); + if (foundLabel != null) { + // iterate to the nearest breakable to the foundLabel + ParserContextBreakableNode breakable = null; + for (final NodeIterator iter = new NodeIterator<>(ParserContextBreakableNode.class, foundLabel); iter.hasNext(); ) { + breakable = iter.next(); + } + return breakable; + } + return null; + } else { + return getBreakable(); + } + } + + /** + * Returns the loop node of the current loop, or null if not inside a loop + * @return loop noder + */ + public ParserContextLoopNode getCurrentLoop() { + final Iterator iter = new NodeIterator<>(ParserContextLoopNode.class, getCurrentFunction()); + return iter.hasNext() ? iter.next() : null; + } + + private ParserContextLoopNode getContinueTo() { + return getCurrentLoop(); + } + + /** + * Find the continue target node corresponding to this label. + * @param labelName label name to search for. If null the closest loop node will be returned unconditionally, e.g. a + * while loop with no label + * @return closest continue target node + */ + public ParserContextLoopNode getContinueTo(final String labelName) { + if (labelName != null) { + final ParserContextLabelNode foundLabel = findLabel(labelName); + if (foundLabel != null) { + // iterate to the nearest loop to the foundLabel + ParserContextLoopNode loop = null; + for (final NodeIterator iter = new NodeIterator<>(ParserContextLoopNode.class, foundLabel); iter.hasNext(); ) { + loop = iter.next(); + } + return loop; + } + return null; + } + return getContinueTo(); + } + + /** + * Get the function body of a function node on the stack. + * This will trigger an assertion if node isn't present + * @param functionNode function node + * @return body of function node + */ + public ParserContextBlockNode getFunctionBody(final ParserContextFunctionNode functionNode) { + for (int i = sp - 1; i >= 0 ; i--) { + if (stack[i] == functionNode) { + return (ParserContextBlockNode)stack[i + 1]; + } + } + throw new AssertionError(functionNode.getName() + " not on context stack"); + } + + /** + * Check the stack for a given label node by name + * @param name name of the label + * @return LabelNode if found, null otherwise + */ + public ParserContextLabelNode findLabel(final String name) { + for (final Iterator iter = new NodeIterator<>(ParserContextLabelNode.class, getCurrentFunction()); iter.hasNext(); ) { + final ParserContextLabelNode next = iter.next(); + if (next.getLabelName().equals(name)) { + return next; + } + } + return null; + } + + /** + * Prepends a statement to the current node. + * @param statement The statement to prepend + */ + public void prependStatementToCurrentNode(final Statement statement) { + assert statement != null; + stack[sp - 1].prependStatement(statement); + } + + /** + * Appends a statement to the current Node. + * @param statement The statement to append + */ + public void appendStatementToCurrentNode(final Statement statement) { + assert statement != null; + stack[sp - 1].appendStatement(statement); + } + + /** + * Returns the innermost function in the context. + * @return the innermost function in the context. + */ + public ParserContextFunctionNode getCurrentFunction() { + for (int i = sp - 1; i >= 0; i--) { + if (stack[i] instanceof ParserContextFunctionNode) { + return (ParserContextFunctionNode) stack[i]; + } + } + return null; + } + + /** + * Returns an iterator over all blocks in the context, with the top block (innermost lexical context) first. + * @return an iterator over all blocks in the context. + */ + public Iterator getBlocks() { + return new NodeIterator<>(ParserContextBlockNode.class); + } + + /** + * Returns the innermost block in the context. + * @return the innermost block in the context. + */ + public ParserContextBlockNode getCurrentBlock() { + return getBlocks().next(); + } + + /** + * The last statement added to the context + * @return The last statement added to the context + */ + public Statement getLastStatement() { + if (sp == 0) { + return null; + } + final ParserContextNode top = stack[sp - 1]; + final int s = top.getStatements().size(); + return s == 0 ? null : top.getStatements().get(s - 1); + } + + /** + * Returns an iterator over all functions in the context, with the top (innermost open) function first. + * @return an iterator over all functions in the context. + */ + public Iterator getFunctions() { + return new NodeIterator<>(ParserContextFunctionNode.class); + } + + private class NodeIterator implements Iterator { + private int index; + private T next; + private final Class clazz; + private ParserContextNode until; + + NodeIterator(final Class clazz) { + this(clazz, null); + } + + NodeIterator(final Class clazz, final ParserContextNode until) { + this.index = sp - 1; + this.clazz = clazz; + this.until = until; + this.next = findNext(); + } + + @Override + public boolean hasNext() { + return next != null; + } + + @Override + public T next() { + if (next == null) { + throw new NoSuchElementException(); + } + final T lnext = next; + next = findNext(); + return lnext; + } + + @SuppressWarnings("unchecked") + private T findNext() { + for (int i = index; i >= 0; i--) { + final Object node = stack[i]; + if (node == until) { + return null; + } + if (clazz.isAssignableFrom(node.getClass())) { + index = i - 1; + return (T)node; + } + } + return null; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBaseNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBaseNode.java new file mode 100644 index 00000000000..262bc541d2e --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBaseNode.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 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.parser; + +import java.util.ArrayList; +import java.util.List; +import jdk.nashorn.internal.ir.Statement; + +/** + * Base class for parser context nodes + */ +abstract class ParserContextBaseNode implements ParserContextNode { + /** + * Flags for this node + */ + protected int flags; + + private List statements; + + /** + * Constructor + */ + public ParserContextBaseNode() { + this.statements = new ArrayList<>(); + } + + /** + * @return The flags for this node + */ + @Override + public int getFlags() { + return flags; + } + + /** + * Returns a single flag + * @param flag + * @return A single flag + */ + protected int getFlag(final int flag) { + return (flags & flag); + } + + /** + * @param flag + * @return the new flags + */ + @Override + public int setFlag(final int flag) { + flags |= flag; + return flags; + } + + /** + * @return The list of statements that belongs to this node + */ + @Override + public List getStatements() { + return statements; + } + + /** + * @param statements + */ + @Override + public void setStatements(final List statements) { + this.statements = statements; + } + + /** + * Adds a Statement at the end of the Statementlist + * @param statement The statement to add + */ + @Override + public void appendStatement(final Statement statement) { + this.statements.add(statement); + } + + /** + * Adds a statement at the begining of the statementlist + * @param statement The statement to add + */ + @Override + public void prependStatement(final Statement statement) { + this.statements.add(0, statement); + } +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBlockNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBlockNode.java new file mode 100644 index 00000000000..e87bd1293a9 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBlockNode.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 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.parser; + +/** + * A ParserContextNode that represents a block that is currently being parsed + */ +class ParserContextBlockNode extends ParserContextBaseNode implements ParserContextBreakableNode { + + private final long token; + + /** + * Constructs a ParserContextBlockNode + * + * @param token The first token of the block + */ + public ParserContextBlockNode(final long token) { + this.token = token; + } + + @Override + public boolean isBreakableWithoutLabel() { + return false; + } + + /** + * Get token + * @return The first token of the block + */ + public long getToken() { + return token; + } + +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBreakableNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBreakableNode.java new file mode 100644 index 00000000000..cb0b46d9c14 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextBreakableNode.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 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.parser; + +import jdk.nashorn.internal.ir.BreakNode; + +/** + * An interface that is implemented by ParserContextNodes that can + * contain a {@link BreakNode} + */ +interface ParserContextBreakableNode extends ParserContextNode { + + /** + * Returns true if not i breakable without label, false otherwise + * @return Returns true if not i breakable without label, false otherwise + */ + boolean isBreakableWithoutLabel(); +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java new file mode 100644 index 00000000000..5418caff915 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 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.parser; + +import java.util.List; +import jdk.nashorn.internal.codegen.Namespace; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.IdentNode; + +/** + * ParserContextNode that represents a function that is currently being parsed + */ +class ParserContextFunctionNode extends ParserContextBaseNode { + + /** Function name */ + private final String name; + + /** Function identifier node */ + private final IdentNode ident; + + /** Name space for function */ + private final Namespace namespace; + + /** Line number for function declaration */ + private final int line; + + /** Function node kind, see {@link FunctionNode#Kind} */ + private final FunctionNode.Kind kind; + + /** List of parameter identifiers for function */ + private final List parameters; + + /** Token for function start */ + private final long token; + + /** Last function token */ + private long lastToken; + + /** Opaque node for parser end state, see {@link Parser} */ + private Object endParserState; + + /** + * @param token The token for the function + * @param ident External function name + * @param name Internal name of the function + * @param namespace Function's namespace + * @param line The source line of the function + * @param kind Function kind + * @param parameters The parameters of the function + */ + public ParserContextFunctionNode(final long token, final IdentNode ident, final String name, final Namespace namespace, final int line, final FunctionNode.Kind kind, final List parameters) { + this.ident = ident; + this.namespace = namespace; + this.line = line; + this.kind = kind; + this.name = name; + this.parameters = parameters; + this.token = token; + } + + /** + * @return Internal name of the function + */ + public String getName() { + return name; + } + + /** + * @return The external identifier for the function + */ + public IdentNode getIdent() { + return ident; + } + + /** + * + * @return true if function is the program function + */ + public boolean isProgram() { + return getFlag(FunctionNode.IS_PROGRAM) != 0; + } + + /** + * @return if function in strict mode + */ + public boolean isStrict() { + return getFlag(FunctionNode.IS_STRICT) != 0; + } + + /** + * @return true if the function has nested evals + */ + public boolean hasNestedEval() { + return getFlag(FunctionNode.HAS_NESTED_EVAL) != 0; + } + + /** + * Returns true if any of the blocks in this function create their own scope. + * @return true if any of the blocks in this function create their own scope. + */ + public boolean hasScopeBlock() { + return getFlag(FunctionNode.HAS_SCOPE_BLOCK) != 0; + } + + /** + * Create a unique name in the namespace of this FunctionNode + * @param base prefix for name + * @return base if no collision exists, otherwise a name prefix with base + */ + public String uniqueName(final String base) { + return namespace.uniqueName(base); + } + + /** + * @return line number of the function + */ + public int getLineNumber() { + return line; + } + + /** + * @return The kind if function + */ + public FunctionNode.Kind getKind() { + return kind; + } + + /** + * Get parameters + * @return The parameters of the function + */ + public List getParameters() { + return parameters; + } + + /** + * Set last token + * @param token New last token + */ + public void setLastToken(final long token) { + this.lastToken = token; + + } + + /** + * @return lastToken Function's last token + */ + public long getLastToken() { + return lastToken; + } + + /** + * Returns the ParserState of when the parsing of this function was ended + * @return endParserState The end parser state + */ + public Object getEndParserState() { + return endParserState; + } + + /** + * Sets the ParserState of when the parsing of this function was ended + * @param endParserState The end parser state + */ + public void setEndParserState(final Object endParserState) { + this.endParserState = endParserState; + } + + /** + * Returns the if of this function + * @return The function id + */ + public int getId() { + return isProgram() ? -1 : Token.descPosition(token); + } +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLabelNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLabelNode.java new file mode 100644 index 00000000000..a5179107b3c --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLabelNode.java @@ -0,0 +1,52 @@ +/** +/* + * Copyright (c) 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.parser; + +/** + * ParserContextNode that represents a LabelNode + */ +class ParserContextLabelNode extends ParserContextBaseNode { + + /** Name for label */ + private final String name; + + /** + * Constructor + * + * @param name The name of the label + */ + public ParserContextLabelNode(final String name) { + this.name = name; + } + + /** + * Returns the name of the label + * @return name of label + */ + public String getLabelName() { + return name; + } +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLoopNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLoopNode.java new file mode 100644 index 00000000000..31e4b3ad5ea --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextLoopNode.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 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.parser; + +/** + * A ParserContextNode that represents a loop that is being parsed + */ +class ParserContextLoopNode extends ParserContextBaseNode implements ParserContextBreakableNode { + + @Override + public boolean isBreakableWithoutLabel() { + return true; + } + +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextNode.java new file mode 100644 index 00000000000..d44a106b8dd --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextNode.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 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.parser; + +import java.util.List; +import jdk.nashorn.internal.ir.Statement; + +/** + * Used for keeping state when needed in the parser. + */ +interface ParserContextNode { + /** + * @return The flags for this node + */ + public int getFlags(); + + /** + * @param flag The flag to set + * @return All current flags after update + */ + public int setFlag(final int flag); + + /** + * @return The list of statements that belongs to this node + */ + public List getStatements(); + + /** + * @param statements The statement list + */ + public void setStatements(final List statements); + + /** + * Adds a Statement at the end of the Statementlist + * @param statement The statement to add + */ + public void appendStatement(final Statement statement); + + /** + * Adds a statement at the begining of the statementlist + * @param statement The statement to add + */ + public void prependStatement(final Statement statement); + +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextSwitchNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextSwitchNode.java new file mode 100644 index 00000000000..8c9c96a6a2d --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextSwitchNode.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 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.parser; + +/** + * A ParserContextNode that represents a SwithcNode that is currently being parsed + */ +class ParserContextSwitchNode extends ParserContextBaseNode implements ParserContextBreakableNode { + + @Override + public boolean isBreakableWithoutLabel() { + return true; + } +} diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java index e77c204afbb..ff543a36290 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java @@ -483,7 +483,7 @@ public final class Context { final int cacheSize = env._class_cache_size; if (cacheSize > 0) { - classCache = new ClassCache(cacheSize); + classCache = new ClassCache(this, cacheSize); } if (env._persistent_cache) { @@ -1261,17 +1261,23 @@ public final class Context { * Cache for compiled script classes. */ @SuppressWarnings("serial") - private static class ClassCache extends LinkedHashMap { + @Logger(name="classcache") + private static class ClassCache extends LinkedHashMap implements Loggable { private final int size; private final ReferenceQueue> queue; + private final DebugLogger log; - ClassCache(final int size) { + ClassCache(final Context context, final int size) { super(size, 0.75f, true); this.size = size; this.queue = new ReferenceQueue<>(); + this.log = initLogger(context); } void cache(final Source source, final Class clazz) { + if (log.isEnabled()) { + log.info("Caching ", source, " in class cache"); + } put(source, new ClassReference(clazz, queue, source)); } @@ -1283,9 +1289,28 @@ public final class Context { @Override public ClassReference get(final Object key) { for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { - remove(ref.source); + final Source source = ref.source; + if (log.isEnabled()) { + log.info("Evicting ", source, " from class cache."); + } + remove(source); } - return super.get(key); + + final ClassReference ref = super.get(key); + if (ref != null && log.isEnabled()) { + log.info("Retrieved class reference for ", ref.source, " from class cache"); + } + return ref; + } + + @Override + public DebugLogger initLogger(final Context context) { + return context.getLogger(getClass()); + } + + @Override + public DebugLogger getLogger() { + return log; } } From bd3defa4bb79eedcc1ba2dd91bed46f8da7a669a Mon Sep 17 00:00:00 2001 From: Andreas Gabrielsson Date: Tue, 14 Oct 2014 16:11:07 +0200 Subject: [PATCH 07/11] 8060471: DynamicLinker.getLinkedCallSiteLocation() is called even when logger is disabled, and it creates a stacktrace. This contributes unnecessarily to compile time Reviewed-by: lagergren, attila --- .../jdk/nashorn/internal/runtime/GlobalConstants.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/GlobalConstants.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/GlobalConstants.java index 6099a70bce8..c6f9b9644d7 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/GlobalConstants.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/GlobalConstants.java @@ -31,7 +31,6 @@ import static jdk.nashorn.internal.lookup.Lookup.MH; import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT; import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.getProgramPoint; import static jdk.nashorn.internal.runtime.logging.DebugLogger.quote; - import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.SwitchPoint; @@ -328,7 +327,9 @@ public final class GlobalConstants implements Loggable { } if (!acc.mayRetry()) { - log.info("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation()); + if (log.isEnabled()) { + log.fine("*** SET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation()); + } return null; } @@ -404,7 +405,9 @@ public final class GlobalConstants implements Loggable { } if (acc.hasBeenInvalidated() || acc.guardFailed()) { - log.fine("*** GET: Giving up on " + quote(name) + " - retry count has exceeded"); + if (log.isEnabled()) { + log.info("*** GET: Giving up on " + quote(name) + " - retry count has exceeded " + DynamicLinker.getLinkedCallSiteLocation()); + } return null; } From 5d93e4cb2561a1ae6941ca11c6a66023f97504c7 Mon Sep 17 00:00:00 2001 From: Attila Szegedi Date: Wed, 15 Oct 2014 15:57:46 +0200 Subject: [PATCH 08/11] 8060242: Compile-time expression evaluator was not seeing into ArrayBufferViews Reviewed-by: hannesw, lagergren, sundar --- .../nashorn/internal/codegen/TypeEvaluator.java | 17 ++++++++--------- .../internal/objects/ArrayBufferView.java | 3 +-- .../internal/objects/NativeFloat32Array.java | 6 ++++++ .../internal/objects/NativeFloat64Array.java | 6 ++++++ .../internal/objects/NativeInt16Array.java | 16 ++++++++++++++++ .../internal/objects/NativeInt32Array.java | 16 ++++++++++++++++ .../internal/objects/NativeInt8Array.java | 16 ++++++++++++++++ .../internal/objects/NativeUint16Array.java | 16 ++++++++++++++++ .../internal/objects/NativeUint32Array.java | 13 ++++++++++++- .../internal/objects/NativeUint8Array.java | 16 ++++++++++++++++ .../objects/NativeUint8ClampedArray.java | 16 ++++++++++++++++ .../runtime/arrays/ContinuousArrayData.java | 7 +++++++ .../internal/runtime/arrays/IntArrayData.java | 7 +------ .../internal/runtime/arrays/LongArrayData.java | 7 +------ .../runtime/arrays/NumberArrayData.java | 7 +------ .../runtime/arrays/ObjectArrayData.java | 7 +------ 16 files changed, 140 insertions(+), 36 deletions(-) diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java index 11a23ecd3a1..319866b5478 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java @@ -35,6 +35,7 @@ import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.Optimistic; +import jdk.nashorn.internal.objects.ArrayBufferView; import jdk.nashorn.internal.objects.NativeArray; import jdk.nashorn.internal.runtime.FindProperty; import jdk.nashorn.internal.runtime.JSType; @@ -204,16 +205,14 @@ final class TypeEvaluator { if (expr instanceof IndexNode) { final IndexNode indexNode = (IndexNode)expr; final Object base = evaluateSafely(indexNode.getBase()); - if(!(base instanceof NativeArray)) { - // We only know how to deal with NativeArray. TODO: maybe manage buffers too - return null; + if(base instanceof NativeArray || base instanceof ArrayBufferView) { + // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their underlying + // array storage, not based on values of individual elements. Thus, a LongArrayData will throw UOE for every + // optimistic int linkage attempt, even if the long value being returned in the first invocation would be + // representable as int. That way, we can presume that the array's optimistic type is the most optimistic + // type for which an element getter has a chance of executing successfully. + return ((ScriptObject)base).getArray().getOptimisticType(); } - // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their underlying - // array storage, not based on values of individual elements. Thus, a LongArrayData will throw UOE for every - // optimistic int linkage attempt, even if the long value being returned in the first invocation would be - // representable as int. That way, we can presume that the array's optimistic type is the most optimistic - // type for which an element getter has a chance of executing successfully. - return ((NativeArray)base).getArray().getOptimisticType(); } return null; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/ArrayBufferView.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/ArrayBufferView.java index efcc3ddcf60..e33fac3bb11 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/ArrayBufferView.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/ArrayBufferView.java @@ -31,7 +31,6 @@ 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; @@ -46,7 +45,7 @@ import jdk.nashorn.internal.runtime.arrays.ArrayData; import jdk.nashorn.internal.runtime.arrays.TypedArrayData; @ScriptClass("ArrayBufferView") -abstract class ArrayBufferView extends ScriptObject { +public abstract class ArrayBufferView extends ScriptObject { private final NativeArrayBuffer buffer; private final int byteOffset; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat32Array.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat32Array.java index 049d8fa8142..e5ef0b5a5fd 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat32Array.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat32Array.java @@ -26,6 +26,7 @@ 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; @@ -140,6 +141,11 @@ public final class NativeFloat32Array extends ArrayBufferView { return getElem(index); } + @Override + public double getDoubleOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public Object getObject(final int index) { return getDouble(index); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat64Array.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat64Array.java index 79268cc51a9..1ad61b277c2 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat64Array.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeFloat64Array.java @@ -26,6 +26,7 @@ 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; @@ -140,6 +141,11 @@ public final class NativeFloat64Array extends ArrayBufferView { return getElem(index); } + @Override + public double getDoubleOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public Object getObject(final int index) { return getDouble(index); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt16Array.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt16Array.java index 72e24a20418..061488487f7 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt16Array.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt16Array.java @@ -26,6 +26,7 @@ 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; @@ -123,16 +124,31 @@ public final class NativeInt16Array extends ArrayBufferView { return getElem(index); } + @Override + public int getIntOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public long getLong(final int index) { return getInt(index); } + @Override + public long getLongOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public double getDouble(final int index) { return getInt(index); } + @Override + public double getDoubleOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public Object getObject(final int index) { return getInt(index); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt32Array.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt32Array.java index 56b3106fb44..5074dc68539 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt32Array.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt32Array.java @@ -26,6 +26,7 @@ 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; @@ -121,16 +122,31 @@ public final class NativeInt32Array extends ArrayBufferView { return getElem(index); } + @Override + public int getIntOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public long getLong(final int index) { return getInt(index); } + @Override + public long getLongOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public double getDouble(final int index) { return getInt(index); } + @Override + public double getDoubleOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public Object getObject(final int index) { return getInt(index); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt8Array.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt8Array.java index 68e6b0bc1ca..319168c0522 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt8Array.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeInt8Array.java @@ -26,6 +26,7 @@ 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; @@ -121,16 +122,31 @@ public final class NativeInt8Array extends ArrayBufferView { return getElem(index); } + @Override + public int getIntOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public long getLong(final int index) { return getInt(index); } + @Override + public long getLongOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public double getDouble(final int index) { return getInt(index); } + @Override + public double getDoubleOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public Object getObject(final int index) { return getInt(index); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint16Array.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint16Array.java index d049c9331f3..7f8a192399a 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint16Array.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint16Array.java @@ -26,6 +26,7 @@ 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; @@ -127,16 +128,31 @@ public final class NativeUint16Array extends ArrayBufferView { return getElem(index); } + @Override + public int getIntOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public long getLong(final int index) { return getInt(index); } + @Override + public long getLongOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public double getDouble(final int index) { return getInt(index); } + @Override + public double getDoubleOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public Object getObject(final int index) { return getInt(index); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint32Array.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint32Array.java index 2ae2be056ae..4772b977dd8 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint32Array.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint32Array.java @@ -26,6 +26,7 @@ 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,7 +129,7 @@ public final class NativeUint32Array extends ArrayBufferView { @Override public Class getElementType() { - return int.class; + return long.class; } @Override @@ -141,11 +142,21 @@ public final class NativeUint32Array extends ArrayBufferView { return getElem(index); } + @Override + public long getLongOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public double getDouble(final int index) { return getLong(index); } + @Override + public double getDoubleOptimistic(final int index, final int programPoint) { + return getLong(index); + } + @Override public Object getObject(final int index) { return getLong(index); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8Array.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8Array.java index 1067baa58d4..be7eb368b49 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8Array.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8Array.java @@ -26,6 +26,7 @@ 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; @@ -127,16 +128,31 @@ public final class NativeUint8Array extends ArrayBufferView { return getElem(index); } + @Override + public int getIntOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public long getLong(final int index) { return getInt(index); } + @Override + public long getLongOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public double getDouble(final int index) { return getInt(index); } + @Override + public double getDoubleOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public Object getObject(final int index) { return getInt(index); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java index ffd0f14cfec..125ac0a2128 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeUint8ClampedArray.java @@ -28,6 +28,7 @@ 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; @@ -157,16 +158,31 @@ public final class NativeUint8ClampedArray extends ArrayBufferView { return getElem(index); } + @Override + public int getIntOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public long getLong(final int index) { return getInt(index); } + @Override + public long getLongOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public double getDouble(final int index) { return getInt(index); } + @Override + public double getDoubleOptimistic(final int index, final int programPoint) { + return getElem(index); + } + @Override public Object getObject(final int index) { return getInt(index); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java index dfa6133db1d..8724d64ac2a 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ContinuousArrayData.java @@ -30,6 +30,7 @@ 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; @@ -37,6 +38,7 @@ import java.lang.invoke.SwitchPoint; import jdk.internal.dynalink.CallSiteDescriptor; import jdk.internal.dynalink.linker.GuardedInvocation; import jdk.internal.dynalink.linker.LinkRequest; +import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.lookup.Lookup; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; @@ -120,6 +122,11 @@ public abstract class ContinuousArrayData extends ArrayData { */ public abstract Class getElementType(); + @Override + public Type getOptimisticType() { + return Type.typeFor(getElementType()); + } + /** * Look up a continuous array element getter * @param get getter, sometimes combined with a has check that throws CCE on failure for relink diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IntArrayData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IntArrayData.java index fece3a6ef64..0792b6b6fa0 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IntArrayData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/IntArrayData.java @@ -26,10 +26,10 @@ 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; -import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptRuntime; @@ -256,11 +256,6 @@ final class IntArrayData extends ContinuousArrayData implements IntElements { return convert(Double.class).set(index, value, strict); } - @Override - public Type getOptimisticType() { - return Type.INT; - } - @Override public int getInt(final int index) { return array[index]; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/LongArrayData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/LongArrayData.java index 31a4484e2ae..f41ee15a818 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/LongArrayData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/LongArrayData.java @@ -27,10 +27,10 @@ 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; -import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptRuntime; @@ -186,11 +186,6 @@ final class LongArrayData extends ContinuousArrayData implements IntOrLongElemen return convert(Double.class).set(index, value, strict); } - @Override - public Type getOptimisticType() { - return Type.LONG; - } - private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), LongArrayData.class, "getElem", long.class, int.class).methodHandle(); private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), LongArrayData.class, "setElem", void.class, int.class, long.class).methodHandle(); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java index 4d27f51df3c..2c57208f496 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/NumberArrayData.java @@ -28,10 +28,10 @@ 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; -import jdk.nashorn.internal.codegen.types.Type; /** * Implementation of {@link ArrayData} as soon as a double has been @@ -166,11 +166,6 @@ final class NumberArrayData extends ContinuousArrayData implements NumericElemen return this; } - @Override - public Type getOptimisticType() { - return Type.NUMBER; - } - private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), NumberArrayData.class, "getElem", double.class, int.class).methodHandle(); private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), NumberArrayData.class, "setElem", void.class, int.class, double.class).methodHandle(); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java index 001ea61c7c8..379ba6e438b 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/arrays/ObjectArrayData.java @@ -26,10 +26,10 @@ 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; -import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.ScriptRuntime; @@ -160,11 +160,6 @@ final class ObjectArrayData extends ContinuousArrayData { return this; } - @Override - public Type getOptimisticType() { - return Type.OBJECT; - } - private static final MethodHandle HAS_GET_ELEM = specialCall(MethodHandles.lookup(), ObjectArrayData.class, "getElem", Object.class, int.class).methodHandle(); private static final MethodHandle SET_ELEM = specialCall(MethodHandles.lookup(), ObjectArrayData.class, "setElem", void.class, int.class, Object.class).methodHandle(); From f571816c7988beda630259fa020b2bdfbcacde35 Mon Sep 17 00:00:00 2001 From: Attila Szegedi Date: Wed, 15 Oct 2014 16:00:21 +0200 Subject: [PATCH 09/11] 8060241: Immediately invoked function expressions cause lot of deoptimization Reviewed-by: hannesw, lagergren --- .../internal/codegen/TypeEvaluator.java | 45 ++++++++++++++----- .../RecompilableScriptFunctionData.java | 16 +++++++ 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java index 319866b5478..b3c0dd8a915 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java @@ -29,9 +29,12 @@ import static jdk.nashorn.internal.runtime.Property.NOT_CONFIGURABLE; import static jdk.nashorn.internal.runtime.Property.NOT_ENUMERABLE; import static jdk.nashorn.internal.runtime.Property.NOT_WRITABLE; +import java.lang.invoke.MethodType; import jdk.nashorn.internal.codegen.types.Type; import jdk.nashorn.internal.ir.AccessNode; +import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.Expression; +import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.IndexNode; import jdk.nashorn.internal.ir.Optimistic; @@ -40,6 +43,8 @@ import jdk.nashorn.internal.objects.NativeArray; import jdk.nashorn.internal.runtime.FindProperty; import jdk.nashorn.internal.runtime.JSType; import jdk.nashorn.internal.runtime.Property; +import jdk.nashorn.internal.runtime.RecompilableScriptFunctionData; +import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.ScriptRuntime; @@ -48,6 +53,13 @@ import jdk.nashorn.internal.runtime.ScriptRuntime; * Used during recompilation. */ final class TypeEvaluator { + /** + * Type signature for invocation of functions without parameters: we must pass (callee, this) of type + * (ScriptFunction, Object) respectively. We also use Object as the return type (we must pass something, + * but it'll be ignored; it can't be void, though). + */ + private static final MethodType EMPTY_INVOCATION_TYPE = MethodType.methodType(Object.class, ScriptFunction.class, Object.class); + private final Compiler compiler; private final ScriptObject runtimeScope; @@ -191,28 +203,39 @@ final class TypeEvaluator { return null; } return getPropertyType(runtimeScope, ((IdentNode)expr).getName()); - } - - if (expr instanceof AccessNode) { + } else if (expr instanceof AccessNode) { final AccessNode accessNode = (AccessNode)expr; final Object base = evaluateSafely(accessNode.getBase()); if (!(base instanceof ScriptObject)) { return null; } return getPropertyType((ScriptObject)base, accessNode.getProperty()); - } - - if (expr instanceof IndexNode) { + } else if (expr instanceof IndexNode) { final IndexNode indexNode = (IndexNode)expr; final Object base = evaluateSafely(indexNode.getBase()); if(base instanceof NativeArray || base instanceof ArrayBufferView) { - // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their underlying - // array storage, not based on values of individual elements. Thus, a LongArrayData will throw UOE for every - // optimistic int linkage attempt, even if the long value being returned in the first invocation would be - // representable as int. That way, we can presume that the array's optimistic type is the most optimistic - // type for which an element getter has a chance of executing successfully. + // NOTE: optimistic array getters throw UnwarrantedOptimismException based on the type of their + // underlying array storage, not based on values of individual elements. Thus, a LongArrayData will + // throw UOE for every optimistic int linkage attempt, even if the long value being returned in the + // first invocation would be representable as int. That way, we can presume that the array's optimistic + // type is the most optimistic type for which an element getter has a chance of executing successfully. return ((ScriptObject)base).getArray().getOptimisticType(); } + } else if (expr instanceof CallNode) { + // Currently, we'll only try to guess the return type of immediately invoked function expressions with no + // parameters, that is (function() { ... })(). We could do better, but these are all heuristics and we can + // gradually introduce them as needed. An easy one would be to do the same for .call(this) idiom. + final CallNode callExpr = (CallNode)expr; + final Expression fnExpr = callExpr.getFunction(); + if (fnExpr instanceof FunctionNode) { + final FunctionNode fn = (FunctionNode)fnExpr; + if (callExpr.getArgs().isEmpty()) { + final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(fn.getId()); + if (data != null) { + return Type.typeFor(data.getReturnType(EMPTY_INVOCATION_TYPE, runtimeScope)); + } + } + } } return null; diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java index f3ad23393de..2e0b33bf69f 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/RecompilableScriptFunctionData.java @@ -676,6 +676,22 @@ public final class RecompilableScriptFunctionData extends ScriptFunctionData imp return addCode(lookup(fnInit).asType(toType), fnInit.getInvalidatedProgramPoints(), callSiteType, fnInit.getFlags()); } + /** + * Returns the return type of a function specialization for particular parameter types.
+ * Be aware that the way this is implemented, it forces full materialization (compilation and installation) of + * code for that specialization. + * @param callSiteType the parameter types at the call site. It must include the mandatory {@code callee} and + * {@code this} parameters, so it needs to start with at least {@code ScriptFunction.class} and + * {@code Object.class} class. Since the return type of the function is calculated from the code itself, it is + * irrelevant and should be set to {@code Object.class}. + * @param runtimeScope a current runtime scope. Can be null but when it's present it will be used as a source of + * current runtime values that can improve the compiler's type speculations (and thus reduce the need for later + * recompilations) if the specialization is not already present and thus needs to be freshly compiled. + * @return the return type of the function specialization. + */ + public Class getReturnType(final MethodType callSiteType, final ScriptObject runtimeScope) { + return getBest(callSiteType, runtimeScope, CompiledFunction.NO_FUNCTIONS).type().returnType(); + } @Override synchronized CompiledFunction getBest(final MethodType callSiteType, final ScriptObject runtimeScope, final Collection forbidden) { From 6427d424c8bd9876a940dc2d009b299271d33f85 Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan Date: Wed, 15 Oct 2014 19:50:03 +0530 Subject: [PATCH 10/11] 8060688: Nashorn: Generated script class name fails --verify-code for names with special chars Reviewed-by: jlaskey, hannesw --- .../nashorn/internal/codegen/Compiler.java | 21 ++++++- nashorn/test/script/nosecurity/JDK-8060688.js | 56 +++++++++++++++++++ .../internal/codegen/CompilerTest.java | 1 + .../runtime/TrustedScriptEngineTest.java | 25 +++++++++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 nashorn/test/script/nosecurity/JDK-8060688.js diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java index 3a0cc6bfb4b..80846e139dd 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java @@ -410,10 +410,29 @@ public final class Compiler implements Loggable { baseName = baseName + installer.getUniqueScriptId(); } - final String mangled = NameCodec.encode(baseName); + // ASM's bytecode verifier does not allow JVM allowed safe escapes using '\' as escape char. + // While ASM accepts such escapes for method names, field names, it enforces Java identifier + // for class names. Workaround that ASM bug here by replacing JVM 'dangerous' chars with '_' + // rather than safe encoding using '\'. + final String mangled = env._verify_code? replaceDangerChars(baseName) : NameCodec.encode(baseName); return mangled != null ? mangled : baseName; } + private static final String DANGEROUS_CHARS = "\\/.;:$[]<>"; + private static String replaceDangerChars(final String name) { + final int len = name.length(); + final StringBuilder buf = new StringBuilder(); + for (int i = 0; i < len; i++) { + final char ch = name.charAt(i); + if (DANGEROUS_CHARS.indexOf(ch) != -1) { + buf.append('_'); + } else { + buf.append(ch); + } + } + return buf.toString(); + } + private String firstCompileUnitName() { final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE). append('/'). diff --git a/nashorn/test/script/nosecurity/JDK-8060688.js b/nashorn/test/script/nosecurity/JDK-8060688.js new file mode 100644 index 00000000000..8fe578146bb --- /dev/null +++ b/nashorn/test/script/nosecurity/JDK-8060688.js @@ -0,0 +1,56 @@ +/* + * Copyright (c) 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. + */ + +/** + * JDK-8060688: Nashorn: Generated script class name fails --verify-code for names with special chars + * + * @test + * @run + */ + +var NashornEngineFactory = Java.type("jdk.nashorn.api.scripting.NashornScriptEngineFactory"); +var ScriptEngine = Java.type("javax.script.ScriptEngine"); +var ScriptContext = Java.type("javax.script.ScriptContext"); + +var factory = new NashornEngineFactory(); + +var e = factory.getScriptEngine("--verify-code"); + +function evalAndCheck(code) { + try { + e.eval(code); + } catch (exp) { + exp.printStackTrace(); + } +} + +// check default name +evalAndCheck("var a = 3"); +// check few names with special chars +var scontext = e.context; +scontext.setAttribute(ScriptEngine.FILENAME, "", ScriptContext.ENGINE_SCOPE); +evalAndCheck("var h = 'hello'"); +scontext.setAttribute(ScriptEngine.FILENAME, "[myscript]", ScriptContext.ENGINE_SCOPE); +evalAndCheck("var foo = 'world'"); +scontext.setAttribute(ScriptEngine.FILENAME, ";/\\$.", ScriptContext.ENGINE_SCOPE); +evalAndCheck("var foo = 'helloworld'"); diff --git a/nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java b/nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java index 9eb801fdd33..dadeb15f64e 100644 --- a/nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java +++ b/nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java @@ -72,6 +72,7 @@ public class CompilerTest { options.set("print.parse", true); options.set("scripting", true); options.set("const.as.var", true); + options.set("verify.code", true); final ErrorManager errors = new ErrorManager() { @Override diff --git a/nashorn/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java b/nashorn/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java index 8225cc083b4..f7a18dcd6b2 100644 --- a/nashorn/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java +++ b/nashorn/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java @@ -325,4 +325,29 @@ public class TrustedScriptEngineTest { ); assertEquals(ret, 10, "Parsed and executed OK"); } + + @Test + public void evalDefaultFileNameTest() throws ScriptException { + final NashornScriptEngineFactory fac = new NashornScriptEngineFactory(); + final ScriptEngine engine = fac.getScriptEngine(new String[] { "--verify-code=true" }); + // default FILENAME being "" make sure generated code bytecode verifies. + engine.eval("var a = 3;"); + } + + @Test + public void evalFileNameWithSpecialCharsTest() throws ScriptException { + final NashornScriptEngineFactory fac = new NashornScriptEngineFactory(); + final ScriptEngine engine = fac.getScriptEngine(new String[] { "--verify-code=true" }); + final ScriptContext ctxt = new SimpleScriptContext(); + // use file name with "dangerous" chars. + ctxt.setAttribute(ScriptEngine.FILENAME, "", ScriptContext.ENGINE_SCOPE); + engine.eval("var a = 3;"); + ctxt.setAttribute(ScriptEngine.FILENAME, "[myscript]", ScriptContext.ENGINE_SCOPE); + engine.eval("var h = 'hello';"); + ctxt.setAttribute(ScriptEngine.FILENAME, ";/\\$.", ScriptContext.ENGINE_SCOPE); + engine.eval("var foo = 'world';"); + // name used by jjs shell tool for the interactive mode + ctxt.setAttribute(ScriptEngine.FILENAME, "", ScriptContext.ENGINE_SCOPE); + engine.eval("var foo = 'world';"); + } } From 6da7daa4c2783c04a6f9d881f2f1a97a60bc0190 Mon Sep 17 00:00:00 2001 From: Attila Szegedi Date: Thu, 16 Oct 2014 11:34:31 +0200 Subject: [PATCH 11/11] 8061113: Boolean used as optimistic call return type Reviewed-by: hannesw, sundar --- .../internal/codegen/TypeEvaluator.java | 9 ++++- nashorn/test/script/basic/JDK-8061113.js | 35 +++++++++++++++++++ .../test/script/basic/JDK-8061113.js.EXPECTED | 1 + 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 nashorn/test/script/basic/JDK-8061113.js create mode 100644 nashorn/test/script/basic/JDK-8061113.js.EXPECTED diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java index b3c0dd8a915..4f3bc07f1db 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/TypeEvaluator.java @@ -232,7 +232,14 @@ final class TypeEvaluator { if (callExpr.getArgs().isEmpty()) { final RecompilableScriptFunctionData data = compiler.getScriptFunctionData(fn.getId()); if (data != null) { - return Type.typeFor(data.getReturnType(EMPTY_INVOCATION_TYPE, runtimeScope)); + final Type returnType = Type.typeFor(data.getReturnType(EMPTY_INVOCATION_TYPE, runtimeScope)); + if (returnType == Type.BOOLEAN) { + // We don't have optimistic booleans. In fact, optimistic call sites getting back boolean + // currently deoptimize all the way to Object. + return Type.OBJECT; + } + assert returnType == Type.INT || returnType == Type.LONG || returnType == Type.NUMBER || returnType == Type.OBJECT; + return returnType; } } } diff --git a/nashorn/test/script/basic/JDK-8061113.js b/nashorn/test/script/basic/JDK-8061113.js new file mode 100644 index 00000000000..1cd8b35bc4a --- /dev/null +++ b/nashorn/test/script/basic/JDK-8061113.js @@ -0,0 +1,35 @@ +/* + * Copyright (c) 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. + */ + +/** + * JDK-8061113: Boolean used as optimistic call return type + * + * @test + * @run + */ + +function testcase() { + var a = {x:0}; + return (function () {return a.x === 0})(); +} +print(testcase()); diff --git a/nashorn/test/script/basic/JDK-8061113.js.EXPECTED b/nashorn/test/script/basic/JDK-8061113.js.EXPECTED new file mode 100644 index 00000000000..27ba77ddaf6 --- /dev/null +++ b/nashorn/test/script/basic/JDK-8061113.js.EXPECTED @@ -0,0 +1 @@ +true