001: /*
002:
003: Derby - Class org.apache.derby.impl.services.bytecode.BCClass
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.services.bytecode;
023:
024: import org.apache.derby.iapi.services.compiler.ClassBuilder;
025: import org.apache.derby.iapi.services.compiler.MethodBuilder;
026: import org.apache.derby.iapi.services.compiler.LocalField;
027:
028: import org.apache.derby.iapi.services.classfile.ClassHolder;
029: import org.apache.derby.iapi.services.classfile.ClassMember;
030: import org.apache.derby.iapi.services.classfile.ClassFormatOutput;
031: import org.apache.derby.iapi.services.loader.ClassFactory;
032:
033: import org.apache.derby.iapi.services.monitor.Monitor;
034:
035: import org.apache.derby.iapi.error.StandardException;
036: import org.apache.derby.iapi.reference.Property;
037: import org.apache.derby.iapi.reference.SQLState;
038:
039: import org.apache.derby.iapi.util.ByteArray;
040: import org.apache.derby.iapi.services.classfile.VMOpcode;
041:
042: import java.lang.reflect.Modifier;
043:
044: import org.apache.derby.iapi.services.sanity.SanityManager;
045: import org.apache.derby.iapi.services.classfile.VMDescriptor;
046:
047: import org.apache.derby.impl.services.bytecode.GClass;
048:
049: import java.io.IOException;
050:
051: /**
052: * ClassBuilder is used to construct a java class's byte array
053: * representation.
054: *
055: * Limitations:
056: * No checking for language use violations such as invalid modifiers
057: * or duplicate field names.
058: * All classes must have a superclass; java.lang.Object must be
059: * supplied if there is no superclass.
060: *
061: * <p>
062: * When a class is first created, it has:
063: * <ul>
064: * <li> a superclass
065: * <li> modifiers
066: * <li> a name
067: * <li> a package
068: * <li> no superinterfaces, methods, fields, or constructors
069: * <li> an empty static initializer
070: * <li> an empty initializer
071: * </ul>
072: * <p>
073: * MethodBuilder implementations are required to supply a way for
074: * Generators to give them code. Most typically, they may have
075: * a stream to which the Generator writes the code that is of
076: * the type to satisfy what the Generator is writing.
077: * <p>
078: * BCClass is a ClassBuilder implementation for generating java bytecode
079: * directly.
080: *
081: */
082: class BCClass extends GClass {
083:
084: /**
085: * Simple text indicating any limits execeeded while generating
086: * the class file.
087: */
088: String limitMsg;
089:
090: //
091: // ClassBuilder interface
092: //
093: /**
094: * add a field to this class. Fields cannot
095: * be initialized here, they must be initialized
096: * in the static initializer code (static fields)
097: * or in the constructors.
098: * <p>
099: * static fields also added to this list,
100: * with the modifier set appropriately.
101: */
102: public LocalField addField(String javaType, String name,
103: int modifiers) {
104:
105: Type type = factory.type(javaType);
106: // put it into the class holder right away.
107: ClassMember field = classHold.addMember(name, type.vmName(),
108: modifiers);
109: int cpi = classHold.addFieldReference(field);
110:
111: return new BCLocalField(type, cpi);
112: }
113:
114: /**
115: * At the time the class is completed and bytecode
116: * generated, if there are no constructors then
117: * the default no-arg constructor will be defined.
118: */
119: public ByteArray getClassBytecode() throws StandardException {
120:
121: // return if already done
122: if (bytecode != null)
123: return bytecode;
124:
125: try {
126:
127: if (SanityManager.DEBUG) {
128: if (SanityManager.DEBUG_ON("ClassLineNumbers")) {
129:
130: ClassFormatOutput sout = new ClassFormatOutput(2);
131:
132: int cpiUTF = classHold.addUtf8("GC.java");
133:
134: sout.putU2(cpiUTF);
135:
136: classHold.addAttribute("SourceFile", sout);
137: }
138: }
139:
140: // the class is now complete, get its bytecode.
141: bytecode = classHold.getFileFormat();
142:
143: } catch (IOException ioe) {
144: throw StandardException.newException(
145: SQLState.GENERATED_CLASS_LINKAGE_ERROR, ioe,
146: getFullName());
147: }
148:
149: // release resources, we have the code now.
150: // name is not released, it may still be accessed.
151: classHold = null;
152:
153: if (SanityManager.DEBUG) {
154: if (SanityManager.DEBUG_ON("DumpClassFile")) {
155: /* Dump the file in derby.system.home */
156: String systemHome = System.getProperty(
157: Property.SYSTEM_HOME_PROPERTY, ".");
158: writeClassFile(systemHome, false, null);
159: }
160: }
161:
162: if (SanityManager.DEBUG) {
163: if (SanityManager.DEBUG_ON("ByteCodeGenInstr")) {
164: SanityManager.DEBUG("ByteCodeGenInstr",
165: "GEN complete for class " + name);
166: }
167: }
168:
169: if (limitMsg != null)
170: throw StandardException.newException(
171: SQLState.GENERATED_CLASS_LIMIT_EXCEEDED,
172: getFullName(), limitMsg);
173: return bytecode;
174: }
175:
176: /**
177: * the class's unqualified name
178: */
179: public String getName() {
180: return name;
181: }
182:
183: /**
184: * a method. Once it is created, thrown
185: * exceptions, statements, and local variable declarations
186: * must be added to it. It is put into its defining class
187: * when it is created.
188: * <verbatim>
189: Java: #modifiers #returnType #methodName() {}
190: // modifiers is the | of the JVM constants for
191: // the modifiers such as static, public, etc.
192: </verbatim>
193: * <p>
194: * This is used to start a constructor as well; pass in
195: * null for the returnType when used in that manner.
196: *
197: * See java.lang.reflect.Modifiers
198: * @param modifiers the | of the Modifiers
199: * constants representing the visibility and control of this
200: * method.
201: * @param returnType the return type of the method as its
202: * Java language type name.
203: * @param methodName the name of the method.
204: *
205: * @return the method builder.
206: */
207: public MethodBuilder newMethodBuilder(int modifiers,
208: String returnType, String methodName) {
209:
210: return newMethodBuilder(modifiers, returnType, methodName,
211: (String[]) null);
212:
213: }
214:
215: /**
216: * a method with parameters. Once it is created, thrown
217: * exceptions, statements, and local variable declarations
218: * must be added to it. It is put into its defining class
219: * when it is created.
220: * <verbatim>
221: Java: #modifiers #returnType #methodName() {}
222: // modifiers is the | of the JVM constants for
223: // the modifiers such as static, public, etc.
224: </verbatim>
225: * <p>
226: * This is used to start a constructor as well; pass in
227: * null for the returnType when used in that manner.
228: *
229: * See java.lang.reflect.Modifiers
230: * @param modifiers the | of the Modifiers
231: * constants representing the visibility and control of this
232: * method.
233: * @param returnType the return type of the method as its
234: * Java language type name.
235: * @param methodName the name of the method.
236: * @param parms an array of ParameterDeclarations representing the
237: * method's parameters
238: *
239: * @return the method builder.
240: */
241: public MethodBuilder newMethodBuilder(int modifiers,
242: String returnType, String methodName, String[] parms) {
243:
244: if (SanityManager.DEBUG) {
245: SanityManager.ASSERT(returnType != null);
246: }
247:
248: BCMethod m = new BCMethod(this , returnType, methodName,
249: modifiers, parms, factory);
250:
251: return m;
252:
253: }
254:
255: /**
256: * a constructor. Once it is created, thrown
257: * exceptions, statements, and local variable declarations
258: * must be added to it. It is put into its defining class
259: * when it is created.
260: * <verbatim>
261: Java: #modifiers #className() {}
262: // modifiers is the | of the JVM constants for
263: // the modifiers such as static, public, etc.
264: // className is taken from definingClass.getName()
265: </verbatim>
266: * <p>
267: * This is used to start a constructor as well; pass in
268: * null for the returnType when used in that manner.
269: *
270: * See Modifiers
271: * @param modifiers the | of the Modifiers
272: * constants representing the visibility and control of this
273: * method.
274: *
275: * @return the method builder for the constructor.
276: */
277: public MethodBuilder newConstructorBuilder(int modifiers) {
278:
279: BCMethod m = new BCMethod(this , "void", "<init>", modifiers,
280: (String[]) null, factory);
281:
282: return m;
283: }
284:
285: //
286: // class interface
287: //
288:
289: String getSuperClassName() {
290: return super ClassName;
291: }
292:
293: /**
294: * Let those that need to get to the
295: * classModify tool to alter the class definition.
296: */
297: ClassHolder modify() {
298: return classHold;
299: }
300:
301: /*
302: ** Method descriptor caching
303: */
304:
305: BCClass(ClassFactory cf, String packageName, int classModifiers,
306: String className, String super ClassName, BCJava factory) {
307:
308: super (cf, packageName.concat(className));
309:
310: if (SanityManager.DEBUG) {
311: if (SanityManager.DEBUG_ON("ByteCodeGenInstr")) {
312: SanityManager.DEBUG("ByteCodeGenInstr",
313: "GEN starting for class " + className);
314: }
315: }
316:
317: // by the time the constructor is done, we have:
318: //
319: // package #packageName;
320: // #classModifiers class #className extends #superClassName
321: // { }
322: //
323:
324: name = className;
325: if (super ClassName == null)
326: super ClassName = "java.lang.Object";
327: this .super ClassName = super ClassName;
328:
329: classType = factory.type(getFullName());
330:
331: classHold = new ClassHolder(qualifiedName, factory
332: .type(super ClassName).vmNameSimple, classModifiers);
333:
334: this .factory = factory;
335: }
336:
337: protected ClassHolder classHold;
338:
339: protected String super ClassName;
340: protected String name;
341:
342: BCJava factory;
343: final Type classType;
344:
345: ClassFactory getClassFactory() {
346: return cf;
347: }
348:
349: public void newFieldWithAccessors(String getter, String setter,
350: int methodModifers, boolean staticField, String type) {
351:
352: String vmType = factory.type(type).vmName();
353: methodModifers |= Modifier.FINAL;
354:
355: // add a field, field has same name as get method
356: int fieldModifiers = Modifier.PRIVATE;
357: if (staticField)
358: fieldModifiers |= Modifier.STATIC;
359:
360: ClassMember field = classHold.addMember(getter, vmType,
361: fieldModifiers);
362: int cpi = classHold.addFieldReference(field);
363:
364: /*
365: ** add the get method
366: */
367:
368: String sig = BCMethodDescriptor.get(BCMethodDescriptor.EMPTY,
369: vmType, factory);
370:
371: ClassMember method = classHold.addMember(getter, sig,
372: methodModifers);
373:
374: CodeChunk chunk = new CodeChunk(this );
375:
376: // load 'this' if required
377: if (!staticField)
378: chunk.addInstr(VMOpcode.ALOAD_0); // this
379:
380: // get the field value
381: chunk.addInstrU2((staticField ? VMOpcode.GETSTATIC
382: : VMOpcode.GETFIELD), cpi);
383:
384: // and return it
385: short vmTypeId = BCJava.vmTypeId(vmType);
386:
387: chunk.addInstr(CodeChunk.RETURN_OPCODE[vmTypeId]);
388:
389: int typeWidth = Type.width(vmTypeId);
390: chunk.complete(null, classHold, method, typeWidth, 1);
391:
392: /*
393: ** add the set method
394: */
395: String[] pda = new String[1];
396: pda[0] = vmType;
397: sig = new BCMethodDescriptor(pda, VMDescriptor.VOID, factory)
398: .toString();
399: method = classHold.addMember(setter, sig, methodModifers);
400: chunk = new CodeChunk(this );
401:
402: // load 'this' if required
403: if (!staticField)
404: chunk.addInstr(VMOpcode.ALOAD_0); // this
405: // push the only parameter
406: chunk
407: .addInstr((short) (CodeChunk.LOAD_VARIABLE_FAST[vmTypeId] + 1));
408:
409: // and set the field
410: chunk.addInstrU2((staticField ? VMOpcode.PUTSTATIC
411: : VMOpcode.PUTFIELD), cpi);
412:
413: chunk.addInstr(VMOpcode.RETURN);
414:
415: chunk.complete(null, classHold, method, typeWidth
416: + (staticField ? 0 : 1), 1 + typeWidth);
417: }
418:
419: /**
420: * Add the fact that some class limit was exceeded while generating
421: * the class. We create a set of them and report at the end, this
422: * allows the generated class file to still be dumped.
423: * @param mb
424: * @param limitName
425: * @param limit
426: * @param value
427: */
428: void addLimitExceeded(BCMethod mb, String limitName, int limit,
429: int value) {
430: StringBuffer sb = new StringBuffer();
431: if (limitMsg != null) {
432: sb.append(limitMsg);
433: sb.append(", ");
434: }
435:
436: sb.append("method:");
437: sb.append(mb.getName());
438: sb.append(" ");
439: sb.append(limitName);
440: sb.append(" (");
441: sb.append(value);
442: sb.append(" > ");
443: sb.append(limit);
444: sb.append(")");
445:
446: limitMsg = sb.toString();
447: }
448:
449: /**
450: * Add the fact that some class limit was exceeded while generating
451: * the class. Text is the simple string passed in.
452: * @param rawText Text to be reported.
453: *
454: * @see BCClass#addLimitExceeded(BCMethod, String, int, int)
455: */
456: void addLimitExceeded(String rawText) {
457: if (limitMsg != null) {
458: limitMsg = limitMsg + ", " + rawText;
459: } else {
460: limitMsg = rawText;
461: }
462: }
463:
464: }
|