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