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