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 26package jdk.nashorn.internal.runtime; 27 28import java.io.PrintWriter; 29import java.util.HashMap; 30import java.util.List; 31import java.util.Locale; 32import java.util.Map; 33import java.util.StringTokenizer; 34import java.util.TimeZone; 35import java.util.logging.Level; 36import jdk.nashorn.internal.codegen.Namespace; 37import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 38import jdk.nashorn.internal.runtime.options.KeyValueOption; 39import jdk.nashorn.internal.runtime.options.LoggingOption; 40import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo; 41import jdk.nashorn.internal.runtime.options.Option; 42import 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 */ 48public 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 memory usage for IR after each phase */ 193 public final boolean _print_mem_usage; 194 195 /** Print function will no print newline characters */ 196 public final boolean _print_no_newline; 197 198 /** Print AST in more human readable form */ 199 public final boolean _print_parse; 200 201 /** Print AST in more human readable form after Lowering */ 202 public final boolean _print_lower_parse; 203 204 /** print symbols and their contents for the script */ 205 public final boolean _print_symbols; 206 207 /** is this environment in scripting mode? */ 208 public final boolean _scripting; 209 210 /** is this environment in strict mode? */ 211 public final boolean _strict; 212 213 /** print version info of Nashorn */ 214 public final boolean _version; 215 216 /** should code verification be done of generated bytecode */ 217 public final boolean _verify_code; 218 219 /** time zone for this environment */ 220 public final TimeZone _timezone; 221 222 /** Local for error messages */ 223 public final Locale _locale; 224 225 /** Logging */ 226 public final Map<String, LoggerInfo> _loggers; 227 228 /** Timing */ 229 public final Timing _timing; 230 231 /** Whether to use anonymous classes. See {@link #useAnonymousClasses(int)}. */ 232 private final AnonymousClasses _anonymousClasses; 233 private enum AnonymousClasses { 234 AUTO, 235 OFF, 236 ON 237 } 238 239 /** Size threshold up to which we use anonymous classes in {@link AnonymousClasses#AUTO} setting */ 240 private final int _anonymous_classes_threshold; 241 242 /** Default value for anonymous class threshold */ 243 private final static int DEFAULT_ANON_CLASS_THRESHOLD = 512; 244 245 /** 246 * Constructor 247 * 248 * @param options a Options object 249 * @param out output print writer 250 * @param err error print writer 251 */ 252 @SuppressWarnings("unused") 253 public ScriptEnvironment(final Options options, final PrintWriter out, final PrintWriter err) { 254 this.out = out; 255 this.err = err; 256 this.namespace = new Namespace(); 257 this.options = options; 258 259 _class_cache_size = options.getInteger("class.cache.size"); 260 _classpath = options.getString("classpath"); 261 _compile_only = options.getBoolean("compile.only"); 262 _const_as_var = options.getBoolean("const.as.var"); 263 _debug_lines = options.getBoolean("debug.lines"); 264 _debug_scopes = options.getBoolean("debug.scopes"); 265 _dest_dir = options.getString("d"); 266 _dump_on_error = options.getBoolean("doe"); 267 _early_lvalue_error = options.getBoolean("early.lvalue.error"); 268 _empty_statements = options.getBoolean("empty.statements"); 269 _fullversion = options.getBoolean("fullversion"); 270 if (options.getBoolean("function.statement.error")) { 271 _function_statement = FunctionStatementBehavior.ERROR; 272 } else if (options.getBoolean("function.statement.warning")) { 273 _function_statement = FunctionStatementBehavior.WARNING; 274 } else { 275 _function_statement = FunctionStatementBehavior.ACCEPT; 276 } 277 _fx = options.getBoolean("fx"); 278 _global_per_engine = options.getBoolean("global.per.engine"); 279 _optimistic_types = options.getBoolean("optimistic.types"); 280 final boolean lazy_compilation = options.getBoolean("lazy.compilation"); 281 if (!lazy_compilation && _optimistic_types) { 282 if (!ALLOW_EAGER_COMPILATION_SILENT_OVERRIDE) { 283 throw new IllegalStateException( 284 ECMAErrors.getMessage( 285 "config.error.eagerCompilationConflictsWithOptimisticTypes", 286 options.getOptionTemplateByKey("lazy.compilation").getName(), 287 options.getOptionTemplateByKey("optimistic.types").getName())); 288 } 289 _lazy_compilation = true; 290 } else { 291 _lazy_compilation = lazy_compilation; 292 } 293 _loader_per_compile = options.getBoolean("loader.per.compile"); 294 _module_path = options.getString("module.path"); 295 _add_modules = options.getString("add.modules"); 296 _no_java = options.getBoolean("no.java"); 297 _no_syntax_extensions = options.getBoolean("no.syntax.extensions"); 298 _no_typed_arrays = options.getBoolean("no.typed.arrays"); 299 _parse_only = options.getBoolean("parse.only"); 300 _persistent_cache = options.getBoolean("persistent.code.cache"); 301 _print_ast = options.getBoolean("print.ast"); 302 _print_lower_ast = options.getBoolean("print.lower.ast"); 303 _print_code = options.getString("print.code") != null; 304 _print_mem_usage = options.getBoolean("print.mem.usage"); 305 _print_no_newline = options.getBoolean("print.no.newline"); 306 _print_parse = options.getBoolean("print.parse"); 307 _print_lower_parse = options.getBoolean("print.lower.parse"); 308 _print_symbols = options.getBoolean("print.symbols"); 309 _scripting = options.getBoolean("scripting"); 310 _strict = options.getBoolean("strict"); 311 _version = options.getBoolean("version"); 312 _verify_code = options.getBoolean("verify.code"); 313 314 final int configuredUrt = options.getInteger("unstable.relink.threshold"); 315 // The default for this property is -1, so we can easily detect when 316 // it is not specified on command line. 317 if (configuredUrt < 0) { 318 // In this case, use a default of 8, or 16 for optimistic types. 319 // Optimistic types come with dual fields, and in order to get 320 // performance on benchmarks with a lot of object instantiation and 321 // then field reassignment, it can take slightly more relinks to 322 // become stable with type changes swapping out an entire property 323 // map and making a map guard fail. Also, honor the "nashorn.*" 324 // system property for now. It was documented in DEVELOPER_README 325 // so we should recognize it for the time being. 326 _unstable_relink_threshold = Options.getIntProperty( 327 "nashorn.unstable.relink.threshold", 328 _optimistic_types ? 16 : 8); 329 } else { 330 _unstable_relink_threshold = configuredUrt; 331 } 332 333 final String anonClasses = options.getString("anonymous.classes"); 334 if (anonClasses == null || anonClasses.equals("auto")) { 335 _anonymousClasses = AnonymousClasses.AUTO; 336 } else if (anonClasses.equals("true")) { 337 _anonymousClasses = AnonymousClasses.ON; 338 } else if (anonClasses.equals("false")) { 339 _anonymousClasses = AnonymousClasses.OFF; 340 } else { 341 throw new RuntimeException("Unsupported value for anonymous classes: " + anonClasses); 342 } 343 344 this._anonymous_classes_threshold = Options.getIntProperty( 345 "nashorn.anonymous.classes.threshold", DEFAULT_ANON_CLASS_THRESHOLD); 346 347 final String language = options.getString("language"); 348 if (language == null || language.equals("es5")) { 349 _es6 = false; 350 } else if (language.equals("es6")) { 351 _es6 = true; 352 } else { 353 throw new RuntimeException("Unsupported language: " + language); 354 } 355 356 String dir = null; 357 String func = null; 358 final String pc = options.getString("print.code"); 359 if (pc != null) { 360 final StringTokenizer st = new StringTokenizer(pc, ","); 361 while (st.hasMoreTokens()) { 362 final StringTokenizer st2 = new StringTokenizer(st.nextToken(), ":"); 363 while (st2.hasMoreTokens()) { 364 final String cmd = st2.nextToken(); 365 if ("dir".equals(cmd)) { 366 dir = st2.nextToken(); 367 } else if ("function".equals(cmd)) { 368 func = st2.nextToken(); 369 } 370 } 371 } 372 } 373 _print_code_dir = dir; 374 _print_code_func = func; 375 376 int callSiteFlags = 0; 377 if (options.getBoolean("profile.callsites")) { 378 callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_PROFILE; 379 } 380 381 if (options.get("trace.callsites") instanceof KeyValueOption) { 382 callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE; 383 final KeyValueOption kv = (KeyValueOption)options.get("trace.callsites"); 384 if (kv.hasValue("miss")) { 385 callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_MISSES; 386 } 387 if (kv.hasValue("enterexit") || (callSiteFlags & NashornCallSiteDescriptor.CALLSITE_TRACE_MISSES) == 0) { 388 callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_ENTEREXIT; 389 } 390 if (kv.hasValue("objects")) { 391 callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_VALUES; 392 } 393 } 394 this._callsite_flags = callSiteFlags; 395 396 final Option<?> timezoneOption = options.get("timezone"); 397 if (timezoneOption != null) { 398 this._timezone = (TimeZone)timezoneOption.getValue(); 399 } else { 400 this._timezone = TimeZone.getDefault(); 401 } 402 403 final Option<?> localeOption = options.get("locale"); 404 if (localeOption != null) { 405 this._locale = (Locale)localeOption.getValue(); 406 } else { 407 this._locale = Locale.getDefault(); 408 } 409 410 final LoggingOption loggingOption = (LoggingOption)options.get("log"); 411 this._loggers = loggingOption == null ? new HashMap<String, LoggerInfo>() : loggingOption.getLoggers(); 412 413 final LoggerInfo timeLoggerInfo = _loggers.get(Timing.getLoggerName()); 414 this._timing = new Timing(timeLoggerInfo != null && timeLoggerInfo.getLevel() != Level.OFF); 415 } 416 417 /** 418 * Get the output stream for this environment 419 * @return output print writer 420 */ 421 public PrintWriter getOut() { 422 return out; 423 } 424 425 /** 426 * Get the error stream for this environment 427 * @return error print writer 428 */ 429 public PrintWriter getErr() { 430 return err; 431 } 432 433 /** 434 * Get the namespace for this environment 435 * @return namespace 436 */ 437 public Namespace getNamespace() { 438 return namespace; 439 } 440 441 /** 442 * Return the JavaScript files passed to the program 443 * 444 * @return a list of files 445 */ 446 public List<String> getFiles() { 447 return options.getFiles(); 448 } 449 450 /** 451 * Return the user arguments to the program, i.e. those trailing "--" after 452 * the filename 453 * 454 * @return a list of user arguments 455 */ 456 public List<String> getArguments() { 457 return options.getArguments(); 458 } 459 460 /** 461 * Check if there is a logger registered for a particular name: typically 462 * the "name" attribute of a Loggable annotation on a class 463 * 464 * @param name logger name 465 * @return true, if a logger exists for that name, false otherwise 466 */ 467 public boolean hasLogger(final String name) { 468 return _loggers.get(name) != null; 469 } 470 471 /** 472 * Check if compilation/runtime timings are enabled 473 * @return true if enabled 474 */ 475 public boolean isTimingEnabled() { 476 return _timing != null ? _timing.isEnabled() : false; 477 } 478 479 /** 480 * Returns true if compilation should use anonymous classes. 481 * @param sourceLength length of source being compiled. 482 * @return true if anonymous classes should be used 483 */ 484 public boolean useAnonymousClasses(final int sourceLength) { 485 return _anonymousClasses == AnonymousClasses.ON 486 || (_anonymousClasses == AnonymousClasses.AUTO && sourceLength <= _anonymous_classes_threshold); 487 } 488 489} 490