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