< prev index next >

src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/ClassEmitter.java

Print this page
rev 1309 : 8085885: address Javadoc warnings in Nashorn source code


  83  * track of the current code generator and what it is doing.
  84  * <p>
  85  * There is, however, nothing stopping you from using this in a
  86  * completely self contained environment, for example in ObjectGenerator
  87  * where there are no visitors or external hooks.
  88  * <p>
  89  * MethodEmitter makes it simple to generate code for methods without
  90  * having to do arduous type checking. It maintains a type stack
  91  * and will pick the appropriate operation for all operations sent to it
  92  * We also allow chained called to a MethodEmitter for brevity, e.g.
  93  * it is legal to write _new(className).dup() or
  94  * load(slot).load(slot2).xor().store(slot3);
  95  * <p>
  96  * If running with assertions enabled, any type conflict, such as different
  97  * bytecode stack sizes or operating on the wrong type will be detected
  98  * and an error thrown.
  99  * <p>
 100  * There is also a very nice debug interface that can emit formatted
 101  * bytecodes that have been written. This is enabled by setting the
 102  * environment "nashorn.codegen.debug" to true, or --log=codegen:{@literal <level>}
 103  * <p>
 104  *
 105  * @see Compiler
 106  */
 107 public class ClassEmitter {
 108     /** Default flags for class generation - public class */
 109     private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
 110 
 111     /** Sanity check flag - have we started on a class? */
 112     private boolean classStarted;
 113 
 114     /** Sanity check flag - have we ended this emission? */
 115     private boolean classEnded;
 116 
 117     /**
 118      * Sanity checks - which methods have we currently
 119      * started for generation in this class?
 120      */
 121     private final HashSet<MethodEmitter> methodsStarted;
 122 
 123     /** The ASM classwriter that we use for all bytecode operations */


 127     protected final Context context;
 128 
 129     /** Compile unit class name. */
 130     private String unitClassName;
 131 
 132     /** Set of constants access methods required. */
 133     private Set<Class<?>> constantMethodNeeded;
 134 
 135     private int methodCount;
 136 
 137     private int initCount;
 138 
 139     private int clinitCount;
 140 
 141     private int fieldCount;
 142 
 143     private final Set<String> methodNames;
 144 
 145     /**
 146      * Constructor - only used internally in this class as it breaks
 147      * abstraction towards ASM or other code generator below
 148      *
 149      * @param env script environment
 150      * @param cw  ASM classwriter
 151      */
 152     private ClassEmitter(final Context context, final ClassWriter cw) {
 153         this.context        = context;
 154         this.cw             = cw;
 155         this.methodsStarted = new HashSet<>();
 156         this.methodNames    = new HashSet<>();
 157     }
 158 
 159     /**
 160      * Return the method names encountered

 161      * @return method names
 162      */
 163     public Set<String> getMethodNames() {
 164         return Collections.unmodifiableSet(methodNames);
 165     }
 166 
 167     /**
 168      * Constructor
 169      *
 170      * @param env             script environment
 171      * @param className       name of class to weave
 172      * @param superClassName  super class name for class
 173      * @param interfaceNames  names of interfaces implemented by this class, or null if none

 174      */
 175     ClassEmitter(final Context context, final String className, final String superClassName, final String... interfaceNames) {
 176         this(context, new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS));
 177         cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, superClassName, interfaceNames);
 178     }
 179 
 180     /**
 181      * Constructor from the compiler
 182      *
 183      * @param env           Script environment
 184      * @param sourceName    Source name
 185      * @param unitClassName Compile unit class name.
 186      * @param strictMode    Should we generate this method in strict mode
 187      */
 188     ClassEmitter(final Context context, final String sourceName, final String unitClassName, final boolean strictMode) {
 189         this(context,
 190              new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
 191                 private static final String OBJECT_CLASS  = "java/lang/Object";
 192 
 193                 @Override
 194                 protected String getCommonSuperClass(final String type1, final String type2) {
 195                     try {
 196                         return super.getCommonSuperClass(type1, type2);
 197                     } catch (final RuntimeException e) {
 198                         if (isScriptObject(Compiler.SCRIPTS_PACKAGE, type1) && isScriptObject(Compiler.SCRIPTS_PACKAGE, type2)) {
 199                             return className(ScriptObject.class);
 200                         }
 201                         return OBJECT_CLASS;
 202                     }
 203                 }
 204             });
 205 
 206         this.unitClassName        = unitClassName;
 207         this.constantMethodNeeded = new HashSet<>();
 208 
 209         cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, unitClassName, null, pathName(jdk.nashorn.internal.scripts.JS.class.getName()), null);
 210         cw.visitSource(sourceName, null);
 211 
 212         defineCommonStatics(strictMode);
 213     }
 214 
 215     Context getContext() {
 216         return context;
 217     }
 218 
 219     /**
 220      * Returns the name of the compile unit class name.
 221      * @return the name of the compile unit class name.
 222      */
 223     String getUnitClassName() {
 224         return unitClassName;
 225     }
 226 
 227     /**
 228      * Get the method count, including init and clinit methods

 229      * @return method count
 230      */
 231     public int getMethodCount() {
 232         return methodCount;
 233     }
 234 
 235     /**
 236      * Get the clinit count

 237      * @return clinit count
 238      */
 239     public int getClinitCount() {
 240         return clinitCount;
 241     }
 242 
 243     /**
 244      * Get the init count

 245      * @return init count
 246      */
 247     public int getInitCount() {
 248         return initCount;
 249     }
 250 
 251     /**
 252      * Get the field count

 253      * @return field count
 254      */
 255     public int getFieldCount() {
 256         return fieldCount;
 257     }
 258 
 259     /**
 260      * Convert a binary name to a package/class name.
 261      *
 262      * @param name Binary name.

 263      * @return Package/class name.
 264      */
 265     private static String pathName(final String name) {
 266         return name.replace('.', '/');
 267     }
 268 
 269     /**
 270      * Define the static fields common in all scripts.

 271      * @param strictMode Should we generate this method in strict mode
 272      */
 273     private void defineCommonStatics(final boolean strictMode) {
 274         // source - used to store the source data (text) for this script.  Shared across
 275         // compile units.  Set externally by the compiler.
 276         field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class);
 277 
 278         // constants - used to the constants array for this script.  Shared across
 279         // compile units.  Set externally by the compiler.
 280         field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class);
 281 
 282         // strictMode - was this script compiled in strict mode.  Set externally by the compiler.
 283         field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode);
 284     }
 285 
 286     /**
 287      * Define static utilities common needed in scripts.  These are per compile unit
 288      * and therefore have to be defined here and not in code gen.
 289      */
 290     private void defineCommonUtilities() {
 291         assert unitClassName != null;
 292 
 293         if (constantMethodNeeded.contains(String.class)) {
 294             // $getString - get the ith entry from the constants table and cast to String.
 295             final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.symbolName(), String.class, int.class);
 296             getStringMethod.begin();
 297             getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
 298                         .load(Type.INT, 0)
 299                         .arrayload()
 300                         .checkcast(String.class)
 301                         ._return();
 302             getStringMethod.end();
 303         }
 304 
 305         if (constantMethodNeeded.contains(PropertyMap.class)) {
 306             // $getMap - get the ith entry from the constants table and cast to PropertyMap.
 307             final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.symbolName(), PropertyMap.class, int.class);
 308             getMapMethod.begin();


 316             // $setMap - overwrite an existing map.
 317             final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.symbolName(), void.class, int.class, PropertyMap.class);
 318             setMapMethod.begin();
 319             setMapMethod.loadConstants()
 320                         .load(Type.INT, 0)
 321                         .load(Type.OBJECT, 1)
 322                         .arraystore();
 323             setMapMethod.returnVoid();
 324             setMapMethod.end();
 325         }
 326 
 327         // $getXXXX$array - get the ith entry from the constants table and cast to XXXX[].
 328         for (final Class<?> clazz : constantMethodNeeded) {
 329             if (clazz.isArray()) {
 330                 defineGetArrayMethod(clazz);
 331             }
 332         }
 333     }
 334 
 335     /**
 336      * Constructs a primitive specific method for getting the ith entry from the constants table as an array.


 337      * @param clazz Array class.
 338      */
 339     private void defineGetArrayMethod(final Class<?> clazz) {
 340         assert unitClassName != null;
 341 
 342         final String        methodName     = getArrayMethodName(clazz);
 343         final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, clazz, int.class);
 344 
 345         getArrayMethod.begin();
 346         getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
 347                       .load(Type.INT, 0)
 348                       .arrayload()
 349                       .checkcast(clazz)
 350                       .invoke(virtualCallNoLookup(clazz, "clone", Object.class))
 351                       .checkcast(clazz)
 352                       ._return();
 353         getArrayMethod.end();
 354     }
 355 
 356 
 357     /**
 358      * Generate the name of a get array from constant pool method.

 359      * @param clazz Name of array class.

 360      * @return Method name.
 361      */
 362     static String getArrayMethodName(final Class<?> clazz) {
 363         assert clazz.isArray();
 364         return GET_ARRAY_PREFIX.symbolName() + clazz.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
 365     }
 366 
 367     /**
 368      * Ensure a get constant method is issued for the class.

 369      * @param clazz Class of constant.
 370      */
 371     void needGetConstantMethod(final Class<?> clazz) {
 372         constantMethodNeeded.add(clazz);
 373     }
 374 
 375     /**
 376      * Inspect class name and decide whether we are generating a ScriptObject class
 377      *
 378      * @param scriptPrefix the script class prefix for the current script
 379      * @param type         the type to check
 380      *
 381      * @return true if type is ScriptObject
 382      */
 383     private static boolean isScriptObject(final String scriptPrefix, final String type) {
 384         if (type.startsWith(scriptPrefix)) {
 385             return true;
 386         } else if (type.equals(CompilerConstants.className(ScriptObject.class))) {
 387             return true;
 388         } else if (type.startsWith(Compiler.OBJECTS_PACKAGE)) {
 389             return true;
 390         }
 391 
 392         return false;
 393     }
 394 
 395     /**
 396      * Call at beginning of class emission
 397      */
 398     public void begin() {
 399         classStarted = true;
 400     }
 401 
 402     /**
 403      * Call at end of class emission
 404      */
 405     public void end() {
 406         assert classStarted : "class not started for " + unitClassName;
 407 
 408         if (unitClassName != null) {
 409             final MethodEmitter initMethod = init(EnumSet.of(Flag.PRIVATE));
 410             initMethod.begin();
 411             initMethod.load(Type.OBJECT, 0);
 412             initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class);
 413             initMethod.returnVoid();
 414             initMethod.end();
 415 
 416             defineCommonUtilities();
 417         }
 418 
 419         cw.visitEnd();
 420         classStarted = false;
 421         classEnded   = true;
 422         assert methodsStarted.isEmpty() : "methodsStarted not empty " + methodsStarted;
 423     }
 424 
 425     /**
 426      * Disassemble an array of byte code.

 427      * @param bytecode  byte array representing bytecode

 428      * @return disassembly as human readable string
 429      */
 430     static String disassemble(final byte[] bytecode) {
 431         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
 432         try (final PrintWriter pw = new PrintWriter(baos)) {
 433             final NashornClassReader cr = new NashornClassReader(bytecode);
 434             final Context ctx = AccessController.doPrivileged(new PrivilegedAction<Context>() {
 435                 @Override
 436                 public Context run() {
 437                     return Context.getContext();
 438                 }
 439             });
 440             final TraceClassVisitor tcv = new TraceClassVisitor(null, new NashornTextifier(ctx.getEnv(), cr), pw);
 441             cr.accept(tcv, 0);
 442         }
 443 
 444         final String str = new String(baos.toByteArray());
 445         return str;
 446     }
 447 
 448     /**
 449      * Call back from MethodEmitter for method start
 450      *
 451      * @see MethodEmitter
 452      *
 453      * @param method method emitter.
 454      */
 455     void beginMethod(final MethodEmitter method) {
 456         assert !methodsStarted.contains(method);
 457         methodsStarted.add(method);
 458     }
 459 
 460     /**
 461      * Call back from MethodEmitter for method end
 462      *
 463      * @see MethodEmitter
 464      *
 465      * @param method
 466      */
 467     void endMethod(final MethodEmitter method) {
 468         assert methodsStarted.contains(method);
 469         methodsStarted.remove(method);
 470     }
 471 
 472     /**
 473      * Add a new method to the class - defaults to public method
 474      *
 475      * @param methodName name of method
 476      * @param rtype      return type of the method
 477      * @param ptypes     parameter types the method
 478      *
 479      * @return method emitter to use for weaving this method
 480      */
 481     MethodEmitter method(final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
 482         return method(DEFAULT_METHOD_FLAGS, methodName, rtype, ptypes); //TODO why public default ?
 483     }
 484 
 485     /**
 486      * Add a new method to the class - defaults to public method
 487      *
 488      * @param methodFlags access flags for the method
 489      * @param methodName  name of method
 490      * @param rtype       return type of the method
 491      * @param ptypes      parameter types the method
 492      *
 493      * @return method emitter to use for weaving this method
 494      */
 495     MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
 496         methodCount++;
 497         methodNames.add(methodName);
 498         return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes));
 499     }
 500 
 501     /**
 502      * Add a new method to the class - defaults to public method
 503      *
 504      * @param methodName name of method
 505      * @param descriptor descriptor of method
 506      *
 507      * @return method emitter to use for weaving this method
 508      */
 509     MethodEmitter method(final String methodName, final String descriptor) {
 510         return method(DEFAULT_METHOD_FLAGS, methodName, descriptor);
 511     }
 512 
 513     /**
 514      * Add a new method to the class - defaults to public method
 515      *
 516      * @param methodFlags access flags for the method
 517      * @param methodName  name of method
 518      * @param descriptor  descriptor of method
 519      *
 520      * @return method emitter to use for weaving this method
 521      */
 522     MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final String descriptor) {
 523         methodCount++;
 524         methodNames.add(methodName);
 525         return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null));
 526     }
 527 
 528     /**
 529      * Add a new method to the class, representing a function node
 530      *
 531      * @param functionNode the function node to generate a method for

 532      * @return method emitter to use for weaving this method
 533      */
 534     MethodEmitter method(final FunctionNode functionNode) {
 535         methodCount++;
 536         methodNames.add(functionNode.getName());
 537         final FunctionSignature signature = new FunctionSignature(functionNode);
 538         final MethodVisitor mv = cw.visitMethod(
 539             ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
 540             functionNode.getName(),
 541             signature.toString(),
 542             null,
 543             null);
 544 
 545         return new MethodEmitter(this, mv, functionNode);
 546     }
 547 
 548     /**
 549      * Add a new method to the class, representing a rest-of version of the function node

 550      *
 551      * @param functionNode the function node to generate a method for

 552      * @return method emitter to use for weaving this method
 553      */
 554     MethodEmitter restOfMethod(final FunctionNode functionNode) {
 555         methodCount++;
 556         methodNames.add(functionNode.getName());
 557         final MethodVisitor mv = cw.visitMethod(
 558             ACC_PUBLIC | ACC_STATIC,
 559             functionNode.getName(),
 560             Type.getMethodDescriptor(functionNode.getReturnType().getTypeClass(), RewriteException.class),
 561             null,
 562             null);
 563 
 564         return new MethodEmitter(this, mv, functionNode);
 565     }
 566 
 567 
 568     /**
 569      * Start generating the <clinit> method in the class
 570      *
 571      * @return method emitter to use for weaving <clinit>
 572      */
 573     MethodEmitter clinit() {
 574         clinitCount++;
 575         return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class);
 576     }
 577 
 578     /**
 579      * Start generating an <init>()V method in the class
 580      *
 581      * @return method emitter to use for weaving <init>()V
 582      */
 583     MethodEmitter init() {
 584         initCount++;
 585         return method(INIT.symbolName(), void.class);
 586     }
 587 
 588     /**
 589      * Start generating an <init>()V method in the class
 590      *
 591      * @param ptypes parameter types for constructor
 592      * @return method emitter to use for weaving <init>()V
 593      */
 594     MethodEmitter init(final Class<?>... ptypes) {
 595         initCount++;
 596         return method(INIT.symbolName(), void.class, ptypes);
 597     }
 598 
 599     /**
 600      * Start generating an <init>(...)V method in the class
 601      *
 602      * @param flags  access flags for the constructor
 603      * @param ptypes parameter types for the constructor
 604      *
 605      * @return method emitter to use for weaving <init>(...)V
 606      */
 607     MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
 608         initCount++;
 609         return method(flags, INIT.symbolName(), void.class, ptypes);
 610     }
 611 
 612     /**
 613      * Add a field to the class, initialized to a value
 614      *
 615      * @param fieldFlags flags, e.g. should it be static or public etc
 616      * @param fieldName  name of field
 617      * @param fieldType  the type of the field
 618      * @param value      the value
 619      *
 620      * @see ClassEmitter.Flag
 621      */
 622     final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType, final Object value) {
 623         fieldCount++;
 624         cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd();
 625     }
 626 
 627     /**
 628      * Add a field to the class
 629      *
 630      * @param fieldFlags access flags for the field
 631      * @param fieldName  name of field
 632      * @param fieldType  type of the field
 633      *
 634      * @see ClassEmitter.Flag
 635      */
 636     final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType) {
 637         field(fieldFlags, fieldName, fieldType, null);
 638     }
 639 
 640     /**
 641      * Add a field to the class - defaults to public
 642      *
 643      * @param fieldName  name of field
 644      * @param fieldType  type of field
 645      */
 646     final void field(final String fieldName, final Class<?> fieldType) {
 647         field(EnumSet.of(Flag.PUBLIC), fieldName, fieldType, null);
 648     }
 649 
 650     /**
 651      * Return a bytecode array from this ClassEmitter. The ClassEmitter must
 652      * have been ended (having its end function called) for this to work.
 653      *
 654      * @return byte code array for generated class, null if class generation hasn't been ended with {@link ClassEmitter#end()}

 655      */
 656     byte[] toByteArray() {
 657         assert classEnded;
 658         if (!classEnded) {
 659             return null;
 660         }
 661 
 662         return cw.toByteArray();
 663     }
 664 
 665     /**
 666      * Abstraction for flags used in class emission
 667      *
 668      * We provide abstraction separating these from the underlying bytecode
 669      * emitter.
 670      *
 671      * Flags are provided for method handles, protection levels, static/virtual
 672      * fields/methods.
 673      */
 674     static enum Flag {
 675         /** method handle with static access */
 676         HANDLE_STATIC(H_INVOKESTATIC),
 677         /** method handle with new invoke special access */
 678         HANDLE_NEWSPECIAL(H_NEWINVOKESPECIAL),
 679         /** method handle with invoke special access */
 680         HANDLE_SPECIAL(H_INVOKESPECIAL),
 681         /** method handle with invoke virtual access */
 682         HANDLE_VIRTUAL(H_INVOKEVIRTUAL),
 683         /** method handle with invoke interface access */
 684         HANDLE_INTERFACE(H_INVOKEINTERFACE),
 685 
 686         /** final access */
 687         FINAL(ACC_FINAL),
 688         /** static access */
 689         STATIC(ACC_STATIC),
 690         /** public access */
 691         PUBLIC(ACC_PUBLIC),
 692         /** private access */
 693         PRIVATE(ACC_PRIVATE);
 694 
 695         private int value;
 696 
 697         private Flag(final int value) {
 698             this.value = value;
 699         }
 700 
 701         /**
 702          * Get the value of this flag
 703          * @return the int value
 704          */
 705         int getValue() {
 706             return value;
 707         }
 708 
 709         /**
 710          * Return the corresponding ASM flag value for an enum set of flags
 711          *
 712          * @param flags enum set of flags
 713          * @return an integer value representing the flags intrinsic values or:ed together


 714          */
 715         static int getValue(final EnumSet<Flag> flags) {
 716             int v = 0;
 717             for (final Flag flag : flags) {
 718                 v |= flag.getValue();
 719             }
 720             return v;
 721         }
 722     }
 723 
 724     private MethodVisitor methodVisitor(final EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
 725         return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
 726     }
 727 
 728 }


  83  * track of the current code generator and what it is doing.
  84  * <p>
  85  * There is, however, nothing stopping you from using this in a
  86  * completely self contained environment, for example in ObjectGenerator
  87  * where there are no visitors or external hooks.
  88  * <p>
  89  * MethodEmitter makes it simple to generate code for methods without
  90  * having to do arduous type checking. It maintains a type stack
  91  * and will pick the appropriate operation for all operations sent to it
  92  * We also allow chained called to a MethodEmitter for brevity, e.g.
  93  * it is legal to write _new(className).dup() or
  94  * load(slot).load(slot2).xor().store(slot3);
  95  * <p>
  96  * If running with assertions enabled, any type conflict, such as different
  97  * bytecode stack sizes or operating on the wrong type will be detected
  98  * and an error thrown.
  99  * <p>
 100  * There is also a very nice debug interface that can emit formatted
 101  * bytecodes that have been written. This is enabled by setting the
 102  * environment "nashorn.codegen.debug" to true, or --log=codegen:{@literal <level>}

 103  *
 104  * @see Compiler
 105  */
 106 public class ClassEmitter {
 107     /** Default flags for class generation - public class */
 108     private static final EnumSet<Flag> DEFAULT_METHOD_FLAGS = EnumSet.of(Flag.PUBLIC);
 109 
 110     /** Sanity check flag - have we started on a class? */
 111     private boolean classStarted;
 112 
 113     /** Sanity check flag - have we ended this emission? */
 114     private boolean classEnded;
 115 
 116     /**
 117      * Sanity checks - which methods have we currently
 118      * started for generation in this class?
 119      */
 120     private final HashSet<MethodEmitter> methodsStarted;
 121 
 122     /** The ASM classwriter that we use for all bytecode operations */


 126     protected final Context context;
 127 
 128     /** Compile unit class name. */
 129     private String unitClassName;
 130 
 131     /** Set of constants access methods required. */
 132     private Set<Class<?>> constantMethodNeeded;
 133 
 134     private int methodCount;
 135 
 136     private int initCount;
 137 
 138     private int clinitCount;
 139 
 140     private int fieldCount;
 141 
 142     private final Set<String> methodNames;
 143 
 144     /**
 145      * Constructor - only used internally in this class as it breaks
 146      * abstraction towards ASM or other code generator below.
 147      *
 148      * @param env script environment
 149      * @param cw  ASM classwriter
 150      */
 151     private ClassEmitter(final Context context, final ClassWriter cw) {
 152         this.context        = context;
 153         this.cw             = cw;
 154         this.methodsStarted = new HashSet<>();
 155         this.methodNames    = new HashSet<>();
 156     }
 157 
 158     /**
 159      * Return the method names encountered.
 160      *
 161      * @return method names
 162      */
 163     public Set<String> getMethodNames() {
 164         return Collections.unmodifiableSet(methodNames);
 165     }
 166 
 167     /**
 168      * Constructor.
 169      *
 170      * @param env             script environment
 171      * @param className       name of class to weave
 172      * @param superClassName  super class name for class
 173      * @param interfaceNames  names of interfaces implemented by this class, or
 174      *        {@code null} if none
 175      */
 176     ClassEmitter(final Context context, final String className, final String superClassName, final String... interfaceNames) {
 177         this(context, new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS));
 178         cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, className, null, superClassName, interfaceNames);
 179     }
 180 
 181     /**
 182      * Constructor from the compiler.
 183      *
 184      * @param env           Script environment
 185      * @param sourceName    Source name
 186      * @param unitClassName Compile unit class name.
 187      * @param strictMode    Should we generate this method in strict mode
 188      */
 189     ClassEmitter(final Context context, final String sourceName, final String unitClassName, final boolean strictMode) {
 190         this(context,
 191              new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
 192                 private static final String OBJECT_CLASS  = "java/lang/Object";
 193 
 194                 @Override
 195                 protected String getCommonSuperClass(final String type1, final String type2) {
 196                     try {
 197                         return super.getCommonSuperClass(type1, type2);
 198                     } catch (final RuntimeException e) {
 199                         if (isScriptObject(Compiler.SCRIPTS_PACKAGE, type1) && isScriptObject(Compiler.SCRIPTS_PACKAGE, type2)) {
 200                             return className(ScriptObject.class);
 201                         }
 202                         return OBJECT_CLASS;
 203                     }
 204                 }
 205             });
 206 
 207         this.unitClassName        = unitClassName;
 208         this.constantMethodNeeded = new HashSet<>();
 209 
 210         cw.visit(V1_7, ACC_PUBLIC | ACC_SUPER, unitClassName, null, pathName(jdk.nashorn.internal.scripts.JS.class.getName()), null);
 211         cw.visitSource(sourceName, null);
 212 
 213         defineCommonStatics(strictMode);
 214     }
 215 
 216     Context getContext() {
 217         return context;
 218     }
 219 
 220     /**

 221      * @return the name of the compile unit class name.
 222      */
 223     String getUnitClassName() {
 224         return unitClassName;
 225     }
 226 
 227     /**
 228      * Get the method count, including init and clinit methods.
 229      *
 230      * @return method count
 231      */
 232     public int getMethodCount() {
 233         return methodCount;
 234     }
 235 
 236     /**
 237      * Get the clinit count.
 238      *
 239      * @return clinit count
 240      */
 241     public int getClinitCount() {
 242         return clinitCount;
 243     }
 244 
 245     /**
 246      * Get the init count.
 247      *
 248      * @return init count
 249      */
 250     public int getInitCount() {
 251         return initCount;
 252     }
 253 
 254     /**
 255      * Get the field count.
 256      *
 257      * @return field count
 258      */
 259     public int getFieldCount() {
 260         return fieldCount;
 261     }
 262 
 263     /**
 264      * Convert a binary name to a package/class name.
 265      *
 266      * @param name Binary name.
 267      *
 268      * @return Package/class name.
 269      */
 270     private static String pathName(final String name) {
 271         return name.replace('.', '/');
 272     }
 273 
 274     /**
 275      * Define the static fields common in all scripts.
 276      *
 277      * @param strictMode Should we generate this method in strict mode
 278      */
 279     private void defineCommonStatics(final boolean strictMode) {
 280         // source - used to store the source data (text) for this script.  Shared across
 281         // compile units.  Set externally by the compiler.
 282         field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), SOURCE.symbolName(), Source.class);
 283 
 284         // constants - used to the constants array for this script.  Shared across
 285         // compile units.  Set externally by the compiler.
 286         field(EnumSet.of(Flag.PRIVATE, Flag.STATIC), CONSTANTS.symbolName(), Object[].class);
 287 
 288         // strictMode - was this script compiled in strict mode.  Set externally by the compiler.
 289         field(EnumSet.of(Flag.PUBLIC, Flag.STATIC, Flag.FINAL), STRICT_MODE.symbolName(), boolean.class, strictMode);
 290     }
 291 
 292     /**
 293      * Define static utilities common needed in scripts. These are per compile
 294      * unit and therefore have to be defined here and not in code gen.
 295      */
 296     private void defineCommonUtilities() {
 297         assert unitClassName != null;
 298 
 299         if (constantMethodNeeded.contains(String.class)) {
 300             // $getString - get the ith entry from the constants table and cast to String.
 301             final MethodEmitter getStringMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), GET_STRING.symbolName(), String.class, int.class);
 302             getStringMethod.begin();
 303             getStringMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
 304                         .load(Type.INT, 0)
 305                         .arrayload()
 306                         .checkcast(String.class)
 307                         ._return();
 308             getStringMethod.end();
 309         }
 310 
 311         if (constantMethodNeeded.contains(PropertyMap.class)) {
 312             // $getMap - get the ith entry from the constants table and cast to PropertyMap.
 313             final MethodEmitter getMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), GET_MAP.symbolName(), PropertyMap.class, int.class);
 314             getMapMethod.begin();


 322             // $setMap - overwrite an existing map.
 323             final MethodEmitter setMapMethod = method(EnumSet.of(Flag.PUBLIC, Flag.STATIC), SET_MAP.symbolName(), void.class, int.class, PropertyMap.class);
 324             setMapMethod.begin();
 325             setMapMethod.loadConstants()
 326                         .load(Type.INT, 0)
 327                         .load(Type.OBJECT, 1)
 328                         .arraystore();
 329             setMapMethod.returnVoid();
 330             setMapMethod.end();
 331         }
 332 
 333         // $getXXXX$array - get the ith entry from the constants table and cast to XXXX[].
 334         for (final Class<?> clazz : constantMethodNeeded) {
 335             if (clazz.isArray()) {
 336                 defineGetArrayMethod(clazz);
 337             }
 338         }
 339     }
 340 
 341     /**
 342      * Constructs a primitive specific method for getting the ith entry from the
 343      * constants table as an array.
 344      *
 345      * @param clazz Array class.
 346      */
 347     private void defineGetArrayMethod(final Class<?> clazz) {
 348         assert unitClassName != null;
 349 
 350         final String        methodName     = getArrayMethodName(clazz);
 351         final MethodEmitter getArrayMethod = method(EnumSet.of(Flag.PRIVATE, Flag.STATIC), methodName, clazz, int.class);
 352 
 353         getArrayMethod.begin();
 354         getArrayMethod.getStatic(unitClassName, CONSTANTS.symbolName(), CONSTANTS.descriptor())
 355                       .load(Type.INT, 0)
 356                       .arrayload()
 357                       .checkcast(clazz)
 358                       .invoke(virtualCallNoLookup(clazz, "clone", Object.class))
 359                       .checkcast(clazz)
 360                       ._return();
 361         getArrayMethod.end();
 362     }
 363 
 364 
 365     /**
 366      * Generate the name of a get array from constant pool method.
 367      *
 368      * @param clazz Name of array class.
 369      *
 370      * @return Method name.
 371      */
 372     static String getArrayMethodName(final Class<?> clazz) {
 373         assert clazz.isArray();
 374         return GET_ARRAY_PREFIX.symbolName() + clazz.getComponentType().getSimpleName() + GET_ARRAY_SUFFIX.symbolName();
 375     }
 376 
 377     /**
 378      * Ensure a get constant method is issued for the class.
 379      *
 380      * @param clazz Class of constant.
 381      */
 382     void needGetConstantMethod(final Class<?> clazz) {
 383         constantMethodNeeded.add(clazz);
 384     }
 385 
 386     /**
 387      * Inspect class name and decide whether we are generating a ScriptObject class.
 388      *
 389      * @param scriptPrefix the script class prefix for the current script
 390      * @param type         the type to check
 391      *
 392      * @return {@code true} if type is ScriptObject
 393      */
 394     private static boolean isScriptObject(final String scriptPrefix, final String type) {
 395         if (type.startsWith(scriptPrefix)) {
 396             return true;
 397         } else if (type.equals(CompilerConstants.className(ScriptObject.class))) {
 398             return true;
 399         } else if (type.startsWith(Compiler.OBJECTS_PACKAGE)) {
 400             return true;
 401         }
 402 
 403         return false;
 404     }
 405 
 406     /**
 407      * Call at beginning of class emission.
 408      */
 409     public void begin() {
 410         classStarted = true;
 411     }
 412 
 413     /**
 414      * Call at end of class emission.
 415      */
 416     public void end() {
 417         assert classStarted : "class not started for " + unitClassName;
 418 
 419         if (unitClassName != null) {
 420             final MethodEmitter initMethod = init(EnumSet.of(Flag.PRIVATE));
 421             initMethod.begin();
 422             initMethod.load(Type.OBJECT, 0);
 423             initMethod.newInstance(jdk.nashorn.internal.scripts.JS.class);
 424             initMethod.returnVoid();
 425             initMethod.end();
 426 
 427             defineCommonUtilities();
 428         }
 429 
 430         cw.visitEnd();
 431         classStarted = false;
 432         classEnded   = true;
 433         assert methodsStarted.isEmpty() : "methodsStarted not empty " + methodsStarted;
 434     }
 435 
 436     /**
 437      * Disassemble an array of byte code.
 438      *
 439      * @param bytecode  byte array representing bytecode
 440      *
 441      * @return disassembly as human readable string
 442      */
 443     static String disassemble(final byte[] bytecode) {
 444         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
 445         try (final PrintWriter pw = new PrintWriter(baos)) {
 446             final NashornClassReader cr = new NashornClassReader(bytecode);
 447             final Context ctx = AccessController.doPrivileged(new PrivilegedAction<Context>() {
 448                 @Override
 449                 public Context run() {
 450                     return Context.getContext();
 451                 }
 452             });
 453             final TraceClassVisitor tcv = new TraceClassVisitor(null, new NashornTextifier(ctx.getEnv(), cr), pw);
 454             cr.accept(tcv, 0);
 455         }
 456 
 457         final String str = new String(baos.toByteArray());
 458         return str;
 459     }
 460 
 461     /**
 462      * Call back from MethodEmitter for method start.
 463      *
 464      * @see MethodEmitter
 465      *
 466      * @param method method emitter.
 467      */
 468     void beginMethod(final MethodEmitter method) {
 469         assert !methodsStarted.contains(method);
 470         methodsStarted.add(method);
 471     }
 472 
 473     /**
 474      * Call back from MethodEmitter for method end.
 475      *
 476      * @see MethodEmitter
 477      *
 478      * @param method
 479      */
 480     void endMethod(final MethodEmitter method) {
 481         assert methodsStarted.contains(method);
 482         methodsStarted.remove(method);
 483     }
 484 
 485     /**
 486      * Add a new method to the class - defaults to public method.
 487      *
 488      * @param methodName name of method
 489      * @param rtype      return type of the method
 490      * @param ptypes     parameter types the method
 491      *
 492      * @return method emitter to use for weaving this method
 493      */
 494     MethodEmitter method(final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
 495         return method(DEFAULT_METHOD_FLAGS, methodName, rtype, ptypes); //TODO why public default ?
 496     }
 497 
 498     /**
 499      * Add a new method to the class - defaults to public method.
 500      *
 501      * @param methodFlags access flags for the method
 502      * @param methodName  name of method
 503      * @param rtype       return type of the method
 504      * @param ptypes      parameter types the method
 505      *
 506      * @return method emitter to use for weaving this method
 507      */
 508     MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
 509         methodCount++;
 510         methodNames.add(methodName);
 511         return new MethodEmitter(this, methodVisitor(methodFlags, methodName, rtype, ptypes));
 512     }
 513 
 514     /**
 515      * Add a new method to the class - defaults to public method.
 516      *
 517      * @param methodName name of method
 518      * @param descriptor descriptor of method
 519      *
 520      * @return method emitter to use for weaving this method
 521      */
 522     MethodEmitter method(final String methodName, final String descriptor) {
 523         return method(DEFAULT_METHOD_FLAGS, methodName, descriptor);
 524     }
 525 
 526     /**
 527      * Add a new method to the class - defaults to public method.
 528      *
 529      * @param methodFlags access flags for the method
 530      * @param methodName  name of method
 531      * @param descriptor  descriptor of method
 532      *
 533      * @return method emitter to use for weaving this method
 534      */
 535     MethodEmitter method(final EnumSet<Flag> methodFlags, final String methodName, final String descriptor) {
 536         methodCount++;
 537         methodNames.add(methodName);
 538         return new MethodEmitter(this, cw.visitMethod(Flag.getValue(methodFlags), methodName, descriptor, null, null));
 539     }
 540 
 541     /**
 542      * Add a new method to the class, representing a function node.
 543      *
 544      * @param functionNode the function node to generate a method for
 545      *
 546      * @return method emitter to use for weaving this method
 547      */
 548     MethodEmitter method(final FunctionNode functionNode) {
 549         methodCount++;
 550         methodNames.add(functionNode.getName());
 551         final FunctionSignature signature = new FunctionSignature(functionNode);
 552         final MethodVisitor mv = cw.visitMethod(
 553             ACC_PUBLIC | ACC_STATIC | (functionNode.isVarArg() ? ACC_VARARGS : 0),
 554             functionNode.getName(),
 555             signature.toString(),
 556             null,
 557             null);
 558 
 559         return new MethodEmitter(this, mv, functionNode);
 560     }
 561 
 562     /**
 563      * Add a new method to the class, representing a rest-of version of the
 564      * function node.
 565      *
 566      * @param functionNode the function node to generate a method for
 567      *
 568      * @return method emitter to use for weaving this method
 569      */
 570     MethodEmitter restOfMethod(final FunctionNode functionNode) {
 571         methodCount++;
 572         methodNames.add(functionNode.getName());
 573         final MethodVisitor mv = cw.visitMethod(
 574             ACC_PUBLIC | ACC_STATIC,
 575             functionNode.getName(),
 576             Type.getMethodDescriptor(functionNode.getReturnType().getTypeClass(), RewriteException.class),
 577             null,
 578             null);
 579 
 580         return new MethodEmitter(this, mv, functionNode);
 581     }
 582 
 583 
 584     /**
 585      * Start generating the <clinit> method in the class.
 586      *
 587      * @return method emitter to use for weaving <clinit>
 588      */
 589     MethodEmitter clinit() {
 590         clinitCount++;
 591         return method(EnumSet.of(Flag.STATIC), CLINIT.symbolName(), void.class);
 592     }
 593 
 594     /**
 595      * Start generating an <init>()V method in the class.
 596      *
 597      * @return method emitter to use for weaving <init>()V
 598      */
 599     MethodEmitter init() {
 600         initCount++;
 601         return method(INIT.symbolName(), void.class);
 602     }
 603 
 604     /**
 605      * Start generating an <init>()V method in the class.
 606      *
 607      * @param ptypes parameter types for constructor
 608      * @return method emitter to use for weaving <init>()V
 609      */
 610     MethodEmitter init(final Class<?>... ptypes) {
 611         initCount++;
 612         return method(INIT.symbolName(), void.class, ptypes);
 613     }
 614 
 615     /**
 616      * Start generating an <init>(...)V method in the class.
 617      *
 618      * @param flags  access flags for the constructor
 619      * @param ptypes parameter types for the constructor
 620      *
 621      * @return method emitter to use for weaving <init>(...)V
 622      */
 623     MethodEmitter init(final EnumSet<Flag> flags, final Class<?>... ptypes) {
 624         initCount++;
 625         return method(flags, INIT.symbolName(), void.class, ptypes);
 626     }
 627 
 628     /**
 629      * Add a field to the class, initialized to a value.
 630      *
 631      * @param fieldFlags flags, e.g. should it be static or public etc
 632      * @param fieldName  name of field
 633      * @param fieldType  the type of the field
 634      * @param value      the value
 635      *
 636      * @see ClassEmitter.Flag
 637      */
 638     final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType, final Object value) {
 639         fieldCount++;
 640         cw.visitField(Flag.getValue(fieldFlags), fieldName, typeDescriptor(fieldType), null, value).visitEnd();
 641     }
 642 
 643     /**
 644      * Add a field to the class.
 645      *
 646      * @param fieldFlags access flags for the field
 647      * @param fieldName  name of field
 648      * @param fieldType  type of the field
 649      *
 650      * @see ClassEmitter.Flag
 651      */
 652     final void field(final EnumSet<Flag> fieldFlags, final String fieldName, final Class<?> fieldType) {
 653         field(fieldFlags, fieldName, fieldType, null);
 654     }
 655 
 656     /**
 657      * Add a field to the class - defaults to public.
 658      *
 659      * @param fieldName  name of field
 660      * @param fieldType  type of field
 661      */
 662     final void field(final String fieldName, final Class<?> fieldType) {
 663         field(EnumSet.of(Flag.PUBLIC), fieldName, fieldType, null);
 664     }
 665 
 666     /**
 667      * Return a bytecode array from this ClassEmitter. The ClassEmitter must
 668      * have been ended (having its end function called) for this to work.
 669      *
 670      * @return byte code array for generated class, {@code null} if class
 671      *         generation hasn't been ended with {@link ClassEmitter#end()}.
 672      */
 673     byte[] toByteArray() {
 674         assert classEnded;
 675         if (!classEnded) {
 676             return null;
 677         }
 678 
 679         return cw.toByteArray();
 680     }
 681 
 682     /**
 683      * Abstraction for flags used in class emission. We provide abstraction
 684      * separating these from the underlying bytecode emitter. Flags are provided
 685      * for method handles, protection levels, static/virtual fields/methods.




 686      */
 687     static enum Flag {
 688         /** method handle with static access */
 689         HANDLE_STATIC(H_INVOKESTATIC),
 690         /** method handle with new invoke special access */
 691         HANDLE_NEWSPECIAL(H_NEWINVOKESPECIAL),
 692         /** method handle with invoke special access */
 693         HANDLE_SPECIAL(H_INVOKESPECIAL),
 694         /** method handle with invoke virtual access */
 695         HANDLE_VIRTUAL(H_INVOKEVIRTUAL),
 696         /** method handle with invoke interface access */
 697         HANDLE_INTERFACE(H_INVOKEINTERFACE),
 698 
 699         /** final access */
 700         FINAL(ACC_FINAL),
 701         /** static access */
 702         STATIC(ACC_STATIC),
 703         /** public access */
 704         PUBLIC(ACC_PUBLIC),
 705         /** private access */
 706         PRIVATE(ACC_PRIVATE);
 707 
 708         private int value;
 709 
 710         private Flag(final int value) {
 711             this.value = value;
 712         }
 713 
 714         /**
 715          * Get the value of this flag
 716          * @return the int value
 717          */
 718         int getValue() {
 719             return value;
 720         }
 721 
 722         /**
 723          * Return the corresponding ASM flag value for an enum set of flags.
 724          *
 725          * @param flags enum set of flags
 726          *
 727          * @return an integer value representing the flags intrinsic values
 728          *         or:ed together
 729          */
 730         static int getValue(final EnumSet<Flag> flags) {
 731             int v = 0;
 732             for (final Flag flag : flags) {
 733                 v |= flag.getValue();
 734             }
 735             return v;
 736         }
 737     }
 738 
 739     private MethodVisitor methodVisitor(final EnumSet<Flag> flags, final String methodName, final Class<?> rtype, final Class<?>... ptypes) {
 740         return cw.visitMethod(Flag.getValue(flags), methodName, methodDescriptor(rtype, ptypes), null, null);
 741     }
 742 
 743 }
< prev index next >