JDK-8006529.js revision 464:e4efb3ce97b2
1239671Srwatson/*
2239671Srwatson * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3264897Sbrooks * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4239671Srwatson *
5239671Srwatson * This code is free software; you can redistribute it and/or modify it
6244899Srwatson * under the terms of the GNU General Public License version 2 only, as
7244899Srwatson * published by the Free Software Foundation.
8244899Srwatson *
9244899Srwatson * This code is distributed in the hope that it will be useful, but WITHOUT
10239671Srwatson * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11239671Srwatson * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12239671Srwatson * version 2 for more details (a copy is included in the LICENSE file that
13239671Srwatson * accompanied this code).
14239671Srwatson *
15239671Srwatson * You should have received a copy of the GNU General Public License version
16239671Srwatson * 2 along with this work; if not, write to the Free Software Foundation,
17239671Srwatson * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18239671Srwatson *
19239671Srwatson * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20239671Srwatson * or visit www.oracle.com if you need additional information or have any
21239671Srwatson * questions.
22239671Srwatson */
23239671Srwatson
24239671Srwatson/**
25239671Srwatson * JDK-8006529 : Methods should not always get callee parameter, and they
26239671Srwatson * should not be too eager in creation of scopes.
27239671Srwatson *
28239671Srwatson * @test
29239671Srwatson * @run
30239671Srwatson */
31239671Srwatson
32239671Srwatson/*
33239671Srwatson * This test script depends on nashorn Compiler internals. It uses reflection
34239671Srwatson * to get access to private field and many public methods of Compiler and
35244899Srwatson * FunctionNode classes. Note that this is trusted code and access to such
36239671Srwatson * internal package classes and methods is okay. But, if you modify any
37239671Srwatson * Compiler or FunctionNode class, you may have to revisit this script.
38239671Srwatson * We cannot use direct Java class (via dynalink bean linker) to Compiler
39239671Srwatson * and FunctionNode because of package-access check and so reflective calls.
40239671Srwatson */
41239671Srwatson
42239671Srwatsonvar Parser              = Java.type("jdk.nashorn.internal.parser.Parser")
43239671Srwatsonvar Compiler            = Java.type("jdk.nashorn.internal.codegen.Compiler")
44239671Srwatsonvar Context             = Java.type("jdk.nashorn.internal.runtime.Context")
45239671Srwatsonvar ScriptEnvironment   = Java.type("jdk.nashorn.internal.runtime.ScriptEnvironment")
46239671Srwatsonvar Source              = Java.type("jdk.nashorn.internal.runtime.Source")
47239671Srwatsonvar FunctionNode        = Java.type("jdk.nashorn.internal.ir.FunctionNode")
48245330Srwatsonvar Block               = Java.type("jdk.nashorn.internal.ir.Block")
49239671Srwatsonvar VarNode             = Java.type("jdk.nashorn.internal.ir.VarNode")
50239671Srwatsonvar ExpressionStatement = Java.type("jdk.nashorn.internal.ir.ExpressionStatement")
51239671Srwatsonvar UnaryNode           = Java.type("jdk.nashorn.internal.ir.UnaryNode")
52239671Srwatsonvar BinaryNode          = Java.type("jdk.nashorn.internal.ir.BinaryNode")
53239671Srwatsonvar ThrowErrorManager   = Java.type("jdk.nashorn.internal.runtime.Context$ThrowErrorManager")
54239671Srwatsonvar ErrorManager        = Java.type("jdk.nashorn.internal.runtime.ErrorManager")
55239671Srwatsonvar Debug               = Java.type("jdk.nashorn.internal.runtime.Debug")
56239671Srwatson
57239671Srwatsonvar parseMethod = Parser.class.getMethod("parse");
58239671Srwatsonvar compileMethod = Compiler.class.getMethod("compile", FunctionNode.class);
59244942Srwatsonvar getBodyMethod = FunctionNode.class.getMethod("getBody");
60244899Srwatsonvar getStatementsMethod = Block.class.getMethod("getStatements");
61244899Srwatsonvar getInitMethod = VarNode.class.getMethod("getInit");
62244942Srwatsonvar getExpressionMethod = ExpressionStatement.class.getMethod("getExpression")
63244899Srwatsonvar rhsMethod = UnaryNode.class.getMethod("rhs")
64239671Srwatsonvar lhsMethod = BinaryNode.class.getMethod("lhs")
65239671Srwatsonvar binaryRhsMethod = BinaryNode.class.getMethod("rhs")
66239671Srwatsonvar debugIdMethod = Debug.class.getMethod("id", java.lang.Object.class)
67239671Srwatson
68264897Sbrooks// These are method names of methods in FunctionNode class
69239671Srwatsonvar allAssertionList = ['isVarArg', 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'needsSelfSymbol', 'isSplit', 'hasEval', 'allVarsInScope', 'isStrict']
70239671Srwatson
71239671Srwatson// corresponding Method objects of FunctionNode class
72239671Srwatsonvar functionNodeMethods = {};
73239671Srwatson// initialize FunctionNode methods
74245330Srwatson(function() {
75239671Srwatson    for (var f in allAssertionList) {
76239671Srwatson        var method = allAssertionList[f];
77239671Srwatson        functionNodeMethods[method] = FunctionNode.class.getMethod(method);
78239671Srwatson    }
79239671Srwatson})();
80239671Srwatson
81239671Srwatson// returns functionNode.getBody().getStatements().get(0)
82239671Srwatsonfunction getFirstFunction(functionNode) {
83239671Srwatson    var f = findFunction(getBodyMethod.invoke(functionNode))
84239671Srwatson    if (f == null) {
85239671Srwatson        throw new Error();
86239671Srwatson    }
87239671Srwatson    return f;
88239671Srwatson}
89239671Srwatson
90239671Srwatsonfunction findFunction(node) {
91239671Srwatson    if(node instanceof Block) {
92239671Srwatson        var stmts = getStatementsMethod.invoke(node)
93239671Srwatson        for(var i = 0; i < stmts.size(); ++i) {
94239671Srwatson            var retval = findFunction(stmts.get(i))
95239671Srwatson            if(retval != null) {
96239671Srwatson                return retval;
97239671Srwatson            }
98239671Srwatson        }
99239671Srwatson    } else if(node instanceof VarNode) {
100239671Srwatson        return findFunction(getInitMethod.invoke(node))
101239671Srwatson    } else if(node instanceof UnaryNode) {
102239671Srwatson        return findFunction(rhsMethod.invoke(node))
103239671Srwatson    } else if(node instanceof BinaryNode) {
104239671Srwatson        return findFunction(lhsMethod.invoke(node)) || findFunction(binaryRhsMethod.invoke(node))
105239671Srwatson	} else if(node instanceof ExpressionStatement) {
106239671Srwatson		return findFunction(getExpressionMethod.invoke(node))
107239671Srwatson    } else if(node instanceof FunctionNode) {
108239671Srwatson        return node
109239671Srwatson    }
110239671Srwatson}
111239671Srwatson
112239671Srwatsonvar getContextMethod = Context.class.getMethod("getContext")
113239671Srwatsonvar getEnvMethod = Context.class.getMethod("getEnv")
114239671Srwatson
115239671Srwatsonvar SourceConstructor = Source.class.getConstructor(java.lang.String.class, java.lang.String.class)
116239671Srwatsonvar ParserConstructor = Parser.class.getConstructor(ScriptEnvironment.class, Source.class, ErrorManager.class)
117239671Srwatsonvar CompilerConstructor = Compiler.class.getConstructor(ScriptEnvironment.class)
118239671Srwatson
119239671Srwatson// compile(script) -- compiles a script specified as a string with its
120239671Srwatson// source code, returns a jdk.nashorn.internal.ir.FunctionNode object
121239671Srwatson// representing it.
122239671Srwatsonfunction compile(source) {
123239671Srwatson    var source = SourceConstructor.newInstance("<no name>", source);
124239671Srwatson
125256745Sbrooks    var env = getEnvMethod.invoke(getContextMethod.invoke(null))
126256745Sbrooks
127256745Sbrooks    var parser   = ParserConstructor.newInstance(env, source, ThrowErrorManager.class.newInstance());
128256745Sbrooks    var func     = parseMethod.invoke(parser);
129256745Sbrooks
130256745Sbrooks    var compiler = CompilerConstructor.newInstance(env);
131256745Sbrooks
132256745Sbrooks    return compileMethod.invoke(compiler, func);
133239671Srwatson};
134239671Srwatson
135265089Sbzvar allAssertions = (function() {
136265089Sbz    var allAssertions = {}
137265089Sbz    for(var assertion in allAssertionList) {
138265089Sbz        allAssertions[allAssertionList[assertion]] = true
139265089Sbz    }
140265089Sbz    return allAssertions;
141265089Sbz})();
142265089Sbz
143265089Sbz
144265089Sbz// test(f[, assertions...]) tests whether all the specified assertions on the
145265089Sbz// passed function node are true.
146265089Sbzfunction test(f) {
147265089Sbz    var assertions = {}
148265089Sbz    for(var i = 1; i < arguments.length; ++i) {
149265089Sbz        var assertion = arguments[i]
150265089Sbz        if(!allAssertions[assertion]) {
151265089Sbz            throw "Unknown assertion " + assertion + " for " + f;
152265089Sbz        }
153265089Sbz        assertions[assertion] = true
154265089Sbz    }
155265089Sbz    for(var assertion in allAssertions) {
156265089Sbz        var expectedValue = !!assertions[assertion]
157265089Sbz        var actualValue = functionNodeMethods[assertion].invoke(f)
158265089Sbz        if(actualValue !== expectedValue) {
159265089Sbz            throw "Expected " + assertion + " === " + expectedValue + ", got " + actualValue + " for " + f + ":" + debugIdMethod.invoke(null, f);
160265089Sbz        }
161265089Sbz    }
162265089Sbz}
163265089Sbz
164265089Sbz// testFirstFn(script[, assertions...] tests whether all the specified
165265089Sbz// assertions are true in the first function in the given script; "script"
166265089Sbz// is a string with the source text of the script.
167273234Sdavidefunction testFirstFn(script) {
168265089Sbz    arguments[0] = getFirstFunction(compile(script))
169273234Sdavide    test.apply(null, arguments)
170265089Sbz}
171265089Sbz
172265089Sbz// ---------------------------------- ACTUAL TESTS START HERE --------------
173265089Sbz
174265089Sbz// The simplest possible functions have no attributes set
175239671SrwatsontestFirstFn("function f() { }")
176239671SrwatsontestFirstFn("function f(x) { x }")
177239671Srwatson
178239671Srwatson// A function referencing a global needs parent scope, and it needs callee
179264897Sbrooks// (because parent scope is passed through callee)
180239671SrwatsontestFirstFn("function f() { x }", 'needsCallee', 'needsParentScope')
181239671Srwatson
182239671Srwatson// A function referencing "arguments" will have to be vararg. It also needs
183239671Srwatson// the callee, as it needs to fill out "arguments.callee".
184239671SrwatsontestFirstFn("function f() { arguments }", 'needsCallee', 'isVarArg')
185264897Sbrooks
186245330Srwatson// A function referencing "arguments" will have to be vararg. If it is
187265089Sbz// strict, it will not have to have a callee, though.
188245330SrwatsontestFirstFn("function f() {'use strict'; arguments }", 'isVarArg', 'isStrict')
189265089Sbz
190245330Srwatson// A function defining "arguments" as a parameter will not be vararg.
191245330SrwatsontestFirstFn("function f(arguments) { arguments }")
192239671Srwatson
193239671Srwatson// A function defining "arguments" as a nested function will not be vararg.
194239671SrwatsontestFirstFn("function f() { function arguments() {}; arguments; }")
195239671Srwatson
196239671Srwatson// A function defining "arguments" as a local variable will be vararg.
197239671SrwatsontestFirstFn("function f() { var arguments; arguments; }", 'isVarArg', 'needsCallee')
198239671Srwatson
199239671Srwatson// A self-referencing function defined as a statement doesn't need a self
200239671Srwatson// symbol, as it'll rather obtain itself from the parent scope.
201239671SrwatsontestFirstFn("function f() { f() }", 'needsCallee', 'needsParentScope')
202264897Sbrooks
203264897Sbrooks// A self-referencing function defined as an expression needs a self symbol,
204264897Sbrooks// as it can't obtain itself from the parent scope.
205264897SbrookstestFirstFn("(function f() { f() })", 'needsCallee', 'needsSelfSymbol')
206264897Sbrooks
207264897Sbrooks// A child function accessing parent's variable triggers the need for scope
208264897Sbrooks// in parent
209264897SbrookstestFirstFn("(function f() { var x; function g() { x } })", 'hasScopeBlock')
210264897Sbrooks
211264897Sbrooks// A child function accessing parent's parameter triggers the need for scope
212264897Sbrooks// in parent
213264897SbrookstestFirstFn("(function f(x) { function g() { x } })", 'hasScopeBlock')
214264897Sbrooks
215264897Sbrooks// A child function accessing a global variable triggers the need for parent
216264897Sbrooks// scope in parent
217264897SbrookstestFirstFn("(function f() { function g() { x } })", 'needsParentScope', 'needsCallee')
218264897Sbrooks
219264897Sbrooks// A child function redefining a local variable from its parent should not
220264897Sbrooks// affect the parent function in any way
221245329SrwatsontestFirstFn("(function f() { var x; function g() { var x; x } })")
222245330Srwatson
223245330Srwatson// Using "with" on its own doesn't do much.
224245330SrwatsontestFirstFn("(function f() { var o; with(o) {} })")
225245330Srwatson
226245330Srwatson// "with" referencing a local variable triggers scoping.
227245330SrwatsontestFirstFn("(function f() { var x; var y; with(x) { y } })", 'hasScopeBlock')
228245330Srwatson
229245330Srwatson// "with" referencing a non-local variable triggers parent scope.
230245330SrwatsontestFirstFn("(function f() { var x; with(x) { y } })", 'needsCallee', 'needsParentScope')
231245330Srwatson
232245330Srwatson// Nested function using "with" is pretty much the same as the parent
233245330Srwatson// function needing with.
234245330SrwatsontestFirstFn("(function f() { function g() { var o; with(o) {} } })")
235245330Srwatson
236245330Srwatson// Nested function using "with" referencing a local variable.
237245330SrwatsontestFirstFn("(function f() { var x; function g() { var o; with(o) { x } } })", 'hasScopeBlock')
238245330Srwatson
239245330Srwatson// Using "eval" triggers pretty much everything. The function even needs to be
240245329Srwatson// vararg, 'cause we don't know if eval will be using "arguments".
241245329SrwatsontestFirstFn("(function f() { eval() })", 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'hasEval', 'isVarArg', 'allVarsInScope')
242245329Srwatson
243245329Srwatson// Nested function using "eval" is almost the same as parent function using
244259265Sbz// eval, but at least the parent doesn't have to be vararg.
245245329SrwatsontestFirstFn("(function f() { function g() { eval() } })", 'needsParentScope', 'needsCallee', 'hasScopeBlock', 'allVarsInScope')
246264605Sbz
247264605Sbz// Function with 250 named parameters is ordinary
248264897SbrookstestFirstFn("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 }")
249264605Sbz
250264897Sbrooks// Function with 251 named parameters is variable arguments
251264897SbrookstestFirstFn("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')
252245329Srwatson