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 26package jdk.nashorn.internal.tools.nasgen; 27 28import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 29import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; 30import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 31import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 32import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC; 33import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; 34import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_CREATE; 35import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_CREATE_DESC; 36import static jdk.nashorn.internal.tools.nasgen.StringConstants.ACCESSORPROPERTY_TYPE; 37import static jdk.nashorn.internal.tools.nasgen.StringConstants.ARRAYLIST_INIT_DESC; 38import static jdk.nashorn.internal.tools.nasgen.StringConstants.ARRAYLIST_TYPE; 39import static jdk.nashorn.internal.tools.nasgen.StringConstants.CLINIT; 40import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_EMPTY_LIST; 41import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTIONS_TYPE; 42import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_ADD; 43import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_ADD_DESC; 44import static jdk.nashorn.internal.tools.nasgen.StringConstants.COLLECTION_TYPE; 45import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC; 46import static jdk.nashorn.internal.tools.nasgen.StringConstants.GETTER_PREFIX; 47import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME; 48import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME_DESC; 49import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT; 50import static jdk.nashorn.internal.tools.nasgen.StringConstants.LIST_DESC; 51import static jdk.nashorn.internal.tools.nasgen.StringConstants.NATIVESYMBOL_TYPE; 52import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC; 53import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_DESC; 54import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_FIELD_NAME; 55import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP; 56import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_NEWMAP_DESC; 57import static jdk.nashorn.internal.tools.nasgen.StringConstants.PROPERTYMAP_TYPE; 58import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN; 59import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN_DESC; 60import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_CREATEBUILTIN_SPECS_DESC; 61import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY; 62import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC; 63import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETDOCUMENTATIONKEY; 64import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETDOCUMENTATIONKEY_DESC; 65import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE; 66import static jdk.nashorn.internal.tools.nasgen.StringConstants.SETTER_PREFIX; 67import static jdk.nashorn.internal.tools.nasgen.StringConstants.SYMBOL_DESC; 68import static jdk.nashorn.internal.tools.nasgen.StringConstants.SYMBOL_PREFIX; 69import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_OBJECT; 70 71import java.io.BufferedInputStream; 72import java.io.FileInputStream; 73import java.io.IOException; 74import java.util.List; 75import jdk.internal.org.objectweb.asm.ClassReader; 76import jdk.internal.org.objectweb.asm.ClassVisitor; 77import jdk.internal.org.objectweb.asm.ClassWriter; 78import jdk.internal.org.objectweb.asm.FieldVisitor; 79import jdk.internal.org.objectweb.asm.Handle; 80import jdk.internal.org.objectweb.asm.MethodVisitor; 81import jdk.internal.org.objectweb.asm.Type; 82import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind; 83 84/** 85 * Base class for class generator classes. 86 * 87 */ 88public 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} 398