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 }