Mercurial > hg > openjdk > jdk9 > nashorn
changeset 907:4f3468f46d20
8047959: bindings created for declarations in eval code are not mutable
Reviewed-by: jlaskey, attila
author | sundar |
---|---|
date | Tue, 24 Jun 2014 19:43:44 +0530 |
parents | 72493c5057dd |
children | cf90d5f11b98 |
files | src/jdk/nashorn/internal/codegen/CodeGenerator.java src/jdk/nashorn/internal/codegen/FieldObjectCreator.java src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java src/jdk/nashorn/internal/codegen/MapCreator.java src/jdk/nashorn/internal/objects/Global.java src/jdk/nashorn/internal/runtime/Context.java src/jdk/nashorn/internal/runtime/Source.java test/script/basic/JDK-8047959.js test/script/basic/JDK-8047959.js.EXPECTED |
diffstat | 9 files changed, 179 insertions(+), 21 deletions(-) [+] |
line wrap: on
line diff
--- a/src/jdk/nashorn/internal/codegen/CodeGenerator.java Mon Jun 23 18:32:11 2014 +0200 +++ b/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/src/jdk/nashorn/internal/codegen/FieldObjectCreator.java Mon Jun 23 18:32:11 2014 +0200 +++ b/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/src/jdk/nashorn/internal/codegen/LocalVariableTypesCalculator.java Mon Jun 23 18:32:11 2014 +0200 +++ b/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/src/jdk/nashorn/internal/codegen/MapCreator.java Mon Jun 23 18:32:11 2014 +0200 +++ b/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/src/jdk/nashorn/internal/objects/Global.java Mon Jun 23 18:32:11 2014 +0200 +++ b/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/src/jdk/nashorn/internal/runtime/Context.java Mon Jun 23 18:32:11 2014 +0200 +++ b/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/src/jdk/nashorn/internal/runtime/Source.java Mon Jun 23 18:32:11 2014 +0200 +++ b/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/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/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