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 }