1 /*
   2  * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * JDK-8006529 : Methods should not always get callee parameter, and they
  26  * should not be too eager in creation of scopes.
  27  *
  28  * @test
  29  * @run
  30  */
  31 
  32 /*
  33  * This test script depends on nashorn Compiler internals. It uses reflection
  34  * to get access to private field and many public methods of Compiler and
  35  * FunctionNode classes. Note that this is trusted code and access to such
  36  * internal package classes and methods is okay. But, if you modify any
  37  * Compiler or FunctionNode class, you may have to revisit this script.
  38  * We cannot use direct Java class (via dynalink bean linker) to Compiler
  39  * and FunctionNode because of package-access check and so reflective calls.
  40  */
  41 var Reflector           = Java.type("jdk.nashorn.test.models.Reflector");
  42 var forName             = java.lang.Class["forName(String)"];
  43 var Parser              = forName("jdk.nashorn.internal.parser.Parser").static
  44 var Compiler            = forName("jdk.nashorn.internal.codegen.Compiler").static
  45 var CompilationPhases   = forName("jdk.nashorn.internal.codegen.Compiler$CompilationPhases").static;
  46 var Context             = forName("jdk.nashorn.internal.runtime.Context").static
  47 var CodeInstaller       = forName("jdk.nashorn.internal.runtime.CodeInstaller").static
  48 var ScriptEnvironment   = forName("jdk.nashorn.internal.runtime.ScriptEnvironment").static
  49 var Source              = forName("jdk.nashorn.internal.runtime.Source").static
  50 var FunctionNode        = forName("jdk.nashorn.internal.ir.FunctionNode").static
  51 var Block               = forName("jdk.nashorn.internal.ir.Block").static
  52 var VarNode             = forName("jdk.nashorn.internal.ir.VarNode").static
  53 var ExpressionStatement = forName("jdk.nashorn.internal.ir.ExpressionStatement").static
  54 var UnaryNode           = forName("jdk.nashorn.internal.ir.UnaryNode").static
  55 var BinaryNode          = forName("jdk.nashorn.internal.ir.BinaryNode").static
  56 var ThrowErrorManager   = forName("jdk.nashorn.internal.runtime.Context$ThrowErrorManager").static
  57 var ErrorManager        = forName("jdk.nashorn.internal.runtime.ErrorManager").static
  58 var Debug               = forName("jdk.nashorn.internal.runtime.Debug").static
  59 var String              = forName("java.lang.String").static
  60 var boolean             = Java.type("boolean");
  61 
  62 var parseMethod = Parser.class.getMethod("parse");
  63 var compileMethod = Compiler.class.getMethod("compile", FunctionNode.class, CompilationPhases.class);
  64 var getBodyMethod = FunctionNode.class.getMethod("getBody");
  65 var getStatementsMethod = Block.class.getMethod("getStatements");
  66 var getInitMethod = VarNode.class.getMethod("getInit");
  67 var getExpressionMethod = ExpressionStatement.class.getMethod("getExpression")
  68 var rhsMethod = UnaryNode.class.getMethod("getExpression")
  69 var lhsMethod = BinaryNode.class.getMethod("lhs")
  70 var binaryRhsMethod = BinaryNode.class.getMethod("rhs")
  71 var debugIdMethod = Debug.class.getMethod("id", java.lang.Object.class)
  72 var compilePhases = Reflector.get(CompilationPhases.class.getField("COMPILE_UPTO_BYTECODE"), null);
  73 
  74 function invoke(m, obj) {
  75     return Reflector.invoke(m, obj);
  76 }
  77 
  78 // These are method names of methods in FunctionNode class
  79 var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'usesSelfSymbol', 'isSplit', 'hasEval', 'allVarsInScope', 'isStrict']
  80 
  81 // corresponding Method objects of FunctionNode class
  82 var functionNodeMethods = {};
  83 // initialize FunctionNode methods
  84 (function() {
  85     for (var f in allAssertionList) {
  86         var method = allAssertionList[f];
  87         functionNodeMethods[method] = FunctionNode.class.getMethod(method);
  88     }
  89 })();
  90 
  91 // returns functionNode.getBody().getStatements().get(0)
  92 function getFirstFunction(functionNode) {
  93     var f = findFunction(invoke(getBodyMethod, functionNode))
  94     if (f == null) {
  95         throw new Error();
  96     }
  97     return f;
  98 }
  99 
 100 function findFunction(node) {
 101     if(node instanceof Block) {
 102         var stmts = invoke(getStatementsMethod, node)
 103         for(var i = 0; i < stmts.size(); ++i) {
 104             var retval = findFunction(stmts.get(i))
 105             if(retval != null) {
 106                 return retval;
 107             }
 108         }
 109     } else if(node instanceof VarNode) {
 110         return findFunction(invoke(getInitMethod, node))
 111     } else if(node instanceof UnaryNode) {
 112         return findFunction(invoke(rhsMethod, node))
 113     } else if(node instanceof BinaryNode) {
 114         return findFunction(invoke(lhsMethod, node)) || findFunction(invoke(binaryRhsMethod, node))
 115     } else if(node instanceof ExpressionStatement) {
 116         return findFunction(invoke(getExpressionMethod, node))
 117     } else if(node instanceof FunctionNode) {
 118         return node
 119     }
 120 }
 121 
 122 var getContextMethod = Context.class.getMethod("getContext")
 123 var getEnvMethod = Context.class.getMethod("getEnv")
 124 
 125 var sourceForMethod = Source.class.getMethod("sourceFor", java.lang.String.class, java.lang.String.class)
 126 var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class)
 127 var CompilerConstructor = Compiler.class.getMethod("forNoInstallerCompilation", Context.class, Source.class, boolean.class);
 128 
 129 // compile(script) -- compiles a script specified as a string with its
 130 // source code, returns a jdk.nashorn.internal.ir.FunctionNode object
 131 // representing it.
 132 function compile(source, phases) {
 133     var source = sourceForMethod.invoke(null, "<no name>", source);
 134 
 135     var ctxt = getContextMethod.invoke(null);
 136     var env = getEnvMethod.invoke(ctxt);
 137 
 138     var parser   = Reflector.newInstance(ParserConstructor, env, source, ThrowErrorManager.class.newInstance());
 139     var func     = invoke(parseMethod, parser);
 140 
 141     var compiler = Reflector.invoke(CompilerConstructor, null, ctxt, source, false);
 142 
 143     return Reflector.invoke(compileMethod, compiler, func, phases);
 144 };
 145 
 146 var allAssertions = (function() {
 147     var allAssertions = {}
 148     for(var assertion in allAssertionList) {
 149         allAssertions[allAssertionList[assertion]] = true
 150     }
 151     return allAssertions;
 152 })();
 153 
 154 
 155 // test(f[, assertions...]) tests whether all the specified assertions on the
 156 // passed function node are true.
 157 function test(f) {
 158     var assertions = {}
 159     for(var i = 1; i < arguments.length; ++i) {
 160         var assertion = arguments[i]
 161         if(!allAssertions[assertion]) {
 162             throw "Unknown assertion " + assertion + " for " + f;
 163         }
 164         assertions[assertion] = true
 165     }
 166     for(var assertion in allAssertions) {
 167         var expectedValue = !!assertions[assertion]
 168         var actualValue = invoke(functionNodeMethods[assertion], f)
 169         if(actualValue !== expectedValue) {
 170             throw "Expected " + assertion + " === " + expectedValue + ", got " + actualValue + " for " + f + ":" 
 171                 + invoke(debugIdMethod, null, f);
 172         }
 173     }
 174 }
 175 
 176 // testFirstFn(script[, assertions...] tests whether all the specified
 177 // assertions are true in the first function in the given script; "script"
 178 // is a string with the source text of the script.
 179 function testFirstFn(script) {
 180     arguments[0] = getFirstFunction(compile(script, compilePhases));
 181     test.apply(null, arguments);
 182 }
 183 
 184 // ---------------------------------- ACTUAL TESTS START HERE --------------
 185 
 186 // The simplest possible functions have no attributes set
 187 testFirstFn("function f() { }")
 188 testFirstFn("function f(x) { x }")
 189 
 190 // A function referencing a global needs parent scope, and it needs callee
 191 // (because parent scope is passed through callee)
 192 testFirstFn("function f() { x }", 'needsCallee', 'needsParentScope')
 193 
 194 // A function referencing "arguments" will have to be vararg. It also needs
 195 // the callee, as it needs to fill out "arguments.callee".
 196 testFirstFn("function f() { arguments }", 'needsCallee', 'isVarArg')
 197 
 198 // A function referencing "arguments" will have to be vararg. If it is
 199 // strict, it will not have to have a callee, though.
 200 testFirstFn("function f() {'use strict'; arguments }", 'isVarArg', 'isStrict')
 201 
 202 // A function defining "arguments" as a parameter will not be vararg.
 203 testFirstFn("function f(arguments) { arguments }")
 204 
 205 // A function defining "arguments" as a nested function will not be vararg.
 206 testFirstFn("function f() { function arguments() {}; arguments; }")
 207 
 208 // A function defining "arguments" as a local variable will be vararg.
 209 testFirstFn("function f() { var arguments; arguments; }", 'isVarArg', 'needsCallee')
 210 
 211 // A self-referencing function defined as a statement doesn't need a self
 212 // symbol, as it'll rather obtain itself from the parent scope.
 213 testFirstFn("function f() { f() }", 'needsCallee', 'needsParentScope')
 214 
 215 // A self-referencing function defined as an expression needs a self symbol,
 216 // as it can't obtain itself from the parent scope.
 217 testFirstFn("(function f() { f() })", 'needsCallee', 'usesSelfSymbol')
 218 
 219 // A child function accessing parent's variable triggers the need for scope
 220 // in parent
 221 testFirstFn("(function f() { var x; function g() { x } })", 'hasScopeBlock')
 222 
 223 // A child function accessing parent's parameter triggers the need for scope
 224 // in parent
 225 testFirstFn("(function f(x) { function g() { x } })", 'hasScopeBlock')
 226 
 227 // A child function accessing a global variable triggers the need for parent
 228 // scope in parent
 229 testFirstFn("(function f() { function g() { x } })", 'needsParentScope', 'needsCallee')
 230 
 231 // A child function redefining a local variable from its parent should not
 232 // affect the parent function in any way
 233 testFirstFn("(function f() { var x; function g() { var x; x } })")
 234 
 235 // Using "with" on its own doesn't do much.
 236 testFirstFn("(function f() { var o; with(o) {} })")
 237 
 238 // "with" referencing a local variable triggers scoping.
 239 testFirstFn("(function f() { var x; var y; with(x) { y } })", 'hasScopeBlock')
 240 
 241 // "with" referencing a non-local variable triggers parent scope.
 242 testFirstFn("(function f() { var x; with(x) { y } })", 'needsCallee', 'needsParentScope')
 243 
 244 // Nested function using "with" is pretty much the same as the parent
 245 // function needing with.
 246 testFirstFn("(function f() { function g() { var o; with(o) {} } })")
 247 
 248 // Nested function using "with" referencing a local variable.
 249 testFirstFn("(function f() { var x; function g() { var o; with(o) { x } } })", 'hasScopeBlock')
 250 
 251 // Using "eval" triggers pretty much everything. The function even needs to be
 252 // vararg, 'cause we don't know if eval will be using "arguments".
 253 testFirstFn("(function f() { eval() })", 'usesSelfSymbol', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'hasEval', 'isVarArg', 'allVarsInScope')
 254 
 255 // Nested function using "eval" is almost the same as parent function using
 256 // eval, but at least the parent doesn't have to be vararg.
 257 testFirstFn("(function f() { function g() { eval() } })", 'usesSelfSymbol', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'allVarsInScope')
 258 
 259 // Function with 125 named parameters is ordinary
 260 testFirstFn("function f(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, p58, p59, p60, p61, p62, p63, p64, p65, p66, p67, p68, p69, p70, p71, p72, p73, p74, p75, p76, p77, p78, p79, p80, p81, p82, p83, p84, p85, p86, p87, p88, p89, p90, p91, p92, p93, p94, p95, p96, p97, p98, p99, p100, p101, p102, p103, p104, p105, p106, p107, p108, p109, p110, p111, p112, p113, p114, p115, p116, p117, p118, p119, p120, p121, p122, p123, p124, p125) { p125 = p124 }")
 261 
 262 // Function with 126 named parameters is variable arguments
 263 // NOTE: hasScopeBlock should be optimized away. Implementation of JDK-8038942 should take care of it.
 264 testFirstFn("function f(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28, p29, p30, p31, p32, p33, p34, p35, p36, p37, p38, p39, p40, p41, p42, p43, p44, p45, p46, p47, p48, p49, p50, p51, p52, p53, p54, p55, p56, p57, p58, p59, p60, p61, p62, p63, p64, p65, p66, p67, p68, p69, p70, p71, p72, p73, p74, p75, p76, p77, p78, p79, p80, p81, p82, p83, p84, p85, p86, p87, p88, p89, p90, p91, p92, p93, p94, p95, p96, p97, p98, p99, p100, p101, p102, p103, p104, p105, p106, p107, p108, p109, p110, p111, p112, p113, p114, p115, p116, p117, p118, p119, p120, p121, p122, p123, p124, p125, p126) { p125 = p126 }", 'isVarArg', 'hasScopeBlock')