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.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.nashorn.internal.runtime.test;
27
28import static org.testng.Assert.assertEquals;
29import static org.testng.Assert.assertTrue;
30import static org.testng.Assert.fail;
31import javax.script.ScriptContext;
32import javax.script.ScriptEngine;
33import javax.script.ScriptEngineFactory;
34import javax.script.ScriptEngineManager;
35import javax.script.ScriptException;
36import javax.script.SimpleScriptContext;
37import jdk.nashorn.api.scripting.ClassFilter;
38import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
39import jdk.nashorn.internal.runtime.Version;
40import org.testng.annotations.Test;
41
42/**
43 * Tests for trusted client usage of nashorn script engine factory extension API
44 */
45@SuppressWarnings("javadoc")
46public class TrustedScriptEngineTest {
47    @Test
48    public void versionTest() {
49        final ScriptEngineManager m = new ScriptEngineManager();
50        final ScriptEngine e = m.getEngineByName("nashorn");
51        assertEquals(e.getFactory().getEngineVersion(), Version.version());
52    }
53
54    private static class MyClassLoader extends ClassLoader {
55        // to check if script engine uses the specified class loader
56        private final boolean[] reached = new boolean[1];
57
58        @Override
59        protected Class<?> findClass(final String name) throws ClassNotFoundException {
60            // flag that it reached here
61            reached[0] = true;
62            return super.findClass(name);
63        }
64
65        public boolean reached() {
66            return reached[0];
67        }
68    }
69
70    // These are for "private" extension API of NashornScriptEngineFactory that
71    // accepts a ClassLoader and/or command line options.
72
73    @Test
74    public void factoryClassLoaderTest() {
75        final ScriptEngineManager sm = new ScriptEngineManager();
76        for (final ScriptEngineFactory fac : sm.getEngineFactories()) {
77            if (fac instanceof NashornScriptEngineFactory) {
78                final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac;
79                final MyClassLoader loader = new MyClassLoader();
80                // set the classloader as app class loader
81                final ScriptEngine e = nfac.getScriptEngine(loader);
82                try {
83                    e.eval("Packages.foo");
84                    // check that the class loader was attempted
85                    assertTrue(loader.reached(), "did not reach class loader!");
86                } catch (final ScriptException se) {
87                    se.printStackTrace();
88                    fail(se.getMessage());
89                }
90                return;
91            }
92        }
93
94        fail("Cannot find nashorn factory!");
95    }
96
97    @Test
98    public void factoryClassLoaderAndOptionsTest() {
99        final ScriptEngineManager sm = new ScriptEngineManager();
100        for (final ScriptEngineFactory fac : sm.getEngineFactories()) {
101            if (fac instanceof NashornScriptEngineFactory) {
102                final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac;
103                final String[] options = new String[] { "-strict" };
104                final MyClassLoader loader = new MyClassLoader();
105                // set the classloader as app class loader
106                final ScriptEngine e = nfac.getScriptEngine(options, loader);
107                try {
108                    e.eval("Packages.foo");
109                    // check that the class loader was attempted
110                    assertTrue(loader.reached(), "did not reach class loader!");
111                } catch (final ScriptException se) {
112                    se.printStackTrace();
113                    fail(se.getMessage());
114                }
115
116                try {
117                    // strict mode - delete of a var should throw SyntaxError
118                    e.eval("var d = 2; delete d;");
119                } catch (final ScriptException se) {
120                    // check that the error message contains "SyntaxError"
121                    assertTrue(se.getMessage().contains("SyntaxError"));
122                }
123
124                return;
125            }
126        }
127
128        fail("Cannot find nashorn factory!");
129    }
130
131    @Test
132    public void factoryOptionsTest() {
133        final ScriptEngineManager sm = new ScriptEngineManager();
134        for (final ScriptEngineFactory fac : sm.getEngineFactories()) {
135            if (fac instanceof NashornScriptEngineFactory) {
136                final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac;
137                // specify --no-syntax-extensions flag
138                final String[] options = new String[] { "--no-syntax-extensions" };
139                final ScriptEngine e = nfac.getScriptEngine(options);
140                try {
141                    // try nashorn specific extension
142                    e.eval("var f = funtion(x) 2*x;");
143                    fail("should have thrown exception!");
144                } catch (final Exception ex) {
145                    //empty
146                }
147                return;
148            }
149        }
150
151        fail("Cannot find nashorn factory!");
152    }
153
154    @Test
155    /**
156     * Test repeated evals with --loader-per-compile=false
157     * We used to get "class redefinition error".
158     */
159    public void noLoaderPerCompilerTest() {
160        final ScriptEngineManager sm = new ScriptEngineManager();
161        for (final ScriptEngineFactory fac : sm.getEngineFactories()) {
162            if (fac instanceof NashornScriptEngineFactory) {
163                final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac;
164                final String[] options = new String[] { "--loader-per-compile=false" };
165                final ScriptEngine e = nfac.getScriptEngine(options);
166                try {
167                    e.eval("2 + 3");
168                    e.eval("4 + 4");
169                } catch (final ScriptException se) {
170                    se.printStackTrace();
171                    fail(se.getMessage());
172                }
173                return;
174            }
175        }
176        fail("Cannot find nashorn factory!");
177    }
178
179    @Test
180    /**
181     * Test that we can use same script name in repeated evals with --loader-per-compile=false
182     * We used to get "class redefinition error" as name was derived from script name.
183     */
184    public void noLoaderPerCompilerWithSameNameTest() {
185        final ScriptEngineManager sm = new ScriptEngineManager();
186        for (final ScriptEngineFactory fac : sm.getEngineFactories()) {
187            if (fac instanceof NashornScriptEngineFactory) {
188                final NashornScriptEngineFactory nfac = (NashornScriptEngineFactory)fac;
189                final String[] options = new String[] { "--loader-per-compile=false" };
190                final ScriptEngine e = nfac.getScriptEngine(options);
191                e.put(ScriptEngine.FILENAME, "test.js");
192                try {
193                    e.eval("2 + 3");
194                    e.eval("4 + 4");
195                } catch (final ScriptException se) {
196                    se.printStackTrace();
197                    fail(se.getMessage());
198                }
199                return;
200            }
201        }
202        fail("Cannot find nashorn factory!");
203    }
204
205    @Test
206    public void globalPerEngineTest() throws ScriptException {
207        final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
208        final String[] options = new String[] { "--global-per-engine" };
209        final ScriptEngine e = fac.getScriptEngine(options);
210
211        e.eval("function foo() {}");
212
213        final ScriptContext newCtx = new SimpleScriptContext();
214        newCtx.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
215
216        // all global definitions shared and so 'foo' should be
217        // visible in new Bindings as well.
218        assertTrue(e.eval("typeof foo", newCtx).equals("function"));
219
220        e.eval("function bar() {}", newCtx);
221
222        // bar should be visible in default context
223        assertTrue(e.eval("typeof bar").equals("function"));
224    }
225
226    @Test
227    public void classFilterTest() throws ScriptException {
228        final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
229        final ScriptEngine e = fac.getScriptEngine(new ClassFilter() {
230            @Override
231            public boolean exposeToScripts(final String fullName) {
232                // don't allow anything that is not "java."
233                return fullName.startsWith("java.");
234            }
235        });
236
237        assertEquals(e.eval("typeof javax.script.ScriptEngine"), "object");
238        assertEquals(e.eval("typeof java.util.Vector"), "function");
239
240        try {
241            e.eval("Java.type('javax.script.ScriptContext')");
242            fail("should not reach here");
243        } catch (final ScriptException | RuntimeException se) {
244            if (! (se.getCause() instanceof ClassNotFoundException)) {
245                fail("ClassNotFoundException expected");
246            }
247        }
248    }
249
250    @Test
251    public void classFilterTest2() throws ScriptException {
252        final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
253        final ScriptEngine e = fac.getScriptEngine(new String[0], Thread.currentThread().getContextClassLoader(),
254            new ClassFilter() {
255                @Override
256                public boolean exposeToScripts(final String fullName) {
257                    // don't allow anything that is not "java."
258                    return fullName.startsWith("java.");
259                }
260            });
261
262        assertEquals(e.eval("typeof javax.script.ScriptEngine"), "object");
263        assertEquals(e.eval("typeof java.util.Vector"), "function");
264
265        try {
266            e.eval("Java.type('javax.script.ScriptContext')");
267            fail("should not reach here");
268        } catch (final ScriptException | RuntimeException se) {
269            if (! (se.getCause() instanceof ClassNotFoundException)) {
270                fail("ClassNotFoundException expected");
271            }
272        }
273    }
274
275    @Test
276    public void nullClassFilterTest() {
277        final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
278        try {
279            fac.getScriptEngine((ClassFilter)null);
280            fail("should have thrown NPE");
281        } catch (final NullPointerException e) {
282            //empty
283        }
284    }
285
286    @Test
287    public void nullClassFilterTest2() {
288        final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
289        try {
290            fac.getScriptEngine(new String[0], null, null);
291            fail("should have thrown NPE");
292        } catch (final NullPointerException e) {
293            //empty
294        }
295    }
296
297    @Test
298    public void nullArgsTest() {
299        final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
300        try {
301            fac.getScriptEngine((String[])null);
302            fail("should have thrown NPE");
303        } catch (final NullPointerException e) {
304            //empty
305        }
306    }
307
308    @Test
309    public void nullArgsTest2() {
310        final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
311        try {
312            fac.getScriptEngine(null, null, new ClassFilter() {
313                @Override
314                public boolean exposeToScripts(final String name) {
315                    return true;
316                }
317            });
318            fail("should have thrown NPE");
319        } catch (final NullPointerException e) {
320            //empty
321        }
322    }
323
324    @Test
325    public void nashornSwallowsConstKeyword() throws Exception {
326        final NashornScriptEngineFactory f = new NashornScriptEngineFactory();
327        final String[] args = new String[] { "--const-as-var" };
328        final ScriptEngine engine = f.getScriptEngine(args);
329
330        final Object ret = engine.eval(""
331            + "(function() {\n"
332            + "  const x = 10;\n"
333            + "  return x;\n"
334            + "})();"
335        );
336        assertEquals(ret, 10, "Parsed and executed OK");
337    }
338
339    @Test
340    public void evalDefaultFileNameTest() throws ScriptException {
341        final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
342        final ScriptEngine engine = fac.getScriptEngine(new String[] { "--verify-code=true" });
343        // default FILENAME being "<eval>" make sure generated code bytecode verifies.
344        engine.eval("var a = 3;");
345    }
346
347    @Test
348    public void evalFileNameWithSpecialCharsTest() throws ScriptException {
349        final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
350        final ScriptEngine engine = fac.getScriptEngine(new String[] { "--verify-code=true" });
351        final ScriptContext ctxt = new SimpleScriptContext();
352        // use file name with "dangerous" chars.
353        ctxt.setAttribute(ScriptEngine.FILENAME, "<myscript>", ScriptContext.ENGINE_SCOPE);
354        engine.eval("var a = 3;");
355        ctxt.setAttribute(ScriptEngine.FILENAME, "[myscript]", ScriptContext.ENGINE_SCOPE);
356        engine.eval("var h = 'hello';");
357        ctxt.setAttribute(ScriptEngine.FILENAME, ";/\\$.", ScriptContext.ENGINE_SCOPE);
358        engine.eval("var foo = 'world';");
359        // name used by jjs shell tool for the interactive mode
360        ctxt.setAttribute(ScriptEngine.FILENAME, "<shell>", ScriptContext.ENGINE_SCOPE);
361        engine.eval("var foo = 'world';");
362    }
363}
364