--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.services.bytecode.BCClass\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.services.bytecode;\r
+\r
+import org.apache.derby.iapi.services.compiler.ClassBuilder;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.compiler.LocalField;\r
+\r
+import org.apache.derby.iapi.services.classfile.ClassHolder;\r
+import org.apache.derby.iapi.services.classfile.ClassMember;\r
+import org.apache.derby.iapi.services.classfile.ClassFormatOutput;\r
+import org.apache.derby.iapi.services.loader.ClassFactory;\r
+\r
+import org.apache.derby.iapi.services.monitor.Monitor;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.Property;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.util.ByteArray;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+\r
+import java.lang.reflect.Modifier;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.services.classfile.VMDescriptor;\r
+\r
+import org.apache.derby.impl.services.bytecode.GClass;\r
+\r
+import java.io.IOException;\r
+\r
+/**\r
+ * ClassBuilder is used to construct a java class's byte array\r
+ * representation.\r
+ *\r
+ * Limitations:\r
+ * No checking for language use violations such as invalid modifiers\r
+ * or duplicate field names.\r
+ * All classes must have a superclass; java.lang.Object must be\r
+ * supplied if there is no superclass.\r
+ *\r
+ * <p>\r
+ * When a class is first created, it has:\r
+ * <ul>\r
+ * <li> a superclass\r
+ * <li> modifiers\r
+ * <li> a name\r
+ * <li> a package\r
+ * <li> no superinterfaces, methods, fields, or constructors\r
+ * <li> an empty static initializer\r
+ * <li> an empty initializer\r
+ * </ul>\r
+ * <p>\r
+ * MethodBuilder implementations are required to supply a way for\r
+ * Generators to give them code. Most typically, they may have\r
+ * a stream to which the Generator writes the code that is of\r
+ * the type to satisfy what the Generator is writing.\r
+ * <p>\r
+ * BCClass is a ClassBuilder implementation for generating java bytecode\r
+ * directly.\r
+ *\r
+ */\r
+class BCClass extends GClass {\r
+ \r
+ /**\r
+ * Simple text indicating any limits execeeded while generating\r
+ * the class file.\r
+ */\r
+ String limitMsg;\r
+ \r
+ //\r
+ // ClassBuilder interface\r
+ //\r
+ /**\r
+ * add a field to this class. Fields cannot\r
+ * be initialized here, they must be initialized\r
+ * in the static initializer code (static fields)\r
+ * or in the constructors.\r
+ * <p>\r
+ * static fields also added to this list,\r
+ * with the modifier set appropriately.\r
+ */\r
+ public LocalField addField(String javaType, String name, int modifiers) {\r
+\r
+ Type type = factory.type(javaType);\r
+ // put it into the class holder right away.\r
+ ClassMember field = classHold.addMember(name, type.vmName(), modifiers);\r
+ int cpi = classHold.addFieldReference(field);\r
+\r
+ return new BCLocalField(type, cpi);\r
+ }\r
+\r
+ /**\r
+ * At the time the class is completed and bytecode\r
+ * generated, if there are no constructors then\r
+ * the default no-arg constructor will be defined.\r
+ */\r
+ public ByteArray getClassBytecode() throws StandardException {\r
+\r
+ // return if already done\r
+ if (bytecode != null) return bytecode;\r
+ \r
+ try {\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (SanityManager.DEBUG_ON("ClassLineNumbers")) {\r
+\r
+ ClassFormatOutput sout = new ClassFormatOutput(2);\r
+\r
+ int cpiUTF = classHold.addUtf8("GC.java");\r
+\r
+ sout.putU2(cpiUTF);\r
+\r
+ classHold.addAttribute("SourceFile", sout);\r
+ }\r
+ }\r
+\r
+ // the class is now complete, get its bytecode.\r
+ bytecode = classHold.getFileFormat();\r
+ \r
+ } catch (IOException ioe) {\r
+ throw StandardException.newException(\r
+ SQLState.GENERATED_CLASS_LINKAGE_ERROR, ioe, getFullName());\r
+ }\r
+\r
+ // release resources, we have the code now.\r
+ // name is not released, it may still be accessed.\r
+ classHold = null;\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (SanityManager.DEBUG_ON("DumpClassFile")) {\r
+ /* Dump the file in derby.system.home */\r
+ String systemHome = System.getProperty(Property.SYSTEM_HOME_PROPERTY,".");\r
+ writeClassFile(systemHome,false,null);\r
+ }\r
+ }\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (SanityManager.DEBUG_ON("ByteCodeGenInstr")) {\r
+ SanityManager.DEBUG("ByteCodeGenInstr",\r
+ "GEN complete for class "+name);\r
+ }\r
+ }\r
+ \r
+ if (limitMsg != null)\r
+ throw StandardException.newException(\r
+ SQLState.GENERATED_CLASS_LIMIT_EXCEEDED, getFullName(), limitMsg);\r
+ return bytecode;\r
+ }\r
+\r
+\r
+ /**\r
+ * the class's unqualified name\r
+ */\r
+ public String getName() {\r
+ return name;\r
+ }\r
+ \r
+ /**\r
+ * a method. Once it is created, thrown\r
+ * exceptions, statements, and local variable declarations\r
+ * must be added to it. It is put into its defining class\r
+ * when it is created.\r
+ * <verbatim>\r
+ Java: #modifiers #returnType #methodName() {}\r
+ // modifiers is the | of the JVM constants for\r
+ // the modifiers such as static, public, etc.\r
+ </verbatim>\r
+ * <p>\r
+ * This is used to start a constructor as well; pass in\r
+ * null for the returnType when used in that manner.\r
+ *\r
+ * See java.lang.reflect.Modifiers\r
+ * @param modifiers the | of the Modifiers\r
+ * constants representing the visibility and control of this\r
+ * method.\r
+ * @param returnType the return type of the method as its\r
+ * Java language type name.\r
+ * @param methodName the name of the method.\r
+ *\r
+ * @return the method builder.\r
+ */\r
+ public MethodBuilder newMethodBuilder(int modifiers, String returnType,\r
+ String methodName) {\r
+\r
+ return newMethodBuilder(modifiers, returnType,\r
+ methodName, (String[]) null);\r
+\r
+ }\r
+\r
+\r
+ /**\r
+ * a method with parameters. Once it is created, thrown\r
+ * exceptions, statements, and local variable declarations\r
+ * must be added to it. It is put into its defining class\r
+ * when it is created.\r
+ * <verbatim>\r
+ Java: #modifiers #returnType #methodName() {}\r
+ // modifiers is the | of the JVM constants for\r
+ // the modifiers such as static, public, etc.\r
+ </verbatim>\r
+ * <p>\r
+ * This is used to start a constructor as well; pass in\r
+ * null for the returnType when used in that manner.\r
+ *\r
+ * See java.lang.reflect.Modifiers\r
+ * @param modifiers the | of the Modifiers\r
+ * constants representing the visibility and control of this\r
+ * method.\r
+ * @param returnType the return type of the method as its\r
+ * Java language type name.\r
+ * @param methodName the name of the method.\r
+ * @param parms an array of ParameterDeclarations representing the\r
+ * method's parameters\r
+ *\r
+ * @return the method builder.\r
+ */\r
+ public MethodBuilder newMethodBuilder(int modifiers, String returnType,\r
+ String methodName, String[] parms) {\r
+\r
+ if (SanityManager.DEBUG) {\r
+ SanityManager.ASSERT(returnType!=null);\r
+ }\r
+\r
+ BCMethod m = new BCMethod(this,\r
+ returnType,\r
+ methodName,\r
+ modifiers,\r
+ parms,\r
+ factory);\r
+\r
+ return m;\r
+ \r
+ }\r
+\r
+\r
+ /**\r
+ * a constructor. Once it is created, thrown\r
+ * exceptions, statements, and local variable declarations\r
+ * must be added to it. It is put into its defining class\r
+ * when it is created.\r
+ * <verbatim>\r
+ Java: #modifiers #className() {}\r
+ // modifiers is the | of the JVM constants for\r
+ // the modifiers such as static, public, etc.\r
+ // className is taken from definingClass.getName()\r
+ </verbatim>\r
+ * <p>\r
+ * This is used to start a constructor as well; pass in\r
+ * null for the returnType when used in that manner.\r
+ *\r
+ * See Modifiers\r
+ * @param modifiers the | of the Modifiers\r
+ * constants representing the visibility and control of this\r
+ * method.\r
+ *\r
+ * @return the method builder for the constructor.\r
+ */\r
+ public MethodBuilder newConstructorBuilder(int modifiers) {\r
+\r
+ BCMethod m = new BCMethod(this, "void", "<init>", \r
+ modifiers,\r
+ (String []) null,\r
+ factory);\r
+\r
+ return m;\r
+ }\r
+ //\r
+ // class interface\r
+ //\r
+\r
+ String getSuperClassName() {\r
+ return superClassName;\r
+ }\r
+\r
+ /**\r
+ * Let those that need to get to the\r
+ * classModify tool to alter the class definition.\r
+ */\r
+ ClassHolder modify() {\r
+ return classHold;\r
+ }\r
+\r
+ /*\r
+ ** Method descriptor caching\r
+ */\r
+\r
+ BCClass(ClassFactory cf, String packageName, int classModifiers,\r
+ String className, String superClassName,\r
+ BCJava factory) {\r
+\r
+ super(cf, packageName.concat(className));\r
+\r
+ if (SanityManager.DEBUG) {\r
+ if (SanityManager.DEBUG_ON("ByteCodeGenInstr")) {\r
+ SanityManager.DEBUG("ByteCodeGenInstr",\r
+ "GEN starting for class "+className);\r
+ }\r
+ }\r
+\r
+ // by the time the constructor is done, we have:\r
+ //\r
+ // package #packageName;\r
+ // #classModifiers class #className extends #superClassName\r
+ // { }\r
+ //\r
+\r
+ name = className;\r
+ if (superClassName == null)\r
+ superClassName = "java.lang.Object";\r
+ this.superClassName = superClassName;\r
+\r
+ classType = factory.type(getFullName());\r
+\r
+ classHold = new ClassHolder(qualifiedName, factory.type(superClassName).vmNameSimple, classModifiers);\r
+\r
+ this.factory = factory;\r
+ }\r
+\r
+ protected ClassHolder classHold;\r
+\r
+ protected String superClassName;\r
+ protected String name;\r
+\r
+ BCJava factory;\r
+ final Type classType;\r
+\r
+ ClassFactory getClassFactory() {\r
+ return cf;\r
+ }\r
+\r
+ public void newFieldWithAccessors(String getter, String setter,\r
+ int methodModifers,\r
+ boolean staticField, String type) {\r
+\r
+ String vmType = factory.type(type).vmName();\r
+ methodModifers |= Modifier.FINAL;\r
+\r
+\r
+ // add a field, field has same name as get method\r
+ int fieldModifiers = Modifier.PRIVATE;\r
+ if (staticField)\r
+ fieldModifiers |= Modifier.STATIC;\r
+\r
+ ClassMember field = classHold.addMember(getter, vmType, fieldModifiers);\r
+ int cpi = classHold.addFieldReference(field);\r
+\r
+ /*\r
+ ** add the get method\r
+ */\r
+\r
+ String sig = BCMethodDescriptor.get(BCMethodDescriptor.EMPTY, vmType, factory);\r
+\r
+ ClassMember method = classHold.addMember(getter, sig, methodModifers);\r
+\r
+ CodeChunk chunk = new CodeChunk(this);\r
+\r
+ // load 'this' if required\r
+ if (!staticField)\r
+ chunk.addInstr(VMOpcode.ALOAD_0); // this\r
+ \r
+ // get the field value\r
+ chunk.addInstrU2((staticField ? VMOpcode.GETSTATIC : VMOpcode.GETFIELD), cpi);\r
+\r
+ // and return it\r
+ short vmTypeId = BCJava.vmTypeId(vmType);\r
+\r
+ chunk.addInstr(CodeChunk.RETURN_OPCODE[vmTypeId]);\r
+\r
+ int typeWidth = Type.width(vmTypeId);\r
+ chunk.complete(null, classHold, method, typeWidth, 1);\r
+\r
+ /*\r
+ ** add the set method\r
+ */\r
+ String[] pda = new String[1];\r
+ pda[0] = vmType;\r
+ sig = new BCMethodDescriptor(pda, VMDescriptor.VOID, factory).toString();\r
+ method = classHold.addMember(setter, sig, methodModifers);\r
+ chunk = new CodeChunk(this);\r
+\r
+ // load 'this' if required\r
+ if (!staticField)\r
+ chunk.addInstr(VMOpcode.ALOAD_0); // this\r
+ // push the only parameter\r
+ chunk.addInstr((short) (CodeChunk.LOAD_VARIABLE_FAST[vmTypeId] + 1));\r
+ \r
+ // and set the field\r
+ chunk.addInstrU2((staticField ? VMOpcode.PUTSTATIC : VMOpcode.PUTFIELD), cpi);\r
+\r
+ chunk.addInstr(VMOpcode.RETURN);\r
+\r
+ chunk.complete(null, classHold, method, typeWidth + (staticField ? 0 : 1), 1 + typeWidth);\r
+ }\r
+ \r
+ /**\r
+ * Add the fact that some class limit was exceeded while generating\r
+ * the class. We create a set of them and report at the end, this\r
+ * allows the generated class file to still be dumped.\r
+ * @param mb\r
+ * @param limitName\r
+ * @param limit\r
+ * @param value\r
+ */\r
+ void addLimitExceeded(BCMethod mb, String limitName, int limit, int value)\r
+ {\r
+ StringBuffer sb = new StringBuffer();\r
+ if (limitMsg != null)\r
+ {\r
+ sb.append(limitMsg);\r
+ sb.append(", ");\r
+ }\r
+ \r
+ sb.append("method:");\r
+ sb.append(mb.getName());\r
+ sb.append(" ");\r
+ sb.append(limitName);\r
+ sb.append(" (");\r
+ sb.append(value);\r
+ sb.append(" > ");\r
+ sb.append(limit);\r
+ sb.append(")");\r
+ \r
+ limitMsg = sb.toString();\r
+ }\r
+ \r
+ /**\r
+ * Add the fact that some class limit was exceeded while generating\r
+ * the class. Text is the simple string passed in.\r
+ * @param rawText Text to be reported.\r
+ * \r
+ * @see BCClass#addLimitExceeded(BCMethod, String, int, int)\r
+ */\r
+ void addLimitExceeded(String rawText)\r
+ {\r
+ if (limitMsg != null)\r
+ {\r
+ limitMsg = limitMsg + ", " + rawText;\r
+ }\r
+ else\r
+ {\r
+ limitMsg = rawText;\r
+ }\r
+ }\r
+\r
+}\r