8098578: Global scope is not accessible with indirect load call

Tue, 16 Jun 2015 18:26:25 +0530

author
sundar
date
Tue, 16 Jun 2015 18:26:25 +0530
changeset 1412
a8706b5e6a2e
parent 1411
77ff49b11306
child 1413
fb91ff186894

8098578: Global scope is not accessible with indirect load call
Reviewed-by: attila, hannesw

src/jdk/nashorn/internal/objects/Global.java file | annotate | diff | comparison | revisions
src/jdk/nashorn/internal/runtime/Context.java file | annotate | diff | comparison | revisions
test/script/basic/JDK-8098578.js file | annotate | diff | comparison | revisions
     1.1 --- a/src/jdk/nashorn/internal/objects/Global.java	Tue Jun 16 13:25:41 2015 +0200
     1.2 +++ b/src/jdk/nashorn/internal/objects/Global.java	Tue Jun 16 18:26:25 2015 +0530
     1.3 @@ -1502,26 +1502,53 @@
     1.4      }
     1.5  
     1.6      /**
     1.7 -     * Global load implementation - Nashorn extension
     1.8 +     * Global load implementation - Nashorn extension.
     1.9       *
    1.10 -     * @param self    scope
    1.11 -     * @param source  source to load
    1.12 +     * <p>
    1.13 +     * load builtin loads the given script. Script source can be a URL or a File
    1.14 +     * or a script object with name and script properties. Evaluated code gets
    1.15 +     * global object "this" and uses global object as scope for evaluation.
    1.16 +     * </p>
    1.17 +     * <p>
    1.18 +     * If self is undefined or null or global, then global object is used
    1.19 +     * as scope as well as "this" for the evaluated code. If self is any other
    1.20 +     * object, then it is indirect load call. With indirect load call, the
    1.21 +     * properties of scope are available to evaluated script as variables. Also,
    1.22 +     * global scope properties are accessible. Any var, function definition in
    1.23 +     * evaluated script goes into an object that is not accessible to user scripts.
    1.24 +     * </p>
    1.25 +     * Thus the indirect load call is equivalent to the following:
    1.26 +     * <pre>
    1.27 +     * <code>
    1.28 +     * (function (scope, source) {
    1.29 +     *    with(scope) {
    1.30 +     *        eval(&lt;script_from_source&gt;);
    1.31 +     *    }
    1.32 +     * })(self, source);
    1.33 +     * </code>
    1.34 +     * </pre>
    1.35       *
    1.36 -     * @return result of load (undefined)
    1.37 +     * @param self    scope to use for the script evaluation
    1.38 +     * @param source  script source
    1.39 +     *
    1.40 +     * @return result of load (may be undefined)
    1.41       *
    1.42       * @throws IOException if source could not be read
    1.43       */
    1.44      public static Object load(final Object self, final Object source) throws IOException {
    1.45          final Global global = Global.instanceFrom(self);
    1.46 -        final ScriptObject scope = self instanceof ScriptObject ? (ScriptObject)self : global;
    1.47 -        return global.getContext().load(scope, source);
    1.48 +        return global.getContext().load(self, source);
    1.49      }
    1.50  
    1.51      /**
    1.52 -     * Global loadWithNewGlobal implementation - Nashorn extension
    1.53 +     * Global loadWithNewGlobal implementation - Nashorn extension.
    1.54       *
    1.55 -     * @param self scope
    1.56 -     * @param args from plus (optional) arguments to be passed to the loaded script
    1.57 +     * loadWithNewGlobal builtin loads the given script from a URL or a File
    1.58 +     * or a script object with name and script properties. Evaluated code gets
    1.59 +     * new global object "this" and uses that new global object as scope for evaluation.
    1.60 +     *
    1.61 +     * @param self self This value is ignored by this function
    1.62 +     * @param args optional arguments to be passed to the loaded script
    1.63       *
    1.64       * @return result of load (may be undefined)
    1.65       *
     2.1 --- a/src/jdk/nashorn/internal/runtime/Context.java	Tue Jun 16 13:25:41 2015 +0200
     2.2 +++ b/src/jdk/nashorn/internal/runtime/Context.java	Tue Jun 16 18:26:25 2015 +0530
     2.3 @@ -777,7 +777,7 @@
     2.4       *
     2.5       * @throws IOException if source cannot be found or loaded
     2.6       */
     2.7 -    public Object load(final ScriptObject scope, final Object from) throws IOException {
     2.8 +    public Object load(final Object scope, final Object from) throws IOException {
     2.9          final Object src = from instanceof ConsString ? from.toString() : from;
    2.10          Source source = null;
    2.11  
    2.12 @@ -829,7 +829,42 @@
    2.13          }
    2.14  
    2.15          if (source != null) {
    2.16 -            return evaluateSource(source, scope, scope);
    2.17 +            if (scope instanceof ScriptObject && ((ScriptObject)scope).isScope()) {
    2.18 +                final ScriptObject sobj = (ScriptObject)scope;
    2.19 +                // passed object is a script object
    2.20 +                // Global is the only user accessible scope ScriptObject
    2.21 +                assert sobj.isGlobal() : "non-Global scope object!!";
    2.22 +                return evaluateSource(source, sobj, sobj);
    2.23 +            } else if (scope == null || scope == UNDEFINED) {
    2.24 +                // undefined or null scope. Use current global instance.
    2.25 +                final Global global = getGlobal();
    2.26 +                return evaluateSource(source, global, global);
    2.27 +            } else {
    2.28 +                /*
    2.29 +                 * Arbitrary object passed for scope.
    2.30 +                 * Indirect load that is equivalent to:
    2.31 +                 *
    2.32 +                 *    (function(scope, source) {
    2.33 +                 *        with (scope) {
    2.34 +                 *            eval(<script_from_source>);
    2.35 +                 *        }
    2.36 +                 *    })(scope, source);
    2.37 +                 */
    2.38 +                final Global global = getGlobal();
    2.39 +                // Create a new object. This is where all declarations
    2.40 +                // (var, function) from the evaluated code go.
    2.41 +                // make global to be its __proto__ so that global
    2.42 +                // definitions are accessible to the evaluated code.
    2.43 +                final ScriptObject evalScope = newScope(global);
    2.44 +
    2.45 +                // finally, make a WithObject around user supplied scope object
    2.46 +                // so that it's properties are accessible as variables.
    2.47 +                final ScriptObject withObj = ScriptRuntime.openWith(evalScope, scope);
    2.48 +
    2.49 +                // evaluate given source with 'withObj' as scope
    2.50 +                // but use global object as "this".
    2.51 +                return evaluateSource(source, withObj, global);
    2.52 +            }
    2.53          }
    2.54  
    2.55          throw typeError("cant.load.script", ScriptRuntime.safeToString(from));
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/test/script/basic/JDK-8098578.js	Tue Jun 16 18:26:25 2015 +0530
     3.3 @@ -0,0 +1,107 @@
     3.4 +/*
     3.5 + * Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved.
     3.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3.7 + * 
     3.8 + * This code is free software; you can redistribute it and/or modify it
     3.9 + * under the terms of the GNU General Public License version 2 only, as
    3.10 + * published by the Free Software Foundation.
    3.11 + * 
    3.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    3.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    3.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    3.15 + * version 2 for more details (a copy is included in the LICENSE file that
    3.16 + * accompanied this code).
    3.17 + * 
    3.18 + * You should have received a copy of the GNU General Public License version
    3.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    3.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    3.21 + * 
    3.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    3.23 + * or visit www.oracle.com if you need additional information or have any
    3.24 + * questions.
    3.25 + */
    3.26 +
    3.27 +/**
    3.28 + * JDK-8098578: Global scope is not accessible with indirect load call
    3.29 + *
    3.30 + * @test
    3.31 + * @run
    3.32 + */
    3.33 +
    3.34 +var obj = { foo: 343 };
    3.35 +var global = this;
    3.36 +var x = 434;
    3.37 +
    3.38 +// indirect load call
    3.39 +var res = load.call(obj, {
    3.40 +   name: "t.js",
    3.41 +   // global is accessible. All declarations go into
    3.42 +   // intermediate inaccessible scope. "this" is global
    3.43 +   // User's passed object's properties are accessible
    3.44 +   // as variables.
    3.45 +   script: "foo -= 300; var bar = x; Assert.assertTrue(bar == 434); function func() {}; this"
    3.46 +})
    3.47 +
    3.48 +// 'this' for the evaluated code is global
    3.49 +Assert.assertTrue(res === global);
    3.50 +
    3.51 +// properties of passed object are accessible in evaluated code
    3.52 +Assert.assertTrue(obj.foo == 43);
    3.53 +
    3.54 +// vars, functions definined in evaluated code don't go into passed object
    3.55 +Assert.assertTrue(typeof obj.bar == "undefined");
    3.56 +Assert.assertTrue(typeof obj.func == "undefined");
    3.57 +
    3.58 +// vars, functions definined in evaluated code don't go leak into global
    3.59 +Assert.assertTrue(typeof bar == "undefined");
    3.60 +Assert.assertTrue(typeof func == "undefined");
    3.61 +Assert.assertTrue(typeof foo == "undefined");
    3.62 +
    3.63 +var res = load.call(undefined, {
    3.64 +    name: "t1.js",
    3.65 +    // still global is accessible and 'this' is global
    3.66 +    script: "Assert.assertTrue(x == 434); this"
    3.67 +});
    3.68 +
    3.69 +// indirect load with 'undefined' this is same as as direct load
    3.70 +// or load on global itself.
    3.71 +Assert.assertTrue(res === global);
    3.72 +
    3.73 +// indirect load with 'undefined' this is same as as direct load
    3.74 +// or load on global itself.
    3.75 +var res = load.call(null, {
    3.76 +    name: "t2.js",
    3.77 +    // still global is accessible and 'this' is global
    3.78 +    script: "Assert.assertTrue(x == 434); this"
    3.79 +});
    3.80 +Assert.assertTrue(res === global);
    3.81 +
    3.82 +// indirect load with mirror object
    3.83 +var mirror = loadWithNewGlobal({
    3.84 +    name: "t3.js",
    3.85 +    script: "({ foo: 'hello', x: Math.PI })"
    3.86 +});
    3.87 +
    3.88 +var res = load.call(mirror, {
    3.89 +    name: "t4.js",
    3.90 +    script: "Assert.assertTrue(foo == 'hello'); Assert.assertTrue(x == Math.PI); this"
    3.91 +});
    3.92 +Assert.assertTrue(res === global);
    3.93 +
    3.94 +// indirect load on non-script object, non-mirror results in TypeError
    3.95 +function tryLoad(obj) {
    3.96 +    try {
    3.97 +        load.call(obj, {
    3.98 +            name: "t5.js", script: "this"
    3.99 +        });
   3.100 +        throw new Error("should thrown TypeError for: " + obj);
   3.101 +    } catch (e if TypeError) {}
   3.102 +}
   3.103 +
   3.104 +tryLoad("hello");
   3.105 +tryLoad(Math.E);
   3.106 +tryLoad(true);
   3.107 +tryLoad(false);
   3.108 +
   3.109 +// indirect load of a large script
   3.110 +load.call({}, __DIR__ + "JDK-8098807-payload.js");

mercurial