src/jdk/nashorn/tools/Shell.java

Wed, 30 Jan 2013 12:26:45 +0100

author
lagergren
date
Wed, 30 Jan 2013 12:26:45 +0100
changeset 57
59970b70ebb5
parent 41
8b3cc4ad1810
child 86
38c44687e4bd
permissions
-rw-r--r--

8007062: Split Lower up into Lower/Attr/FinalizeTypes. Integrate AccessSpecalizer into FinalizeTypes.
Summary: Lower suffered from being a "God class" trying to do everything at once. As Nashorn code generation has grown, so has Lower. It does several post processing passes, tries to do several things at once even though all type information isn't in place, adjusting state afterwards and so on. It also performs control flow analysis, type attribution and constant folding, and everything else code generation related before byte code emission. I have now separated the compilation process into Lower (create low level nodes from high level ones, copy code such as finally block inlining etc), Attr (assign types and symbols to all nodes - freeze slot and scope information) and FinalizeTypes (insert explicit casts, specialize invoke dynamic types for scope accesses). I've removed the kludgy AccessSpecializer, as this now integrates naturally with typing. Everything is now much easier to read and each module performs only one thing. I have added separate loggers for the separate tiers. In the process I have also fixed: (1) problems with type coercion (see test/script/basic/typecoercion.js, basically our coercion was too late and our symbol inference was erroneous. This only manifested itself in very rare occasions where toNumber coercion has side effects, such as for example when valueOf is overridden) (2) copying literal nodes (literal copy did not use the superclass copy, which made all the Node specific fields not to be copied (3) erroneous literal tokenization (literals shouldn't always just inherit token information from whatever node that creates them) (4) splitter weighnodes - unary nodes were considered weightless (4) removed the hateful and kludgy "VarNode.shouldAppend", which really isn't needed when we have an attribution phase that determines self reference symbols (the only thing it was used for) (5) duplicate line number issues in the parser (6) convert bug in CodeGenerator for intermediate results of scope accesses (see test/script/basic/access-specializer.js) ... Several of these things just stopped being problems with the new architecture "can't happen anymore" and are not bug fixes per se. All tests run. No performance regressions exist that I've been able to measure. Some increases in performance were measured, but in the statistical margin of error (which is very wide as HotSpot currently has warmup issues with LambdaForms/invoke dynamic). Compile speed has not measurably increased.
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.runtime.Context;
    45 import jdk.nashorn.internal.runtime.ErrorManager;
    46 import jdk.nashorn.internal.runtime.ScriptFunction;
    47 import jdk.nashorn.internal.runtime.ScriptObject;
    48 import jdk.nashorn.internal.runtime.ScriptRuntime;
    49 import jdk.nashorn.internal.runtime.Source;
    50 import jdk.nashorn.internal.runtime.options.Options;
    52 /**
    53  * Command line Shell for processing JavaScript files.
    54  */
    55 public class Shell {
    57     /**
    58      * Resource name for properties file
    59      */
    60     private static final String MESSAGE_RESOURCE = "jdk.nashorn.tools.resources.Shell";
    61     /**
    62      * Shell message bundle.
    63      */
    64     private static ResourceBundle bundle;
    66     static {
    67         // Without do privileged, under security manager messages can not be
    68         // loaded.
    69         bundle = AccessController.doPrivileged(new PrivilegedAction<ResourceBundle>() {
    70             @Override
    71             public ResourceBundle run() {
    72                 return ResourceBundle.getBundle(MESSAGE_RESOURCE, Locale.getDefault());
    73             }
    74         });
    75     }
    77     /**
    78      * Exit code for command line tool - successful
    79      */
    80     public static final int SUCCESS = 0;
    81     /**
    82      * Exit code for command line tool - error on command line
    83      */
    84     public static final int COMMANDLINE_ERROR = 100;
    85     /**
    86      * Exit code for command line tool - error compiling script
    87      */
    88     public static final int COMPILATION_ERROR = 101;
    89     /**
    90      * Exit code for command line tool - error during runtime
    91      */
    92     public static final int RUNTIME_ERROR = 102;
    93     /**
    94      * Exit code for command line tool - i/o error
    95      */
    96     public static final int IO_ERROR = 103;
    97     /**
    98      * Exit code for command line tool - internal error
    99      */
   100     public static final int INTERNAL_ERROR = 104;
   102     /**
   103      * Constructor
   104      */
   105     protected Shell() {
   106     }
   108     /**
   109      * Main entry point with the default input, output and error streams.
   110      *
   111      * @param args The command line arguments
   112      */
   113     public static void main(final String[] args) {
   114         try {
   115             System.exit(main(System.in, System.out, System.err, args));
   116         } catch (final IOException e) {
   117             System.err.println(e); //bootstrapping, Context.err may not exist
   118             System.exit(IO_ERROR);
   119         }
   120     }
   122     /**
   123      * Starting point for executing a {@code Shell}. Starts a shell with the
   124      * given arguments and streams and lets it run until exit.
   125      *
   126      * @param in input stream for Shell
   127      * @param out output stream for Shell
   128      * @param err error stream for Shell
   129      * @param args arguments to Shell
   130      *
   131      * @return exit code
   132      *
   133      * @throws IOException if there's a problem setting up the streams
   134      */
   135     public static int main(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) throws IOException {
   136         return new Shell().run(in, out, err, args);
   137     }
   139     /**
   140      * Run method logic.
   141      *
   142      * @param in input stream for Shell
   143      * @param out output stream for Shell
   144      * @param err error stream for Shell
   145      * @param args arguments to Shell
   146      *
   147      * @return exit code
   148      *
   149      * @throws IOException if there's a problem setting up the streams
   150      */
   151     protected final int run(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) throws IOException {
   152         final Context context = makeContext(in, out, err, args);
   153         if (context == null) {
   154             return COMMANDLINE_ERROR;
   155         }
   157         final ScriptObject global = context.createGlobal();
   158         final List<String> files = context.getOptions().getFiles();
   159         if (files.isEmpty()) {
   160             return readEvalPrint(context, global);
   161         }
   163         if (context._compile_only) {
   164             return compileScripts(context, global, files);
   165         }
   167         return runScripts(context, global, files);
   168     }
   170     /**
   171      * Make a new Nashorn Context to compile and/or run JavaScript files.
   172      *
   173      * @param in input stream for Shell
   174      * @param out output stream for Shell
   175      * @param err error stream for Shell
   176      * @param args arguments to Shell
   177      *
   178      * @return null if there are problems with option parsing.
   179      */
   180     @SuppressWarnings("resource")
   181     private static Context makeContext(final InputStream in, final OutputStream out, final OutputStream err, final String[] args) {
   182         final PrintStream pout = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out);
   183         final PrintStream perr = err instanceof PrintStream ? (PrintStream) err : new PrintStream(err);
   184         final PrintWriter wout = new PrintWriter(pout, true);
   185         final PrintWriter werr = new PrintWriter(perr, true);
   187         // Set up error handler.
   188         final ErrorManager errors = new ErrorManager(werr);
   189         // Set up options.
   190         final Options options = new Options("nashorn", werr);
   192         // parse options
   193         try {
   194             options.process(args);
   195         } catch (final IllegalArgumentException e) {
   196             werr.println(bundle.getString("shell.usage"));
   197             options.displayHelp(e);
   198             return null;
   199         }
   201         // detect scripting mode by any source's first character being '#'
   202         if (!options.getBoolean("scripting")) {
   203             for (final String fileName : options.getFiles()) {
   204                 final File firstFile = new File(fileName);
   205                 if (firstFile.isFile()) {
   206                     try (final FileReader fr = new FileReader(firstFile)) {
   207                         final int firstChar = fr.read();
   208                         // starts with '#
   209                         if (firstChar == '#') {
   210                             options.set("scripting", true);
   211                             break;
   212                         }
   213                     } catch (final IOException e) {
   214                         // ignore this. File IO errors will be reported later anyway
   215                     }
   216                 }
   217             }
   218         }
   220         return new Context(options, errors, wout, werr, Thread.currentThread().getContextClassLoader());
   221     }
   223     /**
   224      * Compiles the given script files in the command line
   225      *
   226      * @param context the nashorn context
   227      * @param global the global scope
   228      * @param files the list of script files to compile
   229      *
   230      * @return error code
   231      * @throws IOException when any script file read results in I/O error
   232      */
   233     private static int compileScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
   234         final ScriptObject oldGlobal = Context.getGlobal();
   235         final boolean globalChanged = (oldGlobal != global);
   236         try {
   237             if (globalChanged) {
   238                 Context.setGlobal(global);
   239             }
   240             final ErrorManager errors = context.getErrorManager();
   242             // For each file on the command line.
   243             for (final String fileName : files) {
   244                 final File file = new File(fileName);
   245                 final Source source = new Source(fileName, file);
   246                 final Compiler compiler = Compiler.compiler(source, context);
   247                 compiler.compile();
   248                 if (errors.getNumberOfErrors() != 0) {
   249                     return COMPILATION_ERROR;
   250                 }
   251             }
   252         } finally {
   253             context.getOut().flush();
   254             context.getErr().flush();
   255             if (globalChanged) {
   256                 Context.setGlobal(oldGlobal);
   257             }
   258         }
   260         return SUCCESS;
   261     }
   263     /**
   264      * Runs the given JavaScript files in the command line
   265      *
   266      * @param context the nashorn context
   267      * @param global the global scope
   268      * @param files the list of script files to run
   269      *
   270      * @return error code
   271      * @throws IOException when any script file read results in I/O error
   272      */
   273     private int runScripts(final Context context, final ScriptObject global, final List<String> files) throws IOException {
   274         final ScriptObject oldGlobal = Context.getGlobal();
   275         final boolean globalChanged = (oldGlobal != global);
   276         try {
   277             if (globalChanged) {
   278                 Context.setGlobal(global);
   279             }
   280             final ErrorManager errors = context.getErrorManager();
   282             // For each file on the command line.
   283             for (final String fileName : files) {
   284                 final File file = new File(fileName);
   285                 ScriptFunction script = context.compileScript(fileName, file.toURI().toURL(), global, context._strict);
   286                 if (script == null || errors.getNumberOfErrors() != 0) {
   287                     return COMPILATION_ERROR;
   288                 }
   290                 try {
   291                     apply(script, global);
   292                 } catch (final NashornException e) {
   293                     errors.error(e.toString());
   294                     if (context._dump_on_error) {
   295                         e.printStackTrace(context.getErr());
   296                     }
   298                     return RUNTIME_ERROR;
   299                 }
   300             }
   301         } finally {
   302             context.getOut().flush();
   303             context.getErr().flush();
   304             if (globalChanged) {
   305                 Context.setGlobal(oldGlobal);
   306             }
   307         }
   309         return SUCCESS;
   310     }
   312     /**
   313      * Hook to ScriptFunction "apply". A performance metering shell may
   314      * introduce enter/exit timing here.
   315      *
   316      * @param target target function for apply
   317      * @param self self reference for apply
   318      *
   319      * @return result of the function apply
   320      */
   321     protected Object apply(final ScriptFunction target, final Object self) {
   322         return ScriptRuntime.apply(target, self);
   323     }
   325     /**
   326      * read-eval-print loop for Nashorn shell.
   327      *
   328      * @param context the nashorn context
   329      * @param global  global scope object to use
   330      * @return return code
   331      */
   332     @SuppressWarnings("resource")
   333     private static int readEvalPrint(final Context context, final ScriptObject global) {
   334         final String prompt = bundle.getString("shell.prompt");
   335         final BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
   336         final PrintWriter err = context.getErr();
   337         final ScriptObject oldGlobal = Context.getGlobal();
   338         final boolean globalChanged = (oldGlobal != global);
   340         try {
   341             if (globalChanged) {
   342                 Context.setGlobal(global);
   343             }
   345             // initialize with "shell.js" script
   346             try {
   347                 final Source source = new Source("<shell.js>", Shell.class.getResource("resources/shell.js"));
   348                 context.eval(global, source.getString(), global, "<shell.js>", false);
   349             } catch (final Exception e) {
   350                 err.println(e);
   351                 if (context._dump_on_error) {
   352                     e.printStackTrace(err);
   353                 }
   355                 return INTERNAL_ERROR;
   356             }
   358             while (true) {
   359                 err.print(prompt);
   360                 err.flush();
   362                 String source = "";
   363                 try {
   364                     source = in.readLine();
   365                 } catch (final IOException ioe) {
   366                     err.println(ioe.toString());
   367                 }
   369                 if (source == null) {
   370                     break;
   371                 }
   373                 Object res;
   374                 try {
   375                     res = context.eval(global, source, global, "<shell>", context._strict);
   376                 } catch (final Exception e) {
   377                     err.println(e);
   378                     if (context._dump_on_error) {
   379                         e.printStackTrace(err);
   380                     }
   381                     continue;
   382                 }
   384                 if (res != null && res != ScriptRuntime.UNDEFINED) {
   385                     err.println(ScriptRuntime.safeToString(res));
   386                 }
   387             }
   388         } finally {
   389             if (globalChanged) {
   390                 Context.setGlobal(global);
   391             }
   392         }
   394         return SUCCESS;
   395     }
   396 }

mercurial