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