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_PRIVATE;
  30 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
  31 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC;
  32 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC;
  33 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL;
  34 import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_CREATE;
  35 import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_CREATE_DESC;
  36 import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_TYPE;
  37 import static jdk.nashorn.internal.tools.nasgen.StringConstants.ARRAYLIST_INIT_DESC;
  38 import static jdk.nashorn.internal.tools.nasgen.StringConstants.ARRAYLIST_TYPE;
  39 import static jdk.nashorn.internal.tools.nasgen.StringConstants.CLINIT;
  40 import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_EMPTY_LIST;
  41 import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_TYPE;
  42 import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_ADD;
  43 import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_ADD_DESC;
  44 import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_TYPE;
  45 import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC;
  46 import static jdk.nashorn.internal.tools.nasgen.StringConstants.GETTER_PREFIX;
  47 import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME;
  48 import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME_DESC;
  49 import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT;
  50 import static jdk.nashorn.internal.tools.nasgen.StringConstants.LIST_DESC;
  51 import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC;
  52 import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC;
  53 import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME;
  54 import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP;
  55 import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP_DESC;
  56 import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_TYPE;
  57 import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN;
  58 import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN_DESC;
  59 import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN_SPECS_DESC;
  60 import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY;
  61 import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC;
  62 import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETDOCUMENTATION;
  63 import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETDOCUMENTATION_DESC;
  64 import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE;
  65 import static jdk.nashorn.internal.tools.nasgen.StringConstants.SETTER_PREFIX;
  66 import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_OBJECT;
  67 
  68 import java.io.BufferedInputStream;
  69 import java.io.FileInputStream;
  70 import java.io.IOException;
  71 import java.util.List;
  72 import jdk.internal.org.objectweb.asm.ClassReader;
  73 import jdk.internal.org.objectweb.asm.ClassVisitor;
  74 import jdk.internal.org.objectweb.asm.ClassWriter;
  75 import jdk.internal.org.objectweb.asm.FieldVisitor;
  76 import jdk.internal.org.objectweb.asm.Handle;
  77 import jdk.internal.org.objectweb.asm.MethodVisitor;
  78 import jdk.internal.org.objectweb.asm.Type;
  79 import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind;
  80 
  81 /**
  82  * Base class for class generator classes.
  83  *
  84  */
  85 public class ClassGenerator {
  86     /** ASM class writer used to output bytecode for this class */
  87     protected final ClassWriter cw;
  88 
  89     /**
  90      * Constructor
  91      */
  92     protected ClassGenerator() {
  93         this.cw = makeClassWriter();
  94     }
  95 
  96     MethodGenerator makeStaticInitializer() {
  97         return makeStaticInitializer(cw);
  98     }
  99 
 100     MethodGenerator makeConstructor() {
 101         return makeConstructor(cw);
 102     }
 103 
 104     MethodGenerator makeMethod(final int access, final String name, final String desc) {
 105         return makeMethod(cw, access, name, desc);
 106     }
 107 
 108     void addMapField() {
 109         addMapField(cw);
 110     }
 111 
 112     void addField(final String name, final String desc) {
 113         addField(cw, name, desc);
 114     }
 115 
 116     void addFunctionField(final String name) {
 117         addFunctionField(cw, name);
 118     }
 119 
 120     void addGetter(final String owner, final MemberInfo memInfo) {
 121         addGetter(cw, owner, memInfo);
 122     }
 123 
 124     void addSetter(final String owner, final MemberInfo memInfo) {
 125         addSetter(cw, owner, memInfo);
 126     }
 127 
 128     void emitGetClassName(final String name) {
 129         final MethodGenerator mi = makeMethod(ACC_PUBLIC, GET_CLASS_NAME, GET_CLASS_NAME_DESC);
 130         mi.loadLiteral(name);
 131         mi.returnValue();
 132         mi.computeMaxs();
 133         mi.visitEnd();
 134     }
 135 
 136     static ClassWriter makeClassWriter() {
 137         return new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
 138             @Override
 139             protected String getCommonSuperClass(final String type1, final String type2) {
 140                 try {
 141                     return super.getCommonSuperClass(type1, type2);
 142                 } catch (final RuntimeException | LinkageError e) {
 143                     return StringConstants.OBJECT_TYPE;
 144                 }
 145             }
 146         };
 147     }
 148 
 149     static MethodGenerator makeStaticInitializer(final ClassVisitor cv) {
 150         return makeStaticInitializer(cv, CLINIT);
 151     }
 152 
 153     static MethodGenerator makeStaticInitializer(final ClassVisitor cv, final String name) {
 154         final int access =  ACC_PUBLIC | ACC_STATIC;
 155         final String desc = DEFAULT_INIT_DESC;
 156         final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
 157         return new MethodGenerator(mv, access, name, desc);
 158     }
 159 
 160     static MethodGenerator makeConstructor(final ClassVisitor cv) {
 161         final int access = 0;
 162         final String name = INIT;
 163         final String desc = DEFAULT_INIT_DESC;
 164         final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
 165         return new MethodGenerator(mv, access, name, desc);
 166     }
 167 
 168     static MethodGenerator makeMethod(final ClassVisitor cv, final int access, final String name, final String desc) {
 169         final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
 170         return new MethodGenerator(mv, access, name, desc);
 171     }
 172 
 173     static void emitStaticInitPrefix(final MethodGenerator mi, final String className, final int memberCount) {
 174         mi.visitCode();
 175         if (memberCount > 0) {
 176             // new ArrayList(int)
 177             mi.newObject(ARRAYLIST_TYPE);
 178             mi.dup();
 179             mi.push(memberCount);
 180             mi.invokeSpecial(ARRAYLIST_TYPE, INIT, ARRAYLIST_INIT_DESC);
 181             // stack: ArrayList
 182         } else {
 183             // java.util.Collections.EMPTY_LIST
 184             mi.getStatic(COLLECTIONS_TYPE, COLLECTIONS_EMPTY_LIST, LIST_DESC);
 185             // stack List
 186         }
 187     }
 188 
 189     static void emitStaticInitSuffix(final MethodGenerator mi, final String className) {
 190         // stack: Collection
 191         // pmap = PropertyMap.newMap(Collection<Property>);
 192         mi.invokeStatic(PROPERTYMAP_TYPE, PROPERTYMAP_NEWMAP, PROPERTYMAP_NEWMAP_DESC);
 193         // $nasgenmap$ = pmap;
 194         mi.putStatic(className, PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC);
 195         mi.returnVoid();
 196         mi.computeMaxs();
 197         mi.visitEnd();
 198     }
 199 
 200     @SuppressWarnings("fallthrough")
 201     private static Type memInfoType(final MemberInfo memInfo) {
 202         switch (memInfo.getJavaDesc().charAt(0)) {
 203             case 'I': return Type.INT_TYPE;
 204             case 'J': return Type.LONG_TYPE;
 205             case 'D': return Type.DOUBLE_TYPE;
 206             default:  assert false : memInfo.getJavaDesc();
 207             case 'L': return TYPE_OBJECT;
 208         }
 209     }
 210 
 211     private static String getterDesc(final MemberInfo memInfo) {
 212         return Type.getMethodDescriptor(memInfoType(memInfo));
 213     }
 214 
 215     private static String setterDesc(final MemberInfo memInfo) {
 216         return Type.getMethodDescriptor(Type.VOID_TYPE, memInfoType(memInfo));
 217     }
 218 
 219     static void addGetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) {
 220         final int access = ACC_PUBLIC;
 221         final String name = GETTER_PREFIX + memInfo.getJavaName();
 222         final String desc = getterDesc(memInfo);
 223         final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
 224         final MethodGenerator mi = new MethodGenerator(mv, access, name, desc);
 225         mi.visitCode();
 226         if (memInfo.isStatic() && memInfo.getKind() == Kind.PROPERTY) {
 227             mi.getStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
 228         } else {
 229             mi.loadLocal(0);
 230             mi.getField(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
 231         }
 232         mi.returnValue();
 233         mi.computeMaxs();
 234         mi.visitEnd();
 235     }
 236 
 237     static void addSetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) {
 238         final int access = ACC_PUBLIC;
 239         final String name = SETTER_PREFIX + memInfo.getJavaName();
 240         final String desc = setterDesc(memInfo);
 241         final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null);
 242         final MethodGenerator mi = new MethodGenerator(mv, access, name, desc);
 243         mi.visitCode();
 244         if (memInfo.isStatic() && memInfo.getKind() == Kind.PROPERTY) {
 245             mi.loadLocal(1);
 246             mi.putStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
 247         } else {
 248             mi.loadLocal(0);
 249             mi.loadLocal(1);
 250             mi.putField(owner, memInfo.getJavaName(), memInfo.getJavaDesc());
 251         }
 252         mi.returnVoid();
 253         mi.computeMaxs();
 254         mi.visitEnd();
 255     }
 256 
 257     static void addMapField(final ClassVisitor cv) {
 258         // add a PropertyMap static field
 259         final FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL,
 260             PROPERTYMAP_FIELD_NAME, PROPERTYMAP_DESC, null, null);
 261         if (fv != null) {
 262             fv.visitEnd();
 263         }
 264     }
 265 
 266     static void addField(final ClassVisitor cv, final String name, final String desc) {
 267         final FieldVisitor fv = cv.visitField(ACC_PRIVATE, name, desc, null, null);
 268         if (fv != null) {
 269             fv.visitEnd();
 270         }
 271     }
 272 
 273     static void addFunctionField(final ClassVisitor cv, final String name) {
 274         addField(cv, name, OBJECT_DESC);
 275     }
 276 
 277     static void newFunction(final MethodGenerator mi, final String className, final MemberInfo memInfo, final List<MemberInfo> specs) {
 278         final boolean arityFound = (memInfo.getArity() != MemberInfo.DEFAULT_ARITY);
 279 
 280         mi.loadLiteral(memInfo.getName());
 281         mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className, memInfo.getJavaName(), memInfo.getJavaDesc()));
 282 
 283         assert specs != null;
 284         if (!specs.isEmpty()) {
 285             mi.memberInfoArray(className, specs);
 286             mi.invokeStatic(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_CREATEBUILTIN, SCRIPTFUNCTION_CREATEBUILTIN_SPECS_DESC);
 287         } else {
 288             mi.invokeStatic(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_CREATEBUILTIN, SCRIPTFUNCTION_CREATEBUILTIN_DESC);
 289         }
 290 
 291         if (arityFound) {
 292             mi.dup();
 293             mi.push(memInfo.getArity());
 294             mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY, SCRIPTFUNCTION_SETARITY_DESC);
 295         }
 296 
 297         String doc = memInfo.getDocumentation();
 298         if (doc != null) {
 299             mi.dup();
 300             mi.loadLiteral(memInfo.getDocumentation());
 301             mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETDOCUMENTATION, SCRIPTFUNCTION_SETDOCUMENTATION_DESC);
 302         }
 303     }
 304 
 305     static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo memInfo) {
 306         final String propertyName = memInfo.getName();
 307         // stack: Collection
 308         // dup of Collection instance
 309         mi.dup();
 310 
 311         // property = AccessorProperty.create(key, flags, getter, setter);
 312         mi.loadLiteral(propertyName);
 313         // setup flags
 314         mi.push(memInfo.getAttributes());
 315         // setup getter method handle
 316         String javaName = GETTER_PREFIX + memInfo.getJavaName();
 317         mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, getterDesc(memInfo)));
 318         // setup setter method handle
 319         if (memInfo.isFinal()) {
 320             mi.pushNull();
 321         } else {
 322             javaName = SETTER_PREFIX + memInfo.getJavaName();
 323             mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, setterDesc(memInfo)));
 324         }
 325         mi.invokeStatic(ACCESSORPROPERTY_TYPE, ACCESSORPROPERTY_CREATE, ACCESSORPROPERTY_CREATE_DESC);
 326         // boolean Collection.add(property)
 327         mi.invokeInterface(COLLECTION_TYPE, COLLECTION_ADD, COLLECTION_ADD_DESC);
 328         // pop return value of Collection.add
 329         mi.pop();
 330         // stack: Collection
 331     }
 332 
 333     static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo getter, final MemberInfo setter) {
 334         final String propertyName = getter.getName();
 335         // stack: Collection
 336         // dup of Collection instance
 337         mi.dup();
 338 
 339         // property = AccessorProperty.create(key, flags, getter, setter);
 340         mi.loadLiteral(propertyName);
 341         // setup flags
 342         mi.push(getter.getAttributes());
 343         // setup getter method handle
 344         mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className,
 345                 getter.getJavaName(), getter.getJavaDesc()));
 346         // setup setter method handle
 347         if (setter == null) {
 348             mi.pushNull();
 349         } else {
 350             mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className,
 351                     setter.getJavaName(), setter.getJavaDesc()));
 352         }
 353         mi.invokeStatic(ACCESSORPROPERTY_TYPE, ACCESSORPROPERTY_CREATE, ACCESSORPROPERTY_CREATE_DESC);
 354         // boolean Collection.add(property)
 355         mi.invokeInterface(COLLECTION_TYPE, COLLECTION_ADD, COLLECTION_ADD_DESC);
 356         // pop return value of Collection.add
 357         mi.pop();
 358         // stack: Collection
 359     }
 360 
 361     static ScriptClassInfo getScriptClassInfo(final String fileName) throws IOException {
 362         try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName))) {
 363             return getScriptClassInfo(new ClassReader(bis));
 364         }
 365     }
 366 
 367     static ScriptClassInfo getScriptClassInfo(final byte[] classBuf) {
 368         return getScriptClassInfo(new ClassReader(classBuf));
 369     }
 370 
 371     private static ScriptClassInfo getScriptClassInfo(final ClassReader reader) {
 372         final ScriptClassInfoCollector scic = new ScriptClassInfoCollector();
 373         reader.accept(scic, 0);
 374         return scic.getScriptClassInfo();
 375     }
 376 }