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 }