src/jdk/nashorn/internal/codegen/Compiler.java

Wed, 20 Feb 2013 17:08:32 +0530

author
sundar
date
Wed, 20 Feb 2013 17:08:32 +0530
changeset 106
58eea0e8f369
parent 90
5a820fb11814
child 108
a971adb68f38
permissions
-rw-r--r--

8008207: Make constants array and source fields private
Reviewed-by: hannesw, lagergren

jlaskey@3 1 /*
jlaskey@7 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
jlaskey@3 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jlaskey@3 4 *
jlaskey@3 5 * This code is free software; you can redistribute it and/or modify it
jlaskey@3 6 * under the terms of the GNU General Public License version 2 only, as
jlaskey@3 7 * published by the Free Software Foundation. Oracle designates this
jlaskey@3 8 * particular file as subject to the "Classpath" exception as provided
jlaskey@3 9 * by Oracle in the LICENSE file that accompanied this code.
jlaskey@3 10 *
jlaskey@3 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jlaskey@3 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jlaskey@3 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jlaskey@3 14 * version 2 for more details (a copy is included in the LICENSE file that
jlaskey@3 15 * accompanied this code).
jlaskey@3 16 *
jlaskey@3 17 * You should have received a copy of the GNU General Public License version
jlaskey@3 18 * 2 along with this work; if not, write to the Free Software Foundation,
jlaskey@3 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jlaskey@3 20 *
jlaskey@3 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
jlaskey@3 22 * or visit www.oracle.com if you need additional information or have any
jlaskey@3 23 * questions.
jlaskey@3 24 */
jlaskey@3 25
jlaskey@3 26 package jdk.nashorn.internal.codegen;
jlaskey@3 27
jlaskey@3 28 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
jlaskey@3 29 import static jdk.nashorn.internal.codegen.CompilerConstants.DEFAULT_SCRIPT_NAME;
lagergren@89 30 import static jdk.nashorn.internal.codegen.CompilerConstants.LAZY;
jlaskey@3 31 import static jdk.nashorn.internal.codegen.CompilerConstants.SCOPE;
jlaskey@3 32 import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
jlaskey@3 33 import static jdk.nashorn.internal.codegen.CompilerConstants.THIS;
attila@90 34
jlaskey@3 35 import java.io.File;
sundar@106 36 import java.lang.reflect.Field;
sundar@106 37 import java.security.AccessController;
sundar@106 38 import java.security.PrivilegedActionException;
sundar@106 39 import java.security.PrivilegedExceptionAction;
lagergren@89 40 import java.util.Arrays;
jlaskey@3 41 import java.util.EnumSet;
lagergren@89 42 import java.util.HashMap;
jlaskey@3 43 import java.util.HashSet;
lagergren@89 44 import java.util.LinkedList;
jlaskey@3 45 import java.util.Map;
jlaskey@3 46 import java.util.Map.Entry;
jlaskey@3 47 import java.util.Set;
attila@90 48 import jdk.internal.dynalink.support.NameCodec;
jlaskey@3 49 import jdk.nashorn.internal.codegen.ClassEmitter.Flag;
jlaskey@3 50 import jdk.nashorn.internal.codegen.types.Type;
jlaskey@3 51 import jdk.nashorn.internal.ir.FunctionNode;
lagergren@89 52 import jdk.nashorn.internal.ir.FunctionNode.CompilationState;
jlaskey@3 53 import jdk.nashorn.internal.runtime.CodeInstaller;
jlaskey@3 54 import jdk.nashorn.internal.runtime.Context;
jlaskey@3 55 import jdk.nashorn.internal.runtime.DebugLogger;
jlaskey@3 56 import jdk.nashorn.internal.runtime.Source;
jlaskey@3 57 import jdk.nashorn.internal.runtime.options.Options;
jlaskey@3 58
jlaskey@3 59 /**
jlaskey@3 60 * Responsible for converting JavaScripts to java byte code. Main entry
lagergren@89 61 * point for code generator. The compiler may also install classes given some
lagergren@89 62 * predefined Code installation policy, given to it at construction time.
lagergren@89 63 * @see CodeInstaller
jlaskey@3 64 */
jlaskey@3 65 public final class Compiler {
jlaskey@3 66
jlaskey@3 67 /** Name of the scripts package */
jlaskey@3 68 public static final String SCRIPTS_PACKAGE = "jdk/nashorn/internal/scripts";
jlaskey@3 69
jlaskey@3 70 /** Name of the objects package */
jlaskey@3 71 public static final String OBJECTS_PACKAGE = "jdk/nashorn/internal/objects";
jlaskey@3 72
lagergren@89 73 static final boolean LAZY_JIT = Options.getBooleanProperty("nashorn.compiler.lazy");
sundar@82 74
lagergren@89 75 static final boolean TIME_COMPILATION = Options.getBooleanProperty("nashorn.compiler.time");
jlaskey@3 76
lagergren@89 77 private final Map<String, byte[]> bytecode;
jlaskey@3 78
jlaskey@3 79 private final Set<CompileUnit> compileUnits;
jlaskey@3 80
jlaskey@3 81 private final ConstantData constantData;
jlaskey@3 82
lagergren@89 83 private final FunctionNode functionNode;
lagergren@89 84
lagergren@89 85 private final CompilationSequence sequence;
lagergren@89 86
lagergren@89 87 private final Context context;
lagergren@89 88
lagergren@89 89 private final String scriptName;
lagergren@89 90
lagergren@89 91 private boolean strict;
lagergren@89 92
lagergren@89 93 private CodeInstaller<Context> installer;
lagergren@89 94
jlaskey@3 95 static final DebugLogger LOG = new DebugLogger("compiler");
jlaskey@3 96
lagergren@89 97 /**
lagergren@89 98 * This array contains names that need to be reserved at the start
lagergren@89 99 * of a compile, to avoid conflict with variable names later introduced.
lagergren@89 100 * See {@link CompilerConstants} for special names used for structures
lagergren@89 101 * during a compile.
lagergren@89 102 */
lagergren@89 103 private static String[] RESERVED_NAMES = {
lagergren@89 104 SCOPE.tag(),
lagergren@89 105 THIS.tag()
lagergren@89 106 };
jlaskey@3 107
lagergren@89 108 /**
lagergren@89 109 * This class makes it possible to do your own compilation sequence
lagergren@89 110 * from the code generation package. There are predefined compilation
lagergren@89 111 * sequences already
lagergren@89 112 */
lagergren@89 113 @SuppressWarnings("serial")
lagergren@89 114 static class CompilationSequence extends LinkedList<CompilationPhase> {
jlaskey@3 115
lagergren@89 116 CompilationSequence(final CompilationPhase... phases) {
lagergren@89 117 super(Arrays.asList(phases));
lagergren@89 118 }
jlaskey@3 119
lagergren@89 120 CompilationSequence(final CompilationSequence sequence) {
lagergren@89 121 this(sequence.toArray(new CompilationPhase[sequence.size()]));
lagergren@89 122 }
jlaskey@3 123
lagergren@89 124 CompilationSequence insertAfter(final CompilationPhase phase, final CompilationPhase newPhase) {
lagergren@89 125 final CompilationSequence newSeq = new CompilationSequence();
lagergren@89 126 for (final CompilationPhase elem : this) {
lagergren@89 127 newSeq.add(phase);
lagergren@89 128 if (elem.equals(phase)) {
lagergren@89 129 newSeq.add(newPhase);
lagergren@89 130 }
lagergren@89 131 }
lagergren@89 132 assert newSeq.contains(newPhase);
lagergren@89 133 return newSeq;
lagergren@89 134 }
jlaskey@3 135
lagergren@89 136 CompilationSequence insertBefore(final CompilationPhase phase, final CompilationPhase newPhase) {
lagergren@89 137 final CompilationSequence newSeq = new CompilationSequence();
lagergren@89 138 for (final CompilationPhase elem : this) {
lagergren@89 139 if (elem.equals(phase)) {
lagergren@89 140 newSeq.add(newPhase);
lagergren@89 141 }
lagergren@89 142 newSeq.add(phase);
lagergren@89 143 }
lagergren@89 144 assert newSeq.contains(newPhase);
lagergren@89 145 return newSeq;
lagergren@89 146 }
lagergren@89 147
lagergren@89 148 CompilationSequence insertFirst(final CompilationPhase phase) {
lagergren@89 149 final CompilationSequence newSeq = new CompilationSequence(this);
lagergren@89 150 newSeq.addFirst(phase);
lagergren@89 151 return newSeq;
lagergren@89 152 }
lagergren@89 153
lagergren@89 154 CompilationSequence insertLast(final CompilationPhase phase) {
lagergren@89 155 final CompilationSequence newSeq = new CompilationSequence(this);
lagergren@89 156 newSeq.addLast(phase);
lagergren@89 157 return newSeq;
lagergren@89 158 }
lagergren@89 159 }
lagergren@89 160
lagergren@89 161 /**
lagergren@89 162 * Standard (non-lazy) compilation, that basically will take an entire script
lagergren@89 163 * and JIT it at once. This can lead to long startup time and fewer type
lagergren@89 164 * specializations
lagergren@89 165 */
lagergren@89 166 final static CompilationSequence SEQUENCE_NORMAL = new CompilationSequence(
lagergren@89 167 CompilationPhase.CONSTANT_FOLDING_PHASE,
lagergren@89 168 CompilationPhase.LOWERING_PHASE,
lagergren@89 169 CompilationPhase.ATTRIBUTION_PHASE,
lagergren@89 170 CompilationPhase.SPLITTING_PHASE,
lagergren@89 171 CompilationPhase.TYPE_FINALIZATION_PHASE,
lagergren@89 172 CompilationPhase.BYTECODE_GENERATION_PHASE);
lagergren@89 173
lagergren@89 174 final static CompilationSequence SEQUENCE_LAZY =
lagergren@89 175 SEQUENCE_NORMAL.insertFirst(CompilationPhase.LAZY_INITIALIZATION_PHASE);
lagergren@89 176
lagergren@89 177 final static CompilationSequence SEQUENCE_DEFAULT =
lagergren@89 178 LAZY_JIT ?
lagergren@89 179 SEQUENCE_LAZY :
lagergren@89 180 SEQUENCE_NORMAL;
lagergren@89 181
lagergren@89 182 /**
lagergren@89 183 * Constructor
lagergren@89 184 *
lagergren@89 185 * @param installer code installer from
lagergren@89 186 * @param functionNode function node (in any available {@link CompilationState}) to compile
lagergren@89 187 * @param sequence {@link Compiler#CompilationSequence} of {@link CompilationPhase}s to apply as this compilation
lagergren@89 188 * @param strict should this compilation use strict mode semantics
lagergren@89 189 */
lagergren@89 190 Compiler(final Context context, final CodeInstaller<Context> installer, final FunctionNode functionNode, final CompilationSequence sequence, final boolean strict) {
lagergren@89 191 this.context = context;
lagergren@89 192 this.functionNode = functionNode;
lagergren@89 193 this.sequence = sequence;
lagergren@89 194 this.installer = installer;
lagergren@89 195 this.strict = strict || functionNode.isStrictMode();
lagergren@89 196 this.constantData = new ConstantData();
lagergren@89 197 this.compileUnits = new HashSet<>();
lagergren@89 198 this.bytecode = new HashMap<>();
lagergren@89 199
lagergren@89 200 final StringBuilder sb = new StringBuilder();
lagergren@89 201 sb.append(functionNode.uniqueName(DEFAULT_SCRIPT_NAME.tag())).
lagergren@89 202 append('$').
lagergren@89 203 append(safeSourceName(functionNode.getSource())).
lagergren@89 204 append(functionNode.isLazy() ? LAZY.tag() : "");
lagergren@89 205
lagergren@89 206 this.scriptName = sb.toString();
lagergren@89 207
lagergren@89 208 LOG.info("Initializing compiler for scriptName = " + scriptName + ", root function: '" + functionNode.getName() + "'");
lagergren@89 209 }
lagergren@89 210
lagergren@89 211 /**
lagergren@89 212 * Constructor
lagergren@89 213 *
lagergren@89 214 * @param installer code installer from context
lagergren@89 215 * @param functionNode function node (in any available {@link CompilationState}) to compile
lagergren@89 216 * @param strict should this compilation use strict mode semantics
lagergren@89 217 */
lagergren@89 218 public Compiler(final CodeInstaller<Context> installer, final FunctionNode functionNode, final boolean strict) {
lagergren@89 219 this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, strict);
lagergren@89 220 }
lagergren@89 221
lagergren@89 222 /**
lagergren@89 223 * Constructor - compilation will use the same strict semantics as context
lagergren@89 224 *
lagergren@89 225 * @param installer code installer from context
lagergren@89 226 * @param functionNode function node (in any available {@link CompilationState}) to compile
lagergren@89 227 */
lagergren@89 228 public Compiler(final CodeInstaller<Context> installer, final FunctionNode functionNode) {
lagergren@89 229 this(installer.getOwner(), installer, functionNode, SEQUENCE_DEFAULT, installer.getOwner()._strict);
lagergren@89 230 }
lagergren@89 231
lagergren@89 232 /**
lagergren@89 233 * Constructor - compilation needs no installer, but uses a context
lagergren@89 234 * Used in "compile only" scenarios
lagergren@89 235 * @param context a context
lagergren@89 236 * @param functionNode functionNode to compile
lagergren@89 237 */
lagergren@89 238 public Compiler(final Context context, final FunctionNode functionNode) {
lagergren@89 239 this(context, null, functionNode, SEQUENCE_DEFAULT, context._strict);
lagergren@89 240 }
lagergren@89 241
lagergren@89 242 /**
lagergren@89 243 * Execute the compilation this Compiler was created with
lagergren@89 244 * @return true if compilation succeeds.
lagergren@89 245 */
lagergren@89 246 public boolean compile() {
lagergren@89 247 for (final String reservedName : RESERVED_NAMES) {
lagergren@89 248 functionNode.uniqueName(reservedName);
lagergren@89 249 }
lagergren@89 250
lagergren@89 251 for (final CompilationPhase phase : sequence) {
lagergren@89 252 LOG.info("Entering compile phase " + phase + " for function '" + functionNode.getName() + "'");
lagergren@89 253 if (phase.isApplicable(functionNode)) {
lagergren@89 254 if (!phase.apply(this, functionNode)) { //TODO exceptions, error logging
lagergren@89 255 return false;
lagergren@89 256 }
lagergren@89 257 }
lagergren@89 258 }
lagergren@89 259 return true;
lagergren@89 260 }
lagergren@89 261
lagergren@89 262 /**
lagergren@89 263 * Install compiled classes into a given loader
lagergren@89 264 * @return root script class - if there are several compile units they will also be installed
lagergren@89 265 */
lagergren@89 266 public Class<?> install() {
lagergren@89 267 Class<?> rootClass = null;
lagergren@89 268
lagergren@89 269 for (final Entry<String, byte[]> entry : bytecode.entrySet()) {
lagergren@89 270 final String className = entry.getKey();
lagergren@89 271 LOG.info("Installing class " + className);
lagergren@89 272
lagergren@89 273 final byte[] code = entry.getValue();
lagergren@89 274 final Class<?> clazz = installer.install(Compiler.binaryName(className), code);
lagergren@89 275
lagergren@89 276 if (rootClass == null && firstCompileUnitName().equals(className)) {
lagergren@89 277 rootClass = clazz;
lagergren@89 278 }
lagergren@89 279
lagergren@89 280 try {
sundar@106 281 final Source source = getSource();
sundar@106 282 final Object[] constants = getConstantData().toArray();
sundar@106 283 // Need doPrivileged because these fields are private
sundar@106 284 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
sundar@106 285 @Override
sundar@106 286 public Void run() throws Exception {
sundar@106 287 //use reflection to write source and constants table to installed classes
sundar@106 288 final Field sourceField = clazz.getDeclaredField(SOURCE.tag());
sundar@106 289 final Field constantsField = clazz.getDeclaredField(CONSTANTS.tag());
sundar@106 290 sourceField.setAccessible(true);
sundar@106 291 constantsField.setAccessible(true);
sundar@106 292 sourceField.set(null, source);
sundar@106 293 constantsField.set(null, constants);
sundar@106 294 return null;
sundar@106 295 }
sundar@106 296 });
sundar@106 297 } catch (final PrivilegedActionException e) {
lagergren@89 298 throw new RuntimeException(e);
lagergren@89 299 }
lagergren@89 300 }
lagergren@89 301
lagergren@89 302 LOG.info("Root class: " + rootClass);
lagergren@89 303 return rootClass;
lagergren@89 304 }
lagergren@89 305
lagergren@89 306 Set<CompileUnit> getCompileUnits() {
lagergren@89 307 return compileUnits;
lagergren@89 308 }
lagergren@89 309
lagergren@89 310 boolean getStrictMode() {
lagergren@89 311 return strict;
lagergren@89 312 }
lagergren@89 313
lagergren@89 314 void setStrictMode(final boolean strict) {
lagergren@89 315 this.strict = strict;
lagergren@89 316 }
lagergren@89 317
lagergren@89 318 FunctionNode getFunctionNode() {
lagergren@89 319 return functionNode;
lagergren@89 320 }
lagergren@89 321
lagergren@89 322 ConstantData getConstantData() {
lagergren@89 323 return constantData;
lagergren@89 324 }
lagergren@89 325
lagergren@89 326 CodeInstaller<Context> getCodeInstaller() {
lagergren@89 327 return installer;
lagergren@89 328 }
lagergren@89 329
lagergren@89 330 Source getSource() {
lagergren@89 331 return functionNode.getSource();
lagergren@89 332 }
lagergren@89 333
lagergren@89 334 void addClass(final String name, final byte[] code) {
lagergren@89 335 bytecode.put(name, code);
lagergren@89 336 }
lagergren@89 337
lagergren@89 338 Context getContext() {
lagergren@89 339 return this.context;
lagergren@89 340 }
lagergren@89 341
lagergren@89 342 private static String safeSourceName(final Source source) {
lagergren@89 343 String baseName = new File(source.getName()).getName();
lagergren@89 344
lagergren@89 345 final int index = baseName.lastIndexOf(".js");
lagergren@89 346 if (index != -1) {
lagergren@89 347 baseName = baseName.substring(0, index);
lagergren@89 348 }
lagergren@89 349
lagergren@89 350 baseName = baseName.replace('.', '_').replace('-', '_');
lagergren@89 351 final String mangled = NameCodec.encode(baseName);
lagergren@89 352
lagergren@89 353 return mangled != null ? mangled : baseName;
lagergren@89 354 }
lagergren@89 355
lagergren@89 356 private int nextCompileUnitIndex() {
lagergren@89 357 return compileUnits.size() + 1;
lagergren@89 358 }
lagergren@89 359
lagergren@89 360 String firstCompileUnitName() {
lagergren@89 361 return SCRIPTS_PACKAGE + '/' + scriptName;
lagergren@89 362 }
lagergren@89 363
lagergren@89 364 private String nextCompileUnitName() {
lagergren@89 365 return firstCompileUnitName() + '$' + nextCompileUnitIndex();
lagergren@89 366 }
lagergren@89 367
lagergren@89 368 CompileUnit addCompileUnit(final long initialWeight) {
lagergren@89 369 return addCompileUnit(nextCompileUnitName(), initialWeight);
lagergren@89 370 }
lagergren@89 371
lagergren@89 372 CompileUnit addCompileUnit(final String unitClassName) {
lagergren@89 373 return addCompileUnit(unitClassName, 0L);
lagergren@89 374 }
lagergren@89 375
lagergren@89 376 private CompileUnit addCompileUnit(final String unitClassName, final long initialWeight) {
lagergren@89 377 final CompileUnit compileUnit = initCompileUnit(unitClassName, initialWeight);
lagergren@89 378 compileUnits.add(compileUnit);
lagergren@89 379 LOG.info("Added compile unit " + compileUnit);
lagergren@89 380 return compileUnit;
lagergren@89 381 }
lagergren@89 382
lagergren@89 383 private CompileUnit initCompileUnit(final String unitClassName, final long initialWeight) {
lagergren@89 384 final ClassEmitter classEmitter = new ClassEmitter(context, functionNode.getSource().getName(), unitClassName, strict);
lagergren@89 385 final CompileUnit compileUnit = new CompileUnit(unitClassName, classEmitter, initialWeight);
lagergren@89 386
lagergren@89 387 classEmitter.begin();
lagergren@89 388
lagergren@89 389 final MethodEmitter initMethod = classEmitter.init(EnumSet.of(Flag.PRIVATE));
lagergren@89 390 initMethod.begin();
lagergren@89 391 initMethod.load(Type.OBJECT, 0);
lagergren@89 392 initMethod.newInstance(jdk.nashorn.internal.scripts.JS$.class);
lagergren@89 393 initMethod.returnVoid();
lagergren@89 394 initMethod.end();
lagergren@89 395
lagergren@89 396 return compileUnit;
lagergren@89 397 }
lagergren@89 398
lagergren@89 399 CompileUnit findUnit(final long weight) {
lagergren@89 400 for (final CompileUnit unit : compileUnits) {
lagergren@89 401 if (unit.canHold(weight)) {
lagergren@89 402 unit.addWeight(weight);
lagergren@89 403 return unit;
lagergren@89 404 }
lagergren@89 405 }
lagergren@89 406
lagergren@89 407 return addCompileUnit(weight);
lagergren@89 408 }
lagergren@89 409
lagergren@89 410 /**
lagergren@89 411 * Convert a package/class name to a binary name.
lagergren@89 412 *
lagergren@89 413 * @param name Package/class name.
lagergren@89 414 * @return Binary name.
lagergren@89 415 */
lagergren@89 416 public static String binaryName(final String name) {
lagergren@89 417 return name.replace('/', '.');
lagergren@89 418 }
jlaskey@3 419
jlaskey@3 420 /**
jlaskey@3 421 * Should we use integers for arithmetic operations as well?
jlaskey@3 422 * TODO: We currently generate no overflow checks so this is
jlaskey@3 423 * disabled
jlaskey@3 424 *
jlaskey@3 425 * @see #shouldUseIntegers()
jlaskey@3 426 *
jlaskey@3 427 * @return true if arithmetic operations should not widen integer
jlaskey@3 428 * operands by default.
jlaskey@3 429 */
jlaskey@3 430 static boolean shouldUseIntegerArithmetic() {
lagergren@57 431 return USE_INT_ARITH;
jlaskey@3 432 }
jlaskey@3 433
jlaskey@3 434 private static final boolean USE_INT_ARITH;
jlaskey@3 435
jlaskey@3 436 static {
jlaskey@3 437 USE_INT_ARITH = Options.getBooleanProperty("nashorn.compiler.intarithmetic");
jlaskey@3 438 assert !USE_INT_ARITH : "Integer arithmetic is not enabled";
jlaskey@3 439 }
jlaskey@3 440
lagergren@89 441 static {
lagergren@89 442 if (TIME_COMPILATION) {
lagergren@89 443 Runtime.getRuntime().addShutdownHook(new Thread() {
jlaskey@3 444 @Override
lagergren@89 445 public void run() {
lagergren@89 446 for (final CompilationPhase phase : CompilationPhase.values()) {
lagergren@89 447 final StringBuilder sb = new StringBuilder();
lagergren@89 448 sb.append(phase);
lagergren@89 449 while (sb.length() < 32) {
lagergren@89 450 sb.append(' ');
lagergren@89 451 }
lagergren@89 452 sb.append(CompilationPhase.getAccumulatedTime(phase));
lagergren@89 453 sb.append(' ');
lagergren@89 454 sb.append(" ms");
lagergren@89 455 System.err.println(sb.toString()); //Context err is gone by shutdown TODO
jlaskey@3 456 }
jlaskey@3 457 }
jlaskey@3 458 });
jlaskey@3 459 }
jlaskey@3 460 }
jlaskey@3 461 }

mercurial