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.tools.nasgen;
  27 
  28 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
  29 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
  30 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER;
  31 import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
  32 import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
  33 import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
  34 import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
  35 import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC;
  36 import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME;
  37 import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPEOBJECT_TYPE;
  38 import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROTOTYPE_SUFFIX;
  39 import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTOBJECT_INIT_DESC;
  40 
  41 import java.io.FileOutputStream;
  42 import java.io.IOException;
  43 
  44 /**
  45  * This class generates prototype class for a @ScriptClass annotated class.
  46  *
  47  */
  48 public class PrototypeGenerator extends ClassGenerator {
  49     private final ScriptClassInfo scriptClassInfo;
  50     private final String className;
  51     private final int memberCount;
  52 
  53     PrototypeGenerator(final ScriptClassInfo sci) {
  54         this.scriptClassInfo = sci;
  55         this.className = scriptClassInfo.getPrototypeClassName();
  56         this.memberCount = scriptClassInfo.getPrototypeMemberCount();
  57     }
  58 
  59     byte[] getClassBytes() {
  60         // new class extending from ScriptObject
  61         cw.visit(V1_7, ACC_FINAL | ACC_SUPER, className, null, PROTOTYPEOBJECT_TYPE, null);
  62         if (memberCount > 0) {
  63             // add fields
  64             emitFields();
  65             // add <clinit>
  66             emitStaticInitializer();
  67         }
  68 
  69         // add <init>
  70         emitConstructor();
  71 
  72         // add getClassName()
  73         emitGetClassName(scriptClassInfo.getName());
  74 
  75         cw.visitEnd();
  76         return cw.toByteArray();
  77     }
  78 
  79     // --Internals only below this point
  80     private void emitFields() {
  81         // introduce "Function" type instance fields for each
  82         // prototype @Function in script class info
  83         for (MemberInfo memInfo : scriptClassInfo.getMembers()) {
  84             if (memInfo.isPrototypeFunction()) {
  85                 addFunctionField(memInfo.getJavaName());
  86                 memInfo = (MemberInfo)memInfo.clone();
  87                 memInfo.setJavaDesc(OBJECT_DESC);
  88                 addGetter(className, memInfo);
  89                 addSetter(className, memInfo);
  90             } else if (memInfo.isPrototypeProperty()) {
  91                 if (memInfo.isStaticFinal()) {
  92                     addGetter(scriptClassInfo.getJavaName(), memInfo);
  93                 } else {
  94                     addField(memInfo.getJavaName(), memInfo.getJavaDesc());
  95                     memInfo = (MemberInfo)memInfo.clone();
  96                     memInfo.setJavaAccess(ACC_PUBLIC);
  97                     addGetter(className, memInfo);
  98                     addSetter(className, memInfo);
  99                 }
 100             }
 101         }
 102 
 103         addMapField();
 104     }
 105 
 106     private void emitStaticInitializer() {
 107         final MethodGenerator mi = makeStaticInitializer();
 108         emitStaticInitPrefix(mi, className, memberCount);
 109         for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
 110             if (memInfo.isPrototypeFunction() || memInfo.isPrototypeProperty()) {
 111                 linkerAddGetterSetter(mi, className, memInfo);
 112             } else if (memInfo.isPrototypeGetter()) {
 113                 final MemberInfo setter = scriptClassInfo.findSetter(memInfo);
 114                 linkerAddGetterSetter(mi, scriptClassInfo.getJavaName(), memInfo, setter);
 115             }
 116         }
 117         emitStaticInitSuffix(mi, className);
 118     }
 119 
 120     private void emitConstructor() {
 121         final MethodGenerator mi = makeConstructor();
 122         mi.visitCode();
 123         mi.loadThis();
 124         if (memberCount > 0) {
 125             // call "super(map$)"
 126             mi.getStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC);
 127             mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, SCRIPTOBJECT_INIT_DESC);
 128             // initialize Function type fields
 129             initFunctionFields(mi);
 130         } else {
 131             // call "super()"
 132             mi.invokeSpecial(PROTOTYPEOBJECT_TYPE, INIT, DEFAULT_INIT_DESC);
 133         }
 134         mi.returnVoid();
 135         mi.computeMaxs();
 136         mi.visitEnd();
 137     }
 138 
 139     private void initFunctionFields(final MethodGenerator mi) {
 140         for (final MemberInfo memInfo : scriptClassInfo.getMembers()) {
 141             if (! memInfo.isPrototypeFunction()) {
 142                 continue;
 143             }
 144             mi.loadThis();
 145             newFunction(mi, scriptClassInfo.getName(), scriptClassInfo.getJavaName(), memInfo, scriptClassInfo.findSpecializations(memInfo.getJavaName()));
 146             mi.putField(className, memInfo.getJavaName(), OBJECT_DESC);
 147         }
 148     }
 149 
 150     /**
 151      * External entry point for PrototypeGenerator if called from the command line
 152      *
 153      * @param args arguments, takes 1 argument which is the class to process
 154      * @throws IOException if class cannot be read
 155      */
 156     public static void main(final String[] args) throws IOException {
 157         if (args.length != 1) {
 158             System.err.println("Usage: " + PrototypeGenerator.class.getName() + " <class>");
 159             System.exit(1);
 160         }
 161 
 162         final String className = args[0].replace('.', '/');
 163         final ScriptClassInfo sci = getScriptClassInfo(className + ".class");
 164         if (sci == null) {
 165             System.err.println("No @ScriptClass in " + className);
 166             System.exit(2);
 167             throw new AssertionError(); //guard against warning that sci is null below
 168         }
 169         try {
 170             sci.verify();
 171         } catch (final Exception e) {
 172             System.err.println(e.getMessage());
 173             System.exit(3);
 174         }
 175         final PrototypeGenerator gen = new PrototypeGenerator(sci);
 176         try (FileOutputStream fos = new FileOutputStream(className + PROTOTYPE_SUFFIX + ".class")) {
 177             fos.write(gen.getClassBytes());
 178         }
 179     }
 180 }