JDK-8006529.js revision 1777:987ce060a647
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 */
41var Reflector           = Java.type("jdk.nashorn.test.models.Reflector");
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 CompilationPhases   = forName("jdk.nashorn.internal.codegen.Compiler$CompilationPhases").static;
46var Context             = forName("jdk.nashorn.internal.runtime.Context").static
47var CodeInstaller       = forName("jdk.nashorn.internal.runtime.CodeInstaller").static
48var ScriptEnvironment   = forName("jdk.nashorn.internal.runtime.ScriptEnvironment").static
49var Source              = forName("jdk.nashorn.internal.runtime.Source").static
50var FunctionNode        = forName("jdk.nashorn.internal.ir.FunctionNode").static
51var Block               = forName("jdk.nashorn.internal.ir.Block").static
52var VarNode             = forName("jdk.nashorn.internal.ir.VarNode").static
53var ExpressionStatement = forName("jdk.nashorn.internal.ir.ExpressionStatement").static
54var UnaryNode           = forName("jdk.nashorn.internal.ir.UnaryNode").static
55var BinaryNode          = forName("jdk.nashorn.internal.ir.BinaryNode").static
56var ThrowErrorManager   = forName("jdk.nashorn.internal.runtime.Context$ThrowErrorManager").static
57var ErrorManager        = forName("jdk.nashorn.internal.runtime.ErrorManager").static
58var Debug               = forName("jdk.nashorn.internal.runtime.Debug").static
59var String              = forName("java.lang.String").static
60var boolean             = Java.type("boolean");
61
62var parseMethod = Parser.class.getMethod("parse");
63var compileMethod = Compiler.class.getMethod("compile", FunctionNode.class, CompilationPhases.class);
64var getBodyMethod = FunctionNode.class.getMethod("getBody");
65var getStatementsMethod = Block.class.getMethod("getStatements");
66var getInitMethod = VarNode.class.getMethod("getInit");
67var getExpressionMethod = ExpressionStatement.class.getMethod("getExpression")
68var rhsMethod = UnaryNode.class.getMethod("getExpression")
69var lhsMethod = BinaryNode.class.getMethod("lhs")
70var binaryRhsMethod = BinaryNode.class.getMethod("rhs")
71var debugIdMethod = Debug.class.getMethod("id", java.lang.Object.class)
72var compilePhases = Reflector.get(CompilationPhases.class.getField("COMPILE_UPTO_BYTECODE"), null);
73
74function invoke(m, obj) {
75    return Reflector.invoke(m, obj);
76}
77
78// These are method names of methods in FunctionNode class
79var allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'usesSelfSymbol', 'isSplit', 'hasEval', 'allVarsInScope', 'isStrict']
80
81// corresponding Method objects of FunctionNode class
82var 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)
92function getFirstFunction(functionNode) {
93    var f = findFunction(invoke(getBodyMethod, functionNode))
94    if (f == null) {
95        throw new Error();
96    }
97    return f;
98}
99
100function 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
122var getContextMethod = Context.class.getMethod("getContext")
123var getEnvMethod = Context.class.getMethod("getEnv")
124
125var sourceForMethod = Source.class.getMethod("sourceFor", java.lang.String.class, java.lang.String.class)
126var ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class)
127var 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.
132function 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
146var 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.
157function 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.
179function 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
187testFirstFn("function f() { }")
188testFirstFn("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)
192testFirstFn("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".
196testFirstFn("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.
200testFirstFn("function f() {'use strict'; arguments }", 'isVarArg', 'isStrict')
201
202// A function defining "arguments" as a parameter will not be vararg.
203testFirstFn("function f(arguments) { arguments }")
204
205// A function defining "arguments" as a nested function will not be vararg.
206testFirstFn("function f() { function arguments() {}; arguments; }")
207
208// A function defining "arguments" as a local variable will be vararg.
209testFirstFn("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.
213testFirstFn("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.
217testFirstFn("(function f() { f() })", 'needsCallee', 'usesSelfSymbol')
218
219// A child function accessing parent's variable triggers the need for scope
220// in parent
221testFirstFn("(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
225testFirstFn("(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
229testFirstFn("(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
233testFirstFn("(function f() { var x; function g() { var x; x } })")
234
235// Using "with" on its own doesn't do much.
236testFirstFn("(function f() { var o; with(o) {} })")
237
238// "with" referencing a local variable triggers scoping.
239testFirstFn("(function f() { var x; var y; with(x) { y } })", 'hasScopeBlock')
240
241// "with" referencing a non-local variable triggers parent scope.
242testFirstFn("(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.
246testFirstFn("(function f() { function g() { var o; with(o) {} } })")
247
248// Nested function using "with" referencing a local variable.
249testFirstFn("(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".
253testFirstFn("(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.
257testFirstFn("(function f() { function g() { eval() } })", 'usesSelfSymbol', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'allVarsInScope')
258
259// Function with 250 named parameters is ordinary
260testFirstFn("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 }")
261
262// Function with 251 named parameters is variable arguments
263// NOTE: hasScopeBlock should be optimized away. Implementation of JDK-8038942 should take care of it.
264testFirstFn("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', 'hasScopeBlock')
265