8047959: bindings created for declarations in eval code are not mutable
Reviewed-by: jlaskey, attila
--- a/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Mon Jun 23 18:32:11 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/CodeGenerator.java Tue Jun 24 19:43:44 2014 +0530
@@ -211,6 +211,9 @@
* by reflection in class installation */
private final Compiler compiler;
+ /** Is the current code submitted by 'eval' call? */
+ private final boolean evalCode;
+
/** Call site flags given to the code generator to be used for all generated call sites */
private final int callSiteFlags;
@@ -265,6 +268,7 @@
CodeGenerator(final Compiler compiler, final int[] continuationEntryPoints) {
super(new CodeGeneratorLexicalContext());
this.compiler = compiler;
+ this.evalCode = compiler.getSource().isEvalCode();
this.continuationEntryPoints = continuationEntryPoints;
this.callSiteFlags = compiler.getScriptEnvironment()._callsite_flags;
this.log = initLogger(compiler.getContext());
@@ -291,6 +295,14 @@
}
/**
+ * Are we generating code for 'eval' code?
+ * @return true if currently compiled code is 'eval' code.
+ */
+ boolean isEvalCode() {
+ return evalCode;
+ }
+
+ /**
* Load an identity node
*
* @param identNode an identity node to load
--- a/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Mon Jun 23 18:32:11 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Tue Jun 24 19:43:44 2014 +0530
@@ -62,6 +62,8 @@
/** call site flags to be used for invocations */
private final int callSiteFlags;
+ /** are we creating this field object from 'eval' code? */
+ private final boolean evalCode;
/**
* Constructor
@@ -88,7 +90,7 @@
FieldObjectCreator(final CodeGenerator codegen, final List<MapTuple<T>> tuples, final boolean isScope, final boolean hasArguments) {
super(codegen, tuples, isScope, hasArguments);
this.callSiteFlags = codegen.getCallSiteFlags();
-
+ this.evalCode = codegen.isEvalCode();
countFields();
findClass();
}
@@ -153,7 +155,7 @@
@Override
protected PropertyMap makeMap() {
assert propertyMap == null : "property map already initialized";
- propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount);
+ propertyMap = newMapCreator(fieldObjectClass).makeFieldMap(hasArguments(), fieldCount, paddedFieldCount, evalCode);
return propertyMap;
}
--- a/nashorn/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Mon Jun 23 18:32:11 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Tue Jun 24 19:43:44 2014 +0530
@@ -584,7 +584,7 @@
}
setCompilerConstantAsObject(functionNode, CompilerConstants.THIS);
- // NOTE: coarse-grained. If we wanted to solve it completely precisely,
+ // TODO: coarse-grained. If we wanted to solve it completely precisely,
// we'd also need to push/pop its type when handling WithNode (so that
// it can go back to undefined after a 'with' block.
if(functionNode.hasScopeBlock() || functionNode.needsParentScope()) {
--- a/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java Mon Jun 23 18:32:11 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/codegen/MapCreator.java Tue Jun 24 19:43:44 2014 +0530
@@ -63,13 +63,13 @@
/**
* Constructs a property map based on a set of fields.
*
- * @param hasArguments does the created object have an "arguments" property
+ * @param hasArguments does the created object have an "arguments" property
* @param fieldCount Number of fields in use.
- * @param fieldMaximum Number of fields available.
- *
+ * @param fieldMaximum Number of fields available.
+ * @param evalCode is this property map created for 'eval' code?
* @return New map populated with accessor properties.
*/
- PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum) {
+ PropertyMap makeFieldMap(final boolean hasArguments, final int fieldCount, final int fieldMaximum, final boolean evalCode) {
final List<Property> properties = new ArrayList<>();
assert tuples != null;
@@ -79,7 +79,7 @@
final Class<?> initialType = tuple.getValueType();
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
- final int flags = getPropertyFlags(symbol, hasArguments);
+ final int flags = getPropertyFlags(symbol, hasArguments, evalCode);
final Property property = new AccessorProperty(
key,
flags,
@@ -104,7 +104,7 @@
//TODO initial type is object here no matter what. Is that right?
if (symbol != null && !isValidArrayIndex(getArrayIndex(key))) {
- final int flags = getPropertyFlags(symbol, hasArguments);
+ final int flags = getPropertyFlags(symbol, hasArguments, false);
properties.add(
new SpillProperty(
key,
@@ -124,7 +124,7 @@
*
* @return flags to use for fields
*/
- static int getPropertyFlags(final Symbol symbol, final boolean hasArguments) {
+ static int getPropertyFlags(final Symbol symbol, final boolean hasArguments, final boolean evalCode) {
int flags = 0;
if (symbol.isParam()) {
@@ -135,7 +135,13 @@
flags |= Property.HAS_ARGUMENTS;
}
- if (symbol.isScope()) {
+ // See ECMA 5.1 10.5 Declaration Binding Instantiation.
+ // Step 2 If code is eval code, then let configurableBindings
+ // be true else let configurableBindings be false.
+ // We have to make vars, functions declared in 'eval' code
+ // configurable. But vars, functions from any other code is
+ // not configurable.
+ if (symbol.isScope() && !evalCode) {
flags |= Property.NOT_CONFIGURABLE;
}
--- a/nashorn/src/jdk/nashorn/internal/objects/Global.java Mon Jun 23 18:32:11 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/objects/Global.java Tue Jun 24 19:43:44 2014 +0530
@@ -883,7 +883,7 @@
final Global global = Global.instance();
final ScriptObject scope = self instanceof ScriptObject ? (ScriptObject)self : global;
- return global.getContext().eval(scope, str.toString(), callThis, location, Boolean.TRUE.equals(strict));
+ return global.getContext().eval(scope, str.toString(), callThis, location, Boolean.TRUE.equals(strict), true);
}
/**
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java Mon Jun 23 18:32:11 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java Tue Jun 24 19:43:44 2014 +0530
@@ -560,12 +560,29 @@
* @param callThis "this" to be passed to the evaluated code
* @param location location of the eval call
* @param strict is this {@code eval} call from a strict mode code?
+ * @return the return value of the {@code eval}
+ */
+ public Object eval(final ScriptObject initialScope, final String string,
+ final Object callThis, final Object location, final boolean strict) {
+ return eval(initialScope, string, callThis, location, strict, false);
+ }
+
+ /**
+ * Entry point for {@code eval}
+ *
+ * @param initialScope The scope of this eval call
+ * @param string Evaluated code as a String
+ * @param callThis "this" to be passed to the evaluated code
+ * @param location location of the eval call
+ * @param strict is this {@code eval} call from a strict mode code?
+ * @param evalCall is this called from "eval" builtin?
*
* @return the return value of the {@code eval}
*/
- public Object eval(final ScriptObject initialScope, final String string, final Object callThis, final Object location, final boolean strict) {
+ public Object eval(final ScriptObject initialScope, final String string,
+ final Object callThis, final Object location, final boolean strict, final boolean evalCall) {
final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString();
- final Source source = sourceFor(file, string);
+ final Source source = sourceFor(file, string, evalCall);
final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval?
final Global global = Context.getGlobal();
ScriptObject scope = initialScope;
--- a/nashorn/src/jdk/nashorn/internal/runtime/Source.java Mon Jun 23 18:32:11 2014 +0200
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Source.java Tue Jun 24 19:43:44 2014 +0530
@@ -142,29 +142,34 @@
long lastModified();
char[] array();
+
+ boolean isEvalCode();
}
private static class RawData implements Data {
private final char[] array;
+ private final boolean evalCode;
private int hash;
- private RawData(final char[] array) {
+ private RawData(final char[] array, final boolean evalCode) {
this.array = Objects.requireNonNull(array);
+ this.evalCode = evalCode;
}
- private RawData(final String source) {
+ private RawData(final String source, final boolean evalCode) {
this.array = Objects.requireNonNull(source).toCharArray();
+ this.evalCode = evalCode;
}
private RawData(final Reader reader) throws IOException {
- this(readFully(reader));
+ this(readFully(reader), false);
}
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
- h = hash = Arrays.hashCode(array);
+ h = hash = Arrays.hashCode(array) ^ (evalCode? 1 : 0);
}
return h;
}
@@ -175,7 +180,8 @@
return true;
}
if (obj instanceof RawData) {
- return Arrays.equals(array, ((RawData)obj).array);
+ final RawData other = (RawData)obj;
+ return Arrays.equals(array, other.array) && evalCode == other.evalCode;
}
return false;
}
@@ -206,6 +212,10 @@
}
+ @Override
+ public boolean isEvalCode() {
+ return evalCode;
+ }
}
private static class URLData implements Data {
@@ -287,6 +297,11 @@
return array;
}
+ @Override
+ public boolean isEvalCode() {
+ return false;
+ }
+
boolean isDeferred() {
return array == null;
}
@@ -373,11 +388,23 @@
*
* @param name source name
* @param content contents as char array
+ * @param isEval does this represent code from 'eval' call?
+ * @return source instance
+ */
+ public static Source sourceFor(final String name, final char[] content, final boolean isEval) {
+ return new Source(name, baseName(name), new RawData(content, isEval));
+ }
+
+ /**
+ * Returns a Source instance
+ *
+ * @param name source name
+ * @param content contents as char array
*
* @return source instance
*/
public static Source sourceFor(final String name, final char[] content) {
- return new Source(name, baseName(name), new RawData(content));
+ return sourceFor(name, content, false);
}
/**
@@ -385,11 +412,22 @@
*
* @param name source name
* @param content contents as string
+ * @param isEval does this represent code from 'eval' call?
+ * @return source instance
+ */
+ public static Source sourceFor(final String name, final String content, final boolean isEval) {
+ return new Source(name, baseName(name), new RawData(content, isEval));
+ }
+
+ /**
+ * Returns a Source instance
*
+ * @param name source name
+ * @param content contents as string
* @return source instance
*/
public static Source sourceFor(final String name, final String content) {
- return new Source(name, baseName(name), new RawData(content));
+ return sourceFor(name, content, false);
}
/**
@@ -555,6 +593,15 @@
}
/**
+ * Returns whether this source was submitted via 'eval' call or not.
+ *
+ * @return true if this source represents code submitted via 'eval'
+ */
+ public boolean isEvalCode() {
+ return data.isEvalCode();
+ }
+
+ /**
* Find the beginning of the line containing position.
* @param position Index to offending token.
* @return Index of first character of line.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047959.js Tue Jun 24 19:43:44 2014 +0530
@@ -0,0 +1,59 @@
+/*
+ * 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-8047959: bindings created for declarations in eval code are not mutable
+ *
+ * @test
+ * @run
+ */
+
+eval("var x=10;");
+print('delete x? ' + delete x);
+print('typeof x = ' + typeof x);
+
+eval("function f() {}");
+print('delete f? ' + delete f);
+print('typeof f = ' + typeof f);
+
+var foo = 223;
+print('delete foo? ' + delete foo);
+print('typeof foo = ' + typeof foo);
+
+function func() {}
+print('delete func? ' + delete func);
+print('typeof func = ' + typeof func);
+
+eval("var foo = 33;");
+print("delete foo? " + delete foo);
+print("typeof foo? " + typeof foo);
+print("foo = " + foo);
+
+var x = "global";
+(function(){
+ eval("var x='local'");
+ print("x in function = "+ x);
+ print("delete x? = " + delete x);
+ print("x after delete = " + x);
+})();
+print("x = " + x);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8047959.js.EXPECTED Tue Jun 24 19:43:44 2014 +0530
@@ -0,0 +1,15 @@
+delete x? false
+typeof x = number
+delete f? true
+typeof f = undefined
+delete foo? false
+typeof foo = number
+delete func? false
+typeof func = function
+delete foo? false
+typeof foo? number
+foo = 33
+x in function = local
+delete x? = true
+x after delete = global
+x = global