src/jdk/nashorn/tools/Shell.java

Tue, 07 May 2013 14:36:57 +0200

author
lagergren
date
Tue, 07 May 2013 14:36:57 +0200
changeset 252
544e17632e96
parent 248
829b06307fb2
child 253
fb1d7ea3e1b6
permissions
-rw-r--r--

8013913: Removed Source field from all nodes except FunctionNode in order to save footprint
Reviewed-by: jlaskey, attila

     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  */
    26 package jdk.nashorn.tools;
    28 import java.io.BufferedReader;
    29 import java.io.File;
    30 import java.io.FileReader;
    31 import java.io.IOException;
    32 import java.io.InputStream;
    33 import java.io.InputStreamReader;
    34 import java.io.OutputStream;
    35 import java.io.PrintStream;
    36 import java.io.PrintWriter;
    37 import java.security.AccessController;
    38 import java.security.PrivilegedAction;
    39 import java.util.List;
    40 import java.util.Locale;
    41 import java.util.ResourceBundle;
    42 import jdk.nashorn.api.scripting.NashornException;
    43 import jdk.nashorn.internal.codegen.Compiler;
    44 import jdk.nashorn.internal.ir.FunctionNode;
    45 import jdk.nashorn.internal.ir.debug.ASTWriter;
    46 import jdk.nashorn.internal.ir.debug.PrintVisitor;
    47 import jdk.nashorn.internal.parser.Parser;
    48 import jdk.nashorn.internal.runtime.Context;
    49 import jdk.nashorn.internal.runtime.ErrorManager;
    50 import jdk.nashorn.internal.runtime.Property;
    51 import jdk.nashorn.internal.runtime.ScriptEnvironment;
    52 import jdk.nashorn.internal.runtime.ScriptFunction;
    53 import jdk.nashorn.internal.runtime.ScriptObject;
    54 import jdk.nashorn.internal.runtime.ScriptRuntime;
    55 import jdk.nashorn.internal.runtime.Source;
    56 import jdk.nashorn.internal.runtime.options.Options;
    58 /**
    59  * Command line Shell for processing JavaScript files.
    60  */
    61 public class Shell {
    63     /**
    64      * Resource name for properties file
    65      */
    66     private static final String MESSAGE_RESOURCE = "jdk.nashorn.tools.resources.Shell";
    67     /**
    68      * Shell message bundle.
    69      */
    70     private static ResourceBundle bundle;
    72     static {
    73         // Without do privileged, under security manager messages can not be
    74         // loaded.
    75         bundle = AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() {
    76             @Override
    77             public ResourceBundle run() {
    78                 return ResourceBundle.getBundle(MESSAGE_RESOURCE, Locale.getDefault());
    79             }
    80         });
    81     }
    83     /**
    84      * Exit code for command line tool - successful
    85      */
    86     public static final int SUCCESS = 0;
    87     /**
    88      * Exit code for command line tool - error on command line
    89      */
    90     public static final int COMMANDLINE_ERROR = 100;
    91     /**
    92      * Exit code for command line tool - error compiling script
    93      */
    94     public static final int COMPILATION_ERROR = 101;
    95     /**
    96      * Exit code for command line tool - error during runtime
    97      */
    98     public static final int RUNTIME_ERROR = 102;
    99     /**
   100      * Exit code for command line tool - i/o error
   101      */
   102     public static final int IO_ERROR = 103;
   103     /**
   104      * Exit code for command line tool - internal error
   105      */
   106     public static final int INTERNAL_ERROR = 104;
   108     /**
   109      * Constructor
   110      */
   111     protected Shell() {
   112     }
   114     /**
   115      * Main entry point with the default input, output and error streams.
   116      *
   117      * @param args The command line arguments
   118      */
   119     public static void main(final String[] args) {
   120         try {
   121             System.exit(main(System.in, System.out, System.err, args));
   122         } catch (final IOException e) {
   123             System.err.println(e); //bootstrapping, Context.err may not exist
   124             System.exit(IO_ERROR);
   125         }
   126     }
   128     /**
   129      * Starting point for executing a {@code Shell}. Starts a shell with the
   130      * given arguments and streams and lets it run until exit.
   131      *
   132      * @param in input stream for Shell
   133      * @param out output stream for Shell
   134      * @param err error stream for Shell
   135      * @param args arguments to Shell
   136      *
   137      * @return exit code
   138      *
   139      * @throws IOException if there's a problem setting up the streams
   140      */
   141     public static int main(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) throws IOException {
   142         return new Shell().run(in, out, err, args);
   143     }
   145     /**
   146      * Run method logic.
   147      *
   148      * @param in input stream for Shell
   149      * @param out output stream for Shell
   150      * @param err error stream for Shell
   151      * @param args arguments to Shell
   152      *
   153      * @return exit code
   154      *
   155      * @throws IOException if there's a problem setting up the streams
   156      */
   157     protected final int run(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) throws IOException {
   158         final Context context = makeContext(in, out, err, args);
   159         if (context == null) {
   160             return COMMANDLINE_ERROR;
   161         }
   163         final ScriptObject global = context.createGlobal();
   164         final ScriptEnvironment env = context.getEnv();
   165         final List<String> files = env.getFiles();
   166         if (files.isEmpty()) {
   167             return readEvalPrint(context, global);
   168         }
   170         if (env._compile_only) {
   171             return compileScripts(context, global, files);
   172         }
   174         if (env._fx) {
   175             return runFXScripts(context, global, files);
   176         }
   178         return runScripts(context, global, files);
   179     }
   181     /**
   182      * Make a new Nashorn Context to compile and/or run JavaScript files.
   183      *
   184      * @param in input stream for Shell
   185      * @param out output stream for Shell
   186      * @param err error stream for Shell
   187      * @param args arguments to Shell
   188      *
   189      * @return null if there are problems with option parsing.
   190      */
   191     @SuppressWarnings("resource")
   192     private static Context makeContext(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) {
   193         final PrintStream pout = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out);
   194         final PrintStream perr = err instanceof PrintStream ? (PrintStream) err : new PrintStream(err);
   195         final PrintWriter wout = new PrintWriter(pout, true);
   196         final PrintWriter werr = new PrintWriter(perr, true);
   198         // Set up error handler.
   199         final ErrorManager errors = new ErrorManager(werr);
   200         // Set up options.
   201         final Options options = new Options("nashorn", werr);
   203         // parse options
   204         if (args != null) {
   205             try {
   206                 options.process(args);
   207             } catch (final IllegalArgumentException e) {
   208                 werr.println(bundle.getString("shell.usage"));
   209                 options.displayHelp(e);
   210                 return null;
   211             }
   212         }
   214         // detect scripting mode by any source's first character being '#'
   215         if (!options.getBoolean("scripting")) {
   216             for (final String fileName : options.getFiles()) {
   217                 final File firstFile = new File(fileName);
   218                 if (firstFile.isFile()) {
   219                     try (final FileReader fr = new FileReader(firstFile)) {
   220                         final int firstChar = fr.read();
   221                         // starts with '#
   222                         if (firstChar == '#') {
   223                             options.set("scripting", true);
   224                             break;
   225                         }
   226                     } catch (final IOException e) {
   227                         // ignore this. File IO errors will be reported later anyway
   228                     }
   229                 }
   230             }
   231         }
   233         return new Context(options, errors, wout, werr, Thread.currentThread().getContextClassLoader());
   234     }
   236     /**
   237      * Compiles the given script files in the command line
   238      *
   239      * @param context the nashorn context
   240      * @param global the global scope
   241      * @param files the list of script files to compile
   242      *
   243      * @return error code
   244      * @throws IOException when any script file read results in I/O error
   245      */
   246     private static int compileScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
   247         final ScriptObject oldGlobal = Context.getGlobal();
   248         final boolean globalChanged = (oldGlobal != global);
   249         final ScriptEnvironment env = context.getEnv();
   250         try {
   251             if (globalChanged) {
   252                 Context.setGlobal(global);
   253             }
   254             final ErrorManager errors = context.getErrorManager();
   256             // For each file on the command line.
   257             for (final String fileName : files) {
   258                 final FunctionNode functionNode = new Parser(env, new Source(fileName, new File(fileName)), errors).parse();
   260                 if (errors.getNumberOfErrors() != 0) {
   261                     return COMPILATION_ERROR;
   262                 }
   264                 if (env._print_ast) {
   265                     context.getErr().println(new ASTWriter(functionNode));
   266                 }
   268                 if (env._print_parse) {
   269                     context.getErr().println(new PrintVisitor(functionNode));
   270                 }
   272                 //null - pass no code installer - this is compile only
   273                 new Compiler(env).compile(functionNode);
   274             }
   275         } finally {
   276             env.getOut().flush();
   277             env.getErr().flush();
   278             if (globalChanged) {
   279                 Context.setGlobal(oldGlobal);
   280             }
   281         }
   283         return SUCCESS;
   284     }
   286     /**
   287      * Runs the given JavaScript files in the command line
   288      *
   289      * @param context the nashorn context
   290      * @param global the global scope
   291      * @param files the list of script files to run
   292      *
   293      * @return error code
   294      * @throws IOException when any script file read results in I/O error
   295      */
   296     private int runScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
   297         final ScriptObject oldGlobal = Context.getGlobal();
   298         final boolean globalChanged = (oldGlobal != global);
   299         try {
   300             if (globalChanged) {
   301                 Context.setGlobal(global);
   302             }
   303             final ErrorManager errors = context.getErrorManager();
   305             // For each file on the command line.
   306             for (final String fileName : files) {
   307                 final File file = new File(fileName);
   308                 final ScriptFunction script = context.compileScript(new Source(fileName, file.toURI().toURL()), global);
   309                 if (script == null || errors.getNumberOfErrors() != 0) {
   310                     return COMPILATION_ERROR;
   311                 }
   313                 try {
   314                     apply(script, global);
   315                 } catch (final NashornException e) {
   316                     errors.error(e.toString());
   317                     if (context.getEnv()._dump_on_error) {
   318                         e.printStackTrace(context.getErr());
   319                     }
   321                     return RUNTIME_ERROR;
   322                 }
   323             }
   324         } finally {
   325             context.getOut().flush();
   326             context.getErr().flush();
   327             if (globalChanged) {
   328                 Context.setGlobal(oldGlobal);
   329             }
   330         }
   332         return SUCCESS;
   333     }
   335     /**
   336      * Runs launches "fx:bootstrap.js" with the given JavaScript files provided
   337      * as arguments.
   338      *
   339      * @param context the nashorn context
   340      * @param global the global scope
   341      * @param files the list of script files to provide
   342      *
   343      * @return error code
   344      * @throws IOException when any script file read results in I/O error
   345      */
   346     private static int runFXScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
   347         final ScriptObject oldGlobal = Context.getGlobal();
   348         final boolean globalChanged = (oldGlobal != global);
   349         try {
   350             if (globalChanged) {
   351                 Context.setGlobal(global);
   352             }
   354             global.addOwnProperty("$GLOBAL", Property.NOT_ENUMERABLE, global);
   355             global.addOwnProperty("$SCRIPTS", Property.NOT_ENUMERABLE, files);
   356             context.load(global, "fx:bootstrap.js");
   357         } catch (final NashornException e) {
   358             context.getErrorManager().error(e.toString());
   359             if (context.getEnv()._dump_on_error) {
   360                 e.printStackTrace(context.getErr());
   361             }
   363             return RUNTIME_ERROR;
   364         } finally {
   365             context.getOut().flush();
   366             context.getErr().flush();
   367             if (globalChanged) {
   368                 Context.setGlobal(oldGlobal);
   369             }
   370         }
   372         return SUCCESS;
   373     }
   375     /**
   376      * Hook to ScriptFunction "apply". A performance metering shell may
   377      * introduce enter/exit timing here.
   378      *
   379      * @param target target function for apply
   380      * @param self self reference for apply
   381      *
   382      * @return result of the function apply
   383      */
   384     protected Object apply(final ScriptFunction target, final Object self) {
   385         return ScriptRuntime.apply(target, self);
   386     }
   388     /**
   389      * read-eval-print loop for Nashorn shell.
   390      *
   391      * @param context the nashorn context
   392      * @param global  global scope object to use
   393      * @return return code
   394      */
   395     private static int readEvalPrint(final Context context, final ScriptObject global) {
   396         final String prompt = bundle.getString("shell.prompt");
   397         final BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
   398         final PrintWriter err = context.getErr();
   399         final ScriptObject oldGlobal = Context.getGlobal();
   400         final boolean globalChanged = (oldGlobal != global);
   401         final ScriptEnvironment env = context.getEnv();
   403         try {
   404             if (globalChanged) {
   405                 Context.setGlobal(global);
   406             }
   408             // initialize with "shell.js" script
   409             try {
   410                 final Source source = new Source("<shell.js>", Shell.class.getResource("resources/shell.js"));
   411                 context.eval(global, source.getString(), global, "<shell.js>", false);
   412             } catch (final Exception e) {
   413                 err.println(e);
   414                 if (env._dump_on_error) {
   415                     e.printStackTrace(err);
   416                 }
   418                 return INTERNAL_ERROR;
   419             }
   421             while (true) {
   422                 err.print(prompt);
   423                 err.flush();
   425                 String source = "";
   426                 try {
   427                     source = in.readLine();
   428                 } catch (final IOException ioe) {
   429                     err.println(ioe.toString());
   430                 }
   432                 if (source == null) {
   433                     break;
   434                 }
   436                 Object res;
   437                 try {
   438                     res = context.eval(global, source, global, "<shell>", env._strict);
   439                 } catch (final Exception e) {
   440                     err.println(e);
   441                     if (env._dump_on_error) {
   442                         e.printStackTrace(err);
   443                     }
   444                     continue;
   445                 }
   447                 if (res != null && res != ScriptRuntime.UNDEFINED) {
   448                     err.println(ScriptRuntime.safeToString(res));
   449                 }
   450             }
   451         } finally {
   452             if (globalChanged) {
   453                 Context.setGlobal(global);
   454             }
   455         }
   457         return SUCCESS;
   458     }
   459 }

mercurial