8098578: Global scope is not accessible with indirect load call
Reviewed-by: attila, hannesw
This commit is contained in:
parent
e87ce70fa1
commit
2ea5c82229
@ -1502,26 +1502,53 @@ public final class Global extends ScriptObject implements Scope {
|
||||
}
|
||||
|
||||
/**
|
||||
* Global load implementation - Nashorn extension
|
||||
* Global load implementation - Nashorn extension.
|
||||
*
|
||||
* @param self scope
|
||||
* @param source source to load
|
||||
* <p>
|
||||
* load builtin loads the given script. Script source can be a URL or a File
|
||||
* or a script object with name and script properties. Evaluated code gets
|
||||
* global object "this" and uses global object as scope for evaluation.
|
||||
* </p>
|
||||
* <p>
|
||||
* If self is undefined or null or global, then global object is used
|
||||
* as scope as well as "this" for the evaluated code. If self is any other
|
||||
* object, then it is indirect load call. With indirect load call, the
|
||||
* properties of scope are available to evaluated script as variables. Also,
|
||||
* global scope properties are accessible. Any var, function definition in
|
||||
* evaluated script goes into an object that is not accessible to user scripts.
|
||||
* </p>
|
||||
* Thus the indirect load call is equivalent to the following:
|
||||
* <pre>
|
||||
* <code>
|
||||
* (function (scope, source) {
|
||||
* with(scope) {
|
||||
* eval(<script_from_source>);
|
||||
* }
|
||||
* })(self, source);
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @return result of load (undefined)
|
||||
* @param self scope to use for the script evaluation
|
||||
* @param source script source
|
||||
*
|
||||
* @return result of load (may be undefined)
|
||||
*
|
||||
* @throws IOException if source could not be read
|
||||
*/
|
||||
public static Object load(final Object self, final Object source) throws IOException {
|
||||
final Global global = Global.instanceFrom(self);
|
||||
final ScriptObject scope = self instanceof ScriptObject ? (ScriptObject)self : global;
|
||||
return global.getContext().load(scope, source);
|
||||
return global.getContext().load(self, source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Global loadWithNewGlobal implementation - Nashorn extension
|
||||
* Global loadWithNewGlobal implementation - Nashorn extension.
|
||||
*
|
||||
* @param self scope
|
||||
* @param args from plus (optional) arguments to be passed to the loaded script
|
||||
* loadWithNewGlobal builtin loads the given script from a URL or a File
|
||||
* or a script object with name and script properties. Evaluated code gets
|
||||
* new global object "this" and uses that new global object as scope for evaluation.
|
||||
*
|
||||
* @param self self This value is ignored by this function
|
||||
* @param args optional arguments to be passed to the loaded script
|
||||
*
|
||||
* @return result of load (may be undefined)
|
||||
*
|
||||
|
@ -777,7 +777,7 @@ public final class Context {
|
||||
*
|
||||
* @throws IOException if source cannot be found or loaded
|
||||
*/
|
||||
public Object load(final ScriptObject scope, final Object from) throws IOException {
|
||||
public Object load(final Object scope, final Object from) throws IOException {
|
||||
final Object src = from instanceof ConsString ? from.toString() : from;
|
||||
Source source = null;
|
||||
|
||||
@ -829,7 +829,42 @@ public final class Context {
|
||||
}
|
||||
|
||||
if (source != null) {
|
||||
return evaluateSource(source, scope, scope);
|
||||
if (scope instanceof ScriptObject && ((ScriptObject)scope).isScope()) {
|
||||
final ScriptObject sobj = (ScriptObject)scope;
|
||||
// passed object is a script object
|
||||
// Global is the only user accessible scope ScriptObject
|
||||
assert sobj.isGlobal() : "non-Global scope object!!";
|
||||
return evaluateSource(source, sobj, sobj);
|
||||
} else if (scope == null || scope == UNDEFINED) {
|
||||
// undefined or null scope. Use current global instance.
|
||||
final Global global = getGlobal();
|
||||
return evaluateSource(source, global, global);
|
||||
} else {
|
||||
/*
|
||||
* Arbitrary object passed for scope.
|
||||
* Indirect load that is equivalent to:
|
||||
*
|
||||
* (function(scope, source) {
|
||||
* with (scope) {
|
||||
* eval(<script_from_source>);
|
||||
* }
|
||||
* })(scope, source);
|
||||
*/
|
||||
final Global global = getGlobal();
|
||||
// Create a new object. This is where all declarations
|
||||
// (var, function) from the evaluated code go.
|
||||
// make global to be its __proto__ so that global
|
||||
// definitions are accessible to the evaluated code.
|
||||
final ScriptObject evalScope = newScope(global);
|
||||
|
||||
// finally, make a WithObject around user supplied scope object
|
||||
// so that it's properties are accessible as variables.
|
||||
final ScriptObject withObj = ScriptRuntime.openWith(evalScope, scope);
|
||||
|
||||
// evaluate given source with 'withObj' as scope
|
||||
// but use global object as "this".
|
||||
return evaluateSource(source, withObj, global);
|
||||
}
|
||||
}
|
||||
|
||||
throw typeError("cant.load.script", ScriptRuntime.safeToString(from));
|
||||
|
107
nashorn/test/script/basic/JDK-8098578.js
Normal file
107
nashorn/test/script/basic/JDK-8098578.js
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2015 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-8098578: Global scope is not accessible with indirect load call
|
||||
*
|
||||
* @test
|
||||
* @run
|
||||
*/
|
||||
|
||||
var obj = { foo: 343 };
|
||||
var global = this;
|
||||
var x = 434;
|
||||
|
||||
// indirect load call
|
||||
var res = load.call(obj, {
|
||||
name: "t.js",
|
||||
// global is accessible. All declarations go into
|
||||
// intermediate inaccessible scope. "this" is global
|
||||
// User's passed object's properties are accessible
|
||||
// as variables.
|
||||
script: "foo -= 300; var bar = x; Assert.assertTrue(bar == 434); function func() {}; this"
|
||||
})
|
||||
|
||||
// 'this' for the evaluated code is global
|
||||
Assert.assertTrue(res === global);
|
||||
|
||||
// properties of passed object are accessible in evaluated code
|
||||
Assert.assertTrue(obj.foo == 43);
|
||||
|
||||
// vars, functions definined in evaluated code don't go into passed object
|
||||
Assert.assertTrue(typeof obj.bar == "undefined");
|
||||
Assert.assertTrue(typeof obj.func == "undefined");
|
||||
|
||||
// vars, functions definined in evaluated code don't go leak into global
|
||||
Assert.assertTrue(typeof bar == "undefined");
|
||||
Assert.assertTrue(typeof func == "undefined");
|
||||
Assert.assertTrue(typeof foo == "undefined");
|
||||
|
||||
var res = load.call(undefined, {
|
||||
name: "t1.js",
|
||||
// still global is accessible and 'this' is global
|
||||
script: "Assert.assertTrue(x == 434); this"
|
||||
});
|
||||
|
||||
// indirect load with 'undefined' this is same as as direct load
|
||||
// or load on global itself.
|
||||
Assert.assertTrue(res === global);
|
||||
|
||||
// indirect load with 'undefined' this is same as as direct load
|
||||
// or load on global itself.
|
||||
var res = load.call(null, {
|
||||
name: "t2.js",
|
||||
// still global is accessible and 'this' is global
|
||||
script: "Assert.assertTrue(x == 434); this"
|
||||
});
|
||||
Assert.assertTrue(res === global);
|
||||
|
||||
// indirect load with mirror object
|
||||
var mirror = loadWithNewGlobal({
|
||||
name: "t3.js",
|
||||
script: "({ foo: 'hello', x: Math.PI })"
|
||||
});
|
||||
|
||||
var res = load.call(mirror, {
|
||||
name: "t4.js",
|
||||
script: "Assert.assertTrue(foo == 'hello'); Assert.assertTrue(x == Math.PI); this"
|
||||
});
|
||||
Assert.assertTrue(res === global);
|
||||
|
||||
// indirect load on non-script object, non-mirror results in TypeError
|
||||
function tryLoad(obj) {
|
||||
try {
|
||||
load.call(obj, {
|
||||
name: "t5.js", script: "this"
|
||||
});
|
||||
throw new Error("should thrown TypeError for: " + obj);
|
||||
} catch (e if TypeError) {}
|
||||
}
|
||||
|
||||
tryLoad("hello");
|
||||
tryLoad(Math.E);
|
||||
tryLoad(true);
|
||||
tryLoad(false);
|
||||
|
||||
// indirect load of a large script
|
||||
load.call({}, __DIR__ + "JDK-8098807-payload.js");
|
Loading…
x
Reference in New Issue
Block a user