JDK-8006529.js revision 818:26a5fdb90de2
1/* 2 * Copyright (c) 2010, 2013, 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 42var forName = java.lang.Class["forName(String)"]; 43var Parser = forName("jdk.nashorn.internal.parser.Parser").static 44var Compiler = forName("jdk.nashorn.internal.codegen.Compiler").static 45var Context = forName("jdk.nashorn.internal.runtime.Context").static 46var ScriptEnvironment = forName("jdk.nashorn.internal.runtime.ScriptEnvironment").static 47var Source = forName("jdk.nashorn.internal.runtime.Source").static 48var FunctionNode = forName("jdk.nashorn.internal.ir.FunctionNode").static 49var Block = forName("jdk.nashorn.internal.ir.Block").static 50var VarNode = forName("jdk.nashorn.internal.ir.VarNode").static 51var ExpressionStatement = forName("jdk.nashorn.internal.ir.ExpressionStatement").static 52var UnaryNode = forName("jdk.nashorn.internal.ir.UnaryNode").static 53var BinaryNode = forName("jdk.nashorn.internal.ir.BinaryNode").static 54var ThrowErrorManager = forName("jdk.nashorn.internal.runtime.Context$ThrowErrorManager").static 55var ErrorManager = forName("jdk.nashorn.internal.runtime.ErrorManager").static 56var Debug = forName("jdk.nashorn.internal.runtime.Debug").static 57 58var parseMethod = Parser.class.getMethod("parse"); 59var compileMethod = Compiler.class.getMethod("compile", FunctionNode.class); 60var getBodyMethod = FunctionNode.class.getMethod("getBody"); 61var getStatementsMethod = Block.class.getMethod("getStatements"); 62var getInitMethod = VarNode.class.getMethod("getInit"); 63var getExpressionMethod = ExpressionStatement.class.getMethod("getExpression") 64var rhsMethod = UnaryNode.class.getMethod("getExpression") 65var lhsMethod = BinaryNode.class.getMethod("lhs") 66var binaryRhsMethod = BinaryNode.class.getMethod("rhs") 67var debugIdMethod = Debug.class.getMethod("id", java.lang.Object.class) 68 69// These are method names of methods in FunctionNode class 70var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'usesSelfSymbol', 'isSplit', 'hasEval', 'allVarsInScope', 'isStrict'] 71 72// corresponding Method objects of FunctionNode class 73var functionNodeMethods = {}; 74// initialize FunctionNode methods 75(function() { 76 for (var f in allAssertionList) { 77 var method = allAssertionList[f]; 78 functionNodeMethods[method] = FunctionNode.class.getMethod(method); 79 } 80})(); 81 82// returns functionNode.getBody().getStatements().get(0) 83function getFirstFunction(functionNode) { 84 var f = findFunction(getBodyMethod.invoke(functionNode)) 85 if (f == null) { 86 throw new Error(); 87 } 88 return f; 89} 90 91function findFunction(node) { 92 if(node instanceof Block) { 93 var stmts = getStatementsMethod.invoke(node) 94 for(var i = 0; i < stmts.size(); ++i) { 95 var retval = findFunction(stmts.get(i)) 96 if(retval != null) { 97 return retval; 98 } 99 } 100 } else if(node instanceof VarNode) { 101 return findFunction(getInitMethod.invoke(node)) 102 } else if(node instanceof UnaryNode) { 103 return findFunction(rhsMethod.invoke(node)) 104 } else if(node instanceof BinaryNode) { 105 return findFunction(lhsMethod.invoke(node)) || findFunction(binaryRhsMethod.invoke(node)) 106 } else if(node instanceof ExpressionStatement) { 107 return findFunction(getExpressionMethod.invoke(node)) 108 } else if(node instanceof FunctionNode) { 109 return node 110 } 111} 112 113var getContextMethod = Context.class.getMethod("getContext") 114var getEnvMethod = Context.class.getMethod("getEnv") 115 116var SourceConstructor = Source.class.getConstructor(java.lang.String.class, java.lang.String.class) 117var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class) 118var CompilerConstructor = Compiler.class.getConstructor(ScriptEnvironment.class) 119 120// compile(script) -- compiles a script specified as a string with its 121// source code, returns a jdk.nashorn.internal.ir.FunctionNode object 122// representing it. 123function compile(source) { 124 var source = SourceConstructor.newInstance("<no name>", source); 125 126 var env = getEnvMethod.invoke(getContextMethod.invoke(null)) 127 128 var parser = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance()); 129 var func = parseMethod.invoke(parser); 130 131 var compiler = CompilerConstructor.newInstance(env); 132 133 return compileMethod.invoke(compiler, func); 134}; 135 136var allAssertions = (function() { 137 var allAssertions = {} 138 for(var assertion in allAssertionList) { 139 allAssertions[allAssertionList[assertion]] = true 140 } 141 return allAssertions; 142})(); 143 144 145// test(f[, assertions...]) tests whether all the specified assertions on the 146// passed function node are true. 147function test(f) { 148 var assertions = {} 149 for(var i = 1; i < arguments.length; ++i) { 150 var assertion = arguments[i] 151 if(!allAssertions[assertion]) { 152 throw "Unknown assertion " + assertion + " for " + f; 153 } 154 assertions[assertion] = true 155 } 156 for(var assertion in allAssertions) { 157 var expectedValue = !!assertions[assertion] 158 var actualValue = functionNodeMethods[assertion].invoke(f) 159 if(actualValue !== expectedValue) { 160 throw "Expected " + assertion + " === " + expectedValue + ", got " + actualValue + " for " + f + ":" + debugIdMethod.invoke(null, f); 161 } 162 } 163} 164 165// testFirstFn(script[, assertions...] tests whether all the specified 166// assertions are true in the first function in the given script; "script" 167// is a string with the source text of the script. 168function testFirstFn(script) { 169 arguments[0] = getFirstFunction(compile(script)) 170 test.apply(null, arguments) 171} 172 173// ---------------------------------- ACTUAL TESTS START HERE -------------- 174 175// The simplest possible functions have no attributes set 176testFirstFn("function f() { }") 177testFirstFn("function f(x) { x }") 178 179// A function referencing a global needs parent scope, and it needs callee 180// (because parent scope is passed through callee) 181testFirstFn("function f() { x }", 'needsCallee', 'needsParentScope') 182 183// A function referencing "arguments" will have to be vararg. It also needs 184// the callee, as it needs to fill out "arguments.callee". 185testFirstFn("function f() { arguments }", 'needsCallee', 'isVarArg') 186 187// A function referencing "arguments" will have to be vararg. If it is 188// strict, it will not have to have a callee, though. 189testFirstFn("function f() {'use strict'; arguments }", 'isVarArg', 'isStrict') 190 191// A function defining "arguments" as a parameter will not be vararg. 192testFirstFn("function f(arguments) { arguments }") 193 194// A function defining "arguments" as a nested function will not be vararg. 195testFirstFn("function f() { function arguments() {}; arguments; }") 196 197// A function defining "arguments" as a local variable will be vararg. 198testFirstFn("function f() { var arguments; arguments; }", 'isVarArg', 'needsCallee') 199 200// A self-referencing function defined as a statement doesn't need a self 201// symbol, as it'll rather obtain itself from the parent scope. 202testFirstFn("function f() { f() }", 'needsCallee', 'needsParentScope') 203 204// A self-referencing function defined as an expression needs a self symbol, 205// as it can't obtain itself from the parent scope. 206testFirstFn("(function f() { f() })", 'needsCallee', 'usesSelfSymbol') 207 208// A child function accessing parent's variable triggers the need for scope 209// in parent 210testFirstFn("(function f() { var x; function g() { x } })", 'hasScopeBlock') 211 212// A child function accessing parent's parameter triggers the need for scope 213// in parent 214testFirstFn("(function f(x) { function g() { x } })", 'hasScopeBlock') 215 216// A child function accessing a global variable triggers the need for parent 217// scope in parent 218testFirstFn("(function f() { function g() { x } })", 'needsParentScope', 'needsCallee') 219 220// A child function redefining a local variable from its parent should not 221// affect the parent function in any way 222testFirstFn("(function f() { var x; function g() { var x; x } })") 223 224// Using "with" on its own doesn't do much. 225testFirstFn("(function f() { var o; with(o) {} })") 226 227// "with" referencing a local variable triggers scoping. 228testFirstFn("(function f() { var x; var y; with(x) { y } })", 'hasScopeBlock') 229 230// "with" referencing a non-local variable triggers parent scope. 231testFirstFn("(function f() { var x; with(x) { y } })", 'needsCallee', 'needsParentScope') 232 233// Nested function using "with" is pretty much the same as the parent 234// function needing with. 235testFirstFn("(function f() { function g() { var o; with(o) {} } })") 236 237// Nested function using "with" referencing a local variable. 238testFirstFn("(function f() { var x; function g() { var o; with(o) { x } } })", 'hasScopeBlock') 239 240// Using "eval" triggers pretty much everything. The function even needs to be 241// vararg, 'cause we don't know if eval will be using "arguments". 242testFirstFn("(function f() { eval() })", 'usesSelfSymbol', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'hasEval', 'isVarArg', 'allVarsInScope') 243 244// Nested function using "eval" is almost the same as parent function using 245// eval, but at least the parent doesn't have to be vararg. 246testFirstFn("(function f() { function g() { eval() } })", 'usesSelfSymbol', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'allVarsInScope') 247 248// Function with 250 named parameters is ordinary 249testFirstFn("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, p127, p128, p129, p130, p131, p132, p133, p134, p135, p136, p137, p138, p139, p140, p141, p142, p143, p144, p145, p146, p147, p148, p149, p150, p151, p152, p153, p154, p155, p156, p157, p158, p159, p160, p161, p162, p163, p164, p165, p166, p167, p168, p169, p170, p171, p172, p173, p174, p175, p176, p177, p178, p179, p180, p181, p182, p183, p184, p185, p186, p187, p188, p189, p190, p191, p192, p193, p194, p195, p196, p197, p198, p199, p200, p201, p202, p203, p204, p205, p206, p207, p208, p209, p210, p211, p212, p213, p214, p215, p216, p217, p218, p219, p220, p221, p222, p223, p224, p225, p226, p227, p228, p229, p230, p231, p232, p233, p234, p235, p236, p237, p238, p239, p240, p241, p242, p243, p244, p245, p246, p247, p248, p249, p250) { p250 = p249 }") 250 251// Function with 251 named parameters is variable arguments 252testFirstFn("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, p127, p128, p129, p130, p131, p132, p133, p134, p135, p136, p137, p138, p139, p140, p141, p142, p143, p144, p145, p146, p147, p148, p149, p150, p151, p152, p153, p154, p155, p156, p157, p158, p159, p160, p161, p162, p163, p164, p165, p166, p167, p168, p169, p170, p171, p172, p173, p174, p175, p176, p177, p178, p179, p180, p181, p182, p183, p184, p185, p186, p187, p188, p189, p190, p191, p192, p193, p194, p195, p196, p197, p198, p199, p200, p201, p202, p203, p204, p205, p206, p207, p208, p209, p210, p211, p212, p213, p214, p215, p216, p217, p218, p219, p220, p221, p222, p223, p224, p225, p226, p227, p228, p229, p230, p231, p232, p233, p234, p235, p236, p237, p238, p239, p240, p241, p242, p243, p244, p245, p246, p247, p248, p249, p250, p251) { p250 = p251 }", 'isVarArg') 253