       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
      26 package jdk.nashorn.internal.runtime;
      27
      28 import java.io.PrintWriter;
      29 import java.util.HashMap;
      30 import java.util.List;
      31 import java.util.Locale;
      32 import java.util.Map;
      33 import java.util.StringTokenizer;
      34 import java.util.TimeZone;
      35 import java.util.logging.Level;
      36 import jdk.nashorn.internal.codegen.Namespace;
      37 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
      38 import jdk.nashorn.internal.runtime.options.KeyValueOption;
      39 import jdk.nashorn.internal.runtime.options.LoggingOption;
      40 import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo;
      41 import jdk.nashorn.internal.runtime.options.Option;
      42 import jdk.nashorn.internal.runtime.options.Options;
      43
      44 /**
      45  * Script environment consists of command line options, arguments, script files
      46  * and output and error writers, top level Namespace etc.
      47  */
      48 public final class ScriptEnvironment {
      49     // Primarily intended to be used in test environments so that eager compilation tests work without an
      50     // error when tested with optimistic compilation.
      51     private static final boolean ALLOW_EAGER_COMPILATION_SILENT_OVERRIDE = Options.getBooleanProperty(
      52             "nashorn.options.allowEagerCompilationSilentOverride", false);
      53
      54     /** Output writer for this environment */
      55     private final PrintWriter out;
      56
      57     /** Error writer for this environment */
      58     private final PrintWriter err;
      59
      60     /** Top level namespace. */
      61     private final Namespace namespace;
      62
      63     /** Current Options object. */
      64     private final Options options;
      65
      66     /** Size of the per-global Class cache size */
      67     public final int     _class_cache_size;
      68
      69     /** -classpath value. */
      70     public final String  _classpath;
      71
      72     /** Only compile script, do not run it or generate other ScriptObjects */
      73     public final boolean _compile_only;
      74
      75     /** Accept "const" keyword and treat it as variable. Interim feature */
      76     public final boolean _const_as_var;
      77
      78     /** Accumulated callsite flags that will be used when bootstrapping script callsites */
      79     public final int     _callsite_flags;
      80
      81     /** Generate line number table in class files */
      82     public final boolean _debug_lines;
      83
      84     /** Put all variables in scopes to make them debuggable */
      85     public final boolean _debug_scopes;
      86
      87     /** Directory in which source files and generated class files are dumped */
      88     public final String  _dest_dir;
      89
      90     /** Display stack trace upon error, default is false */
      91     public final boolean _dump_on_error;
      92
      93     /** Invalid lvalue expressions should be reported as early errors */
      94     public final boolean _early_lvalue_error;
      95
      96     /** Empty statements should be preserved in the AST */
      97     public final boolean _empty_statements;
      98
      99     /** Show full Nashorn version */
     100     public final boolean _fullversion;
     101
     102     /** Launch using as fx application */
     103     public final boolean _fx;
     104
     105     /** Use single Global instance per jsr223 engine instance. */
     106     public final boolean _global_per_engine;
     107
     108     /** Enable experimental ECMAScript 6 features. */
     109     public final boolean _es6;
     110
     111
     112     /** Number of times a dynamic call site has to be relinked before it is
     113      * considered unstable (and thus should be linked as if it were megamorphic).
     114      */
     115     public final int _unstable_relink_threshold;
     116
     117     /** Argument passed to compile only if optimistic compilation should take place */
     118     public static final String COMPILE_ONLY_OPTIMISTIC_ARG = "optimistic";
     119
     120     /**
     121      *  Behavior when encountering a function declaration in a lexical context where only statements are acceptable
     122      * (function declarations are source elements, but not statements).
     123      */
     124     public enum FunctionStatementBehavior {
     125         /**
     126          * Accept the function declaration silently and treat it as if it were a function expression assigned to a local
     127          * variable.
     128          */
     129         ACCEPT,
     130         /**
     131          * Log a parser warning, but accept the function declaration and treat it as if it were a function expression
     132          * assigned to a local variable.
     133          */
     134         WARNING,
     135         /**
     136          * Raise a {@code SyntaxError}.
     137          */
     138         ERROR
     139     }
     140
     141     /**
     142      * Behavior when encountering a function declaration in a lexical context where only statements are acceptable
     143      * (function declarations are source elements, but not statements).
     144      */
     145     public final FunctionStatementBehavior _function_statement;
     146
     147     /** Should lazy compilation take place */
     148     public final boolean _lazy_compilation;
     149
     150     /** Should optimistic types be used */
     151     public final boolean _optimistic_types;
     152
     153     /** Create a new class loaded for each compilation */
     154     public final boolean _loader_per_compile;
     155
     156     /** --module-path, if any */
     157     public final String _module_path;
     158
     159     /** --add-modules, if any */
     160     public final String _add_modules;
     161
     162     /** Do not support Java support extensions. */
     163     public final boolean _no_java;
     164
     165     /** Do not support non-standard syntax extensions. */
     166     public final boolean _no_syntax_extensions;
     167
     168     /** Do not support typed arrays. */
     169     public final boolean _no_typed_arrays;
     170
     171     /** Only parse the source code, do not compile */
     172     public final boolean _parse_only;
     173
     174     /** Enable disk cache for compiled scripts */
     175     public final boolean _persistent_cache;
     176
     177     /** Print the AST before lowering */
     178     public final boolean _print_ast;
     179
     180     /** Print the AST after lowering */
     181     public final boolean _print_lower_ast;
     182
     183     /** Print resulting bytecode for script */
     184     public final boolean _print_code;
     185
     186     /** Directory (optional) to print files to */
     187     public final String _print_code_dir;
     188
     189     /** List of functions to write to the print code dir, optional */
     190     public final String _print_code_func;
     191
     192     /** Print function will no print newline characters */
     193     public final boolean _print_no_newline;
     194
     195     /** Print AST in more human readable form */
     196     public final boolean _print_parse;
     197
     198     /** Print AST in more human readable form after Lowering */
     199     public final boolean _print_lower_parse;
     200
     201     /** print symbols and their contents for the script */
     202     public final boolean _print_symbols;
     203
     204     /** is this environment in scripting mode? */
     205     public final boolean _scripting;
     206
     207     /** is this environment in strict mode? */
     208     public final boolean _strict;
     209
     210     /** print version info of Nashorn */
     211     public final boolean _version;
     212
     213     /** should code verification be done of generated bytecode */
     214     public final boolean _verify_code;
     215
     216     /** time zone for this environment */
     217     public final TimeZone _timezone;
     218
     219     /** Local for error messages */
     220     public final Locale _locale;
     221
     222     /** Logging */
     223     public final Map<String, LoggerInfo> _loggers;
     224
     225     /** Timing */
     226     public final Timing _timing;
     227
     228     /** Whether to use anonymous classes. See {@link #useAnonymousClasses(int)}. */
     229     private final AnonymousClasses _anonymousClasses;
     230     private enum AnonymousClasses {
     231         AUTO,
     232         OFF,
     233         ON
     234     }
     235
     236     /** Size threshold up to which we use anonymous classes in {@link AnonymousClasses#AUTO} setting */
     237     private final int _anonymous_classes_threshold;
     238
     239     /** Default value for anonymous class threshold */
     240     private final static int DEFAULT_ANON_CLASS_THRESHOLD = 512;
     241
     242     /**
     243      * Constructor
     244      *
     245      * @param options a Options object
     246      * @param out output print writer
     247      * @param err error print writer
     248      */
     249     @SuppressWarnings("unused")
     250     public ScriptEnvironment(final Options options, final PrintWriter out, final PrintWriter err) {
     251         this.out = out;
     252         this.err = err;
     253         this.namespace = new Namespace();
     254         this.options = options;
     255
     256         _class_cache_size     = options.getInteger("class.cache.size");
     257         _classpath            = options.getString("classpath");
     258         _compile_only         = options.getBoolean("compile.only");
     259         _const_as_var         = options.getBoolean("const.as.var");
     260         _debug_lines          = options.getBoolean("debug.lines");
     261         _debug_scopes         = options.getBoolean("debug.scopes");
     262         _dest_dir             = options.getString("d");
     263         _dump_on_error        = options.getBoolean("doe");
     264         _early_lvalue_error   = options.getBoolean("early.lvalue.error");
     265         _empty_statements     = options.getBoolean("empty.statements");
     266         _fullversion          = options.getBoolean("fullversion");
     267         if (options.getBoolean("function.statement.error")) {
     268             _function_statement = FunctionStatementBehavior.ERROR;
     269         } else if (options.getBoolean("function.statement.warning")) {
     270             _function_statement = FunctionStatementBehavior.WARNING;
     271         } else {
     272             _function_statement = FunctionStatementBehavior.ACCEPT;
     273         }
     274         _fx                   = options.getBoolean("fx");
     275         _global_per_engine    = options.getBoolean("global.per.engine");
     276         _optimistic_types     = options.getBoolean("optimistic.types");
     277         final boolean lazy_compilation = options.getBoolean("lazy.compilation");
     278         if (!lazy_compilation && _optimistic_types) {
     279             if (!ALLOW_EAGER_COMPILATION_SILENT_OVERRIDE) {
     280                 throw new IllegalStateException(
     281                         ECMAErrors.getMessage(
     282                                 "config.error.eagerCompilationConflictsWithOptimisticTypes",
     283                                 options.getOptionTemplateByKey("lazy.compilation").getName(),
     284                                 options.getOptionTemplateByKey("optimistic.types").getName()));
     285             }
     286             _lazy_compilation = true;
     287         } else {
     288             _lazy_compilation = lazy_compilation;
     289         }
     290         _loader_per_compile   = options.getBoolean("loader.per.compile");
     291         _module_path          = options.getString("module.path");
     292         _add_modules          = options.getString("add.modules");
     293         _no_java              = options.getBoolean("no.java");
     294         _no_syntax_extensions = options.getBoolean("no.syntax.extensions");
     295         _no_typed_arrays      = options.getBoolean("no.typed.arrays");
     296         _parse_only           = options.getBoolean("parse.only");
     297         _persistent_cache     = options.getBoolean("persistent.code.cache");
     298         _print_ast            = options.getBoolean("print.ast");
     299         _print_lower_ast      = options.getBoolean("print.lower.ast");
     300         _print_code           = options.getString("print.code") != null;
     301         _print_no_newline     = options.getBoolean("print.no.newline");
     302         _print_parse          = options.getBoolean("print.parse");
     303         _print_lower_parse    = options.getBoolean("print.lower.parse");
     304         _print_symbols        = options.getBoolean("print.symbols");
     305         _scripting            = options.getBoolean("scripting");
     306         _strict               = options.getBoolean("strict");
     307         _version              = options.getBoolean("version");
     308         _verify_code          = options.getBoolean("verify.code");
     309
     310         final int configuredUrt = options.getInteger("unstable.relink.threshold");
     311         // The default for this property is -1, so we can easily detect when
     312         // it is not specified on command line.
     313         if (configuredUrt < 0) {
     314             // In this case, use a default of 8, or 16 for optimistic types.
     315             // Optimistic types come with dual fields, and in order to get
     316             // performance on benchmarks with a lot of object instantiation and
     317             // then field reassignment, it can take slightly more relinks to
     318             // become stable with type changes swapping out an entire property
     319             // map and making a map guard fail. Also, honor the "nashorn.*"
     320             // system property for now. It was documented in DEVELOPER_README
     321             // so we should recognize it for the time being.
     322             _unstable_relink_threshold = Options.getIntProperty(
     323                     "nashorn.unstable.relink.threshold",
     324                     _optimistic_types ? 16 : 8);
     325         } else {
     326             _unstable_relink_threshold = configuredUrt;
     327         }
     328
     329         final String anonClasses = options.getString("anonymous.classes");
     330         if (anonClasses == null || anonClasses.equals("auto")) {
     331             _anonymousClasses = AnonymousClasses.AUTO;
     332         } else if (anonClasses.equals("true")) {
     333             _anonymousClasses = AnonymousClasses.ON;
     334         } else if (anonClasses.equals("false")) {
     335             _anonymousClasses = AnonymousClasses.OFF;
     336         } else {
     337             throw new RuntimeException("Unsupported value for anonymous classes: " + anonClasses);
     338         }
     339
     340         this._anonymous_classes_threshold = Options.getIntProperty(
     341                 "nashorn.anonymous.classes.threshold", DEFAULT_ANON_CLASS_THRESHOLD);
     342
     343         final String language = options.getString("language");
     344         if (language == null || language.equals("es5")) {
     345             _es6 = false;
     346         } else if (language.equals("es6")) {
     347             _es6 = true;
     348         } else {
     349             throw new RuntimeException("Unsupported language: " + language);
     350         }
     351
     352         String dir = null;
     353         String func = null;
     354         final String pc = options.getString("print.code");
     355         if (pc != null) {
     356             final StringTokenizer st = new StringTokenizer(pc, ",");
     357             while (st.hasMoreTokens()) {
     358                 final StringTokenizer st2 = new StringTokenizer(st.nextToken(), ":");
     359                 while (st2.hasMoreTokens()) {
     360                     final String cmd = st2.nextToken();
     361                     if ("dir".equals(cmd)) {
     362                         dir = st2.nextToken();
     363                     } else if ("function".equals(cmd)) {
     364                         func = st2.nextToken();
     365                     }
     366                 }
     367             }
     368         }
     369         _print_code_dir = dir;
     370         _print_code_func = func;
     371
     372         int callSiteFlags = 0;
     373         if (options.getBoolean("profile.callsites")) {
     374             callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_PROFILE;
     375         }
     376
     377         if (options.get("trace.callsites") instanceof KeyValueOption) {
     378             callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE;
     379             final KeyValueOption kv = (KeyValueOption)options.get("trace.callsites");
     380             if (kv.hasValue("miss")) {
     381                 callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_MISSES;
     382             }
     383             if (kv.hasValue("enterexit") || (callSiteFlags & NashornCallSiteDescriptor.CALLSITE_TRACE_MISSES) == 0) {
     384                 callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_ENTEREXIT;
     385             }
     386             if (kv.hasValue("objects")) {
     387                 callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_VALUES;
     388             }
     389         }
     390         this._callsite_flags = callSiteFlags;
     391
     392         final Option<?> timezoneOption = options.get("timezone");
     393         if (timezoneOption != null) {
     394             this._timezone = (TimeZone)timezoneOption.getValue();
     395         } else {
     396             this._timezone  = TimeZone.getDefault();
     397         }
     398
     399         final Option<?> localeOption = options.get("locale");
     400         if (localeOption != null) {
     401             this._locale = (Locale)localeOption.getValue();
     402         } else {
     403             this._locale = Locale.getDefault();
     404         }
     405
     406         final LoggingOption loggingOption = (LoggingOption)options.get("log");
     407         this._loggers = loggingOption == null ? new HashMap<String, LoggerInfo>() : loggingOption.getLoggers();
     408
     409         final LoggerInfo timeLoggerInfo = _loggers.get(Timing.getLoggerName());
     410         this._timing = new Timing(timeLoggerInfo != null && timeLoggerInfo.getLevel() != Level.OFF);
     411     }
     412
     413     /**
     414      * Get the output stream for this environment
     415      * @return output print writer
     416      */
     417     public PrintWriter getOut() {
     418         return out;
     419     }
     420
     421     /**
     422      * Get the error stream for this environment
     423      * @return error print writer
     424      */
     425     public PrintWriter getErr() {
     426         return err;
     427     }
     428
     429     /**
     430      * Get the namespace for this environment
     431      * @return namespace
     432      */
     433     public Namespace getNamespace() {
     434         return namespace;
     435     }
     436
     437     /**
     438      * Return the JavaScript files passed to the program
     439      *
     440      * @return a list of files
     441      */
     442     public List<String> getFiles() {
     443         return options.getFiles();
     444     }
     445
     446     /**
     447      * Return the user arguments to the program, i.e. those trailing "--" after
     448      * the filename
     449      *
     450      * @return a list of user arguments
     451      */
     452     public List<String> getArguments() {
     453         return options.getArguments();
     454     }
     455
     456     /**
     457      * Check if there is a logger registered for a particular name: typically
     458      * the "name" attribute of a Loggable annotation on a class
     459      *
     460      * @param name logger name
     461      * @return true, if a logger exists for that name, false otherwise
     462      */
     463     public boolean hasLogger(final String name) {
     464         return _loggers.get(name) != null;
     465     }
     466
     467     /**
     468      * Check if compilation/runtime timings are enabled
     469      * @return true if enabled
     470      */
     471     public boolean isTimingEnabled() {
     472         return _timing != null ? _timing.isEnabled() : false;
     473     }
     474
     475     /**
     476      * Returns true if compilation should use anonymous classes.
     477      * @param sourceLength length of source being compiled.
     478      * @return true if anonymous classes should be used
     479      */
     480     public boolean useAnonymousClasses(final int sourceLength) {
     481         return _anonymousClasses == AnonymousClasses.ON
     482                 || (_anonymousClasses == AnonymousClasses.AUTO && sourceLength <= _anonymous_classes_threshold);
     483     }
     484
     485 }