ClassGenerator.java revision 2:da1e581c933b
1/* 2 * Copyright (c) 2010, 2012, 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_PRIVATE; 29import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 30import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 31import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC; 32import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKEVIRTUAL; 33import static jdk.nashorn.internal.tools.nasgen.StringConstants.CLINIT; 34import static jdk.nashorn.internal.tools.nasgen.StringConstants.DEFAULT_INIT_DESC; 35import static jdk.nashorn.internal.tools.nasgen.StringConstants.GETTER_PREFIX; 36import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME; 37import static jdk.nashorn.internal.tools.nasgen.StringConstants.GET_CLASS_NAME_DESC; 38import static jdk.nashorn.internal.tools.nasgen.StringConstants.INIT; 39import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_NEWPROPERTY; 40import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_NEWPROPERTY_DESC; 41import static jdk.nashorn.internal.tools.nasgen.StringConstants.LOOKUP_TYPE; 42import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_DESC; 43import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_FIELD_NAME; 44import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_NEWMAP; 45import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_NEWMAP_DESC; 46import static jdk.nashorn.internal.tools.nasgen.StringConstants.MAP_TYPE; 47import static jdk.nashorn.internal.tools.nasgen.StringConstants.OBJECT_DESC; 48import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION; 49import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_DESC; 50import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_MAKEFUNCTION_SPECS_DESC; 51import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTIONIMPL_TYPE; 52import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY; 53import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_SETARITY_DESC; 54import static jdk.nashorn.internal.tools.nasgen.StringConstants.SCRIPTFUNCTION_TYPE; 55import static jdk.nashorn.internal.tools.nasgen.StringConstants.SETTER_PREFIX; 56import static jdk.nashorn.internal.tools.nasgen.StringConstants.TYPE_OBJECT; 57 58import java.io.BufferedInputStream; 59import java.io.FileInputStream; 60import java.io.IOException; 61import java.util.List; 62import jdk.internal.org.objectweb.asm.ClassReader; 63import jdk.internal.org.objectweb.asm.ClassVisitor; 64import jdk.internal.org.objectweb.asm.ClassWriter; 65import jdk.internal.org.objectweb.asm.FieldVisitor; 66import jdk.internal.org.objectweb.asm.Handle; 67import jdk.internal.org.objectweb.asm.MethodVisitor; 68import jdk.internal.org.objectweb.asm.Type; 69import jdk.nashorn.internal.tools.nasgen.MemberInfo.Kind; 70 71/** 72 * Base class for class generator classes. 73 * 74 */ 75public class ClassGenerator { 76 /** ASM class writer used to output bytecode for this class */ 77 protected final ClassWriter cw; 78 79 /** 80 * Constructor 81 */ 82 protected ClassGenerator() { 83 this.cw = makeClassWriter(); 84 } 85 86 MethodGenerator makeStaticInitializer() { 87 return makeStaticInitializer(cw); 88 } 89 90 MethodGenerator makeConstructor() { 91 return makeConstructor(cw); 92 } 93 94 MethodGenerator makeMethod(final int access, final String name, final String desc) { 95 return makeMethod(cw, access, name, desc); 96 } 97 98 void addMapField() { 99 addMapField(cw); 100 } 101 102 void addField(final String name, final String desc) { 103 addField(cw, name, desc); 104 } 105 106 void addFunctionField(final String name) { 107 addFunctionField(cw, name); 108 } 109 110 void addGetter(final String owner, final MemberInfo memInfo) { 111 addGetter(cw, owner, memInfo); 112 } 113 114 void addSetter(final String owner, final MemberInfo memInfo) { 115 addSetter(cw, owner, memInfo); 116 } 117 118 void emitGetClassName(final String name) { 119 final MethodGenerator mi = makeMethod(ACC_PUBLIC, GET_CLASS_NAME, GET_CLASS_NAME_DESC); 120 mi.loadLiteral(name); 121 mi.returnValue(); 122 mi.computeMaxs(); 123 mi.visitEnd(); 124 } 125 126 static ClassWriter makeClassWriter() { 127 return new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { 128 @Override 129 protected String getCommonSuperClass(final String type1, final String type2) { 130 try { 131 return super.getCommonSuperClass(type1, type2); 132 } catch (final RuntimeException | LinkageError e) { 133 return StringConstants.OBJECT_TYPE; 134 } 135 } 136 }; 137 } 138 139 static MethodGenerator makeStaticInitializer(final ClassVisitor cv) { 140 return makeStaticInitializer(cv, CLINIT); 141 } 142 143 static MethodGenerator makeStaticInitializer(final ClassVisitor cv, final String name) { 144 final int access = ACC_PUBLIC | ACC_STATIC; 145 final String desc = DEFAULT_INIT_DESC; 146 final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null); 147 return new MethodGenerator(mv, access, name, desc); 148 } 149 150 static MethodGenerator makeConstructor(final ClassVisitor cv) { 151 final int access = ACC_PUBLIC; 152 final String name = INIT; 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 makeMethod(final ClassVisitor cv, final int access, final String name, final String desc) { 159 final MethodVisitor mv = cv.visitMethod(access, name, desc, null, null); 160 return new MethodGenerator(mv, access, name, desc); 161 } 162 163 static void emitStaticInitPrefix(final MethodGenerator mi, final String className) { 164 mi.visitCode(); 165 mi.pushNull(); 166 mi.putStatic(className, MAP_FIELD_NAME, MAP_DESC); 167 mi.loadClass(className); 168 mi.invokeStatic(MAP_TYPE, MAP_NEWMAP, MAP_NEWMAP_DESC); 169 mi.storeLocal(0); 170 } 171 172 static void emitStaticInitSuffix(final MethodGenerator mi, final String className) { 173 mi.loadLocal(0); 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, 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 mi.loadLocal(0); 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 mi.storeLocal(0); 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 mi.loadLocal(0); 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 mi.storeLocal(0); 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