ClassGenerator.java revision 414:edca88d3a03e
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.CLINIT; 35import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC; 36import static jdk.nashorn.internal.tools.nasgen.StringConstants.GETTER_PREFIX; 37import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME; 38import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME_DESC; 39import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT; 40import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_NEWPROPERTY; 41import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_NEWPROPERTY_DESC; 42import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_TYPE; 43import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DESC; 44import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME; 45import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_NEWMAP; 46import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_NEWMAP_DESC; 47import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE; 48import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC; 49import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION; 50import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC; 51import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC; 52import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE; 53import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY; 54import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC; 55import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE; 56import static jdk.nashorn.internal.tools.nasgen.StringConstants.SETTER_PREFIX; 57import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_OBJECT; 58 59import java.io.BufferedInputStream; 60import java.io.FileInputStream; 61import java.io.IOException; 62import java.util.List; 63import jdk.internal.org.objectweb.asm.ClassReader; 64import jdk.internal.org.objectweb.asm.ClassVisitor; 65import jdk.internal.org.objectweb.asm.ClassWriter; 66import jdk.internal.org.objectweb.asm.FieldVisitor; 67import jdk.internal.org.objectweb.asm.Handle; 68import jdk.internal.org.objectweb.asm.MethodVisitor; 69import jdk.internal.org.objectweb.asm.Type; 70import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind; 71 72/** 73 * Base class for class generator classes. 74 * 75 */ 76public class ClassGenerator { 77 /** ASM class writer used to output bytecode for this class */ 78 protected final ClassWriter cw; 79 80 /** 81 * Constructor 82 */ 83 protected ClassGenerator() { 84 this.cw = makeClassWriter(); 85 } 86 87 MethodGenerator makeStaticInitializer() { 88 return makeStaticInitializer(cw); 89 } 90 91 MethodGenerator makeConstructor() { 92 return makeConstructor(cw); 93 } 94 95 MethodGenerator makeMethod(final int access, final String name, final String desc) { 96 return makeMethod(cw, access, name, desc); 97 } 98 99 void addMapField() { 100 addMapField(cw); 101 } 102 103 void addField(final String name, final String desc) { 104 addField(cw, name, desc); 105 } 106 107 void addFunctionField(final String name) { 108 addFunctionField(cw, name); 109 } 110 111 void addGetter(final String owner, final MemberInfo memInfo) { 112 addGetter(cw, owner, memInfo); 113 } 114 115 void addSetter(final String owner, final MemberInfo memInfo) { 116 addSetter(cw, owner, memInfo); 117 } 118 119 void emitGetClassName(final String name) { 120 final MethodGenerator mi = makeMethod(ACC_PUBLIC, GET_CLASS_NAME, GET_CLASS_NAME_DESC); 121 mi.loadLiteral(name); 122 mi.returnValue(); 123 mi.computeMaxs(); 124 mi.visitEnd(); 125 } 126 127 static ClassWriter makeClassWriter() { 128 return new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { 129 @Override 130 protected String getCommonSuperClass(final String type1, final String type2) { 131 try { 132 return super.getCommonSuperClass(type1, type2); 133 } catch (final RuntimeException | LinkageError e) { 134 return StringConstants.OBJECT_TYPE; 135 } 136 } 137 }; 138 } 139 140 static MethodGenerator makeStaticInitializer(final ClassVisitor cv) { 141 return makeStaticInitializer(cv, CLINIT); 142 } 143 144 static MethodGenerator makeStaticInitializer(final ClassVisitor cv, final String name) { 145 final int access = ACC_PUBLIC | ACC_STATIC; 146 final String desc = DEFAULT_INIT_DESC; 147 final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null); 148 return new MethodGenerator(mv, access, name, desc); 149 } 150 151 static MethodGenerator makeConstructor(final ClassVisitor cv) { 152 final int access = ACC_PUBLIC; 153 final String name = INIT; 154 final String desc = DEFAULT_INIT_DESC; 155 final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null); 156 return new MethodGenerator(mv, access, name, desc); 157 } 158 159 static MethodGenerator makeMethod(final ClassVisitor cv, final int access, final String name, final String desc) { 160 final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null); 161 return new MethodGenerator(mv, access, name, desc); 162 } 163 164 static void emitStaticInitPrefix(final MethodGenerator mi, final String className) { 165 mi.visitCode(); 166 mi.pushNull(); 167 mi.putStatic(className, MAP_FIELD_NAME, MAP_DESC); 168 mi.invokeStatic(MAP_TYPE, MAP_NEWMAP, MAP_NEWMAP_DESC); 169 // stack: PropertyMap 170 } 171 172 static void emitStaticInitSuffix(final MethodGenerator mi, final String className) { 173 // stack: PropertyMap 174 mi.putStatic(className, MAP_FIELD_NAME, MAP_DESC); 175 mi.returnVoid(); 176 mi.computeMaxs(); 177 mi.visitEnd(); 178 } 179 180 @SuppressWarnings("fallthrough") 181 private static Type memInfoType(final MemberInfo memInfo) { 182 switch (memInfo.getJavaDesc().charAt(0)) { 183 case 'I': return Type.INT_TYPE; 184 case 'J': return Type.LONG_TYPE; 185 case 'D': return Type.DOUBLE_TYPE; 186 default: assert false : memInfo.getJavaDesc(); 187 case 'L': return TYPE_OBJECT; 188 } 189 } 190 191 private static String getterDesc(final MemberInfo memInfo) { 192 return Type.getMethodDescriptor(memInfoType(memInfo)); 193 } 194 195 private static String setterDesc(final MemberInfo memInfo) { 196 return Type.getMethodDescriptor(Type.VOID_TYPE, memInfoType(memInfo)); 197 } 198 199 static void addGetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) { 200 final int access = ACC_PUBLIC; 201 final String name = GETTER_PREFIX + memInfo.getJavaName(); 202 final String desc = getterDesc(memInfo); 203 final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null); 204 final MethodGenerator mi = new MethodGenerator(mv, access, name, desc); 205 mi.visitCode(); 206 if (memInfo.isStatic() && memInfo.getKind() == Kind.PROPERTY) { 207 mi.getStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc()); 208 } else { 209 mi.loadLocal(0); 210 mi.getField(owner, memInfo.getJavaName(), memInfo.getJavaDesc()); 211 } 212 mi.returnValue(); 213 mi.computeMaxs(); 214 mi.visitEnd(); 215 } 216 217 static void addSetter(final ClassVisitor cv, final String owner, final MemberInfo memInfo) { 218 final int access = ACC_PUBLIC; 219 final String name = SETTER_PREFIX + memInfo.getJavaName(); 220 final String desc = setterDesc(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.loadLocal(1); 226 mi.putStatic(owner, memInfo.getJavaName(), memInfo.getJavaDesc()); 227 } else { 228 mi.loadLocal(0); 229 mi.loadLocal(1); 230 mi.putField(owner, memInfo.getJavaName(), memInfo.getJavaDesc()); 231 } 232 mi.returnVoid(); 233 mi.computeMaxs(); 234 mi.visitEnd(); 235 } 236 237 static void addMapField(final ClassVisitor cv) { 238 // add a MAP static field 239 final FieldVisitor fv = cv.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL, 240 MAP_FIELD_NAME, MAP_DESC, null, null); 241 if (fv != null) { 242 fv.visitEnd(); 243 } 244 } 245 246 static void addField(final ClassVisitor cv, final String name, final String desc) { 247 final FieldVisitor fv = cv.visitField(ACC_PRIVATE, name, desc, null, null); 248 if (fv != null) { 249 fv.visitEnd(); 250 } 251 } 252 253 static void addFunctionField(final ClassVisitor cv, final String name) { 254 addField(cv, name, OBJECT_DESC); 255 } 256 257 static void newFunction(final MethodGenerator mi, final String className, final MemberInfo memInfo, final List<MemberInfo> specs) { 258 final boolean arityFound = (memInfo.getArity() != MemberInfo.DEFAULT_ARITY); 259 260 mi.loadLiteral(memInfo.getName()); 261 mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className, memInfo.getJavaName(), memInfo.getJavaDesc())); 262 263 assert specs != null; 264 if (!specs.isEmpty()) { 265 mi.memberInfoArray(className, specs); 266 mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC); 267 } else { 268 mi.invokeStatic(SCRIPTFUNCTIONIMPL_TYPE, SCRIPTFUNCTIONIMPL_MAKEFUNCTION, SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC); 269 } 270 271 if (arityFound) { 272 mi.dup(); 273 mi.push(memInfo.getArity()); 274 mi.invokeVirtual(SCRIPTFUNCTION_TYPE, SCRIPTFUNCTION_SETARITY, SCRIPTFUNCTION_SETARITY_DESC); 275 } 276 277 } 278 279 static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo memInfo) { 280 final String propertyName = memInfo.getName(); 281 // stack: PropertyMap 282 mi.loadLiteral(propertyName); 283 // setup flags 284 mi.push(memInfo.getAttributes()); 285 // setup getter method handle 286 String javaName = GETTER_PREFIX + memInfo.getJavaName(); 287 mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, getterDesc(memInfo))); 288 // setup setter method handle 289 if (memInfo.isFinal()) { 290 mi.pushNull(); 291 } else { 292 javaName = SETTER_PREFIX + memInfo.getJavaName(); 293 mi.visitLdcInsn(new Handle(H_INVOKEVIRTUAL, className, javaName, setterDesc(memInfo))); 294 } 295 mi.invokeStatic(LOOKUP_TYPE, LOOKUP_NEWPROPERTY, LOOKUP_NEWPROPERTY_DESC); 296 // stack: PropertyMap 297 } 298 299 static void linkerAddGetterSetter(final MethodGenerator mi, final String className, final MemberInfo getter, final MemberInfo setter) { 300 final String propertyName = getter.getName(); 301 // stack: PropertyMap 302 mi.loadLiteral(propertyName); 303 // setup flags 304 mi.push(getter.getAttributes()); 305 // setup getter method handle 306 mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className, 307 getter.getJavaName(), getter.getJavaDesc())); 308 // setup setter method handle 309 if (setter == null) { 310 mi.pushNull(); 311 } else { 312 mi.visitLdcInsn(new Handle(H_INVOKESTATIC, className, 313 setter.getJavaName(), setter.getJavaDesc())); 314 } 315 mi.invokeStatic(LOOKUP_TYPE, LOOKUP_NEWPROPERTY, LOOKUP_NEWPROPERTY_DESC); 316 // stack: PropertyMap 317 } 318 319 static ScriptClassInfo getScriptClassInfo(final String fileName) throws IOException { 320 try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName))) { 321 return getScriptClassInfo(new ClassReader(bis)); 322 } 323 } 324 325 static ScriptClassInfo getScriptClassInfo(final byte[] classBuf) { 326 return getScriptClassInfo(new ClassReader(classBuf)); 327 } 328 329 private static ScriptClassInfo getScriptClassInfo(final ClassReader reader) { 330 final ScriptClassInfoCollector scic = new ScriptClassInfoCollector(); 331 reader.accept(scic, 0); 332 return scic.getScriptClassInfo(); 333 } 334} 335