1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.services.bytecode.BCClass |
4 | |
5 | Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.services.bytecode; |
22 | |
23 | import org.apache.derby.iapi.services.compiler.ClassBuilder; |
24 | import org.apache.derby.iapi.services.compiler.MethodBuilder; |
25 | import org.apache.derby.iapi.services.compiler.LocalField; |
26 | |
27 | import org.apache.derby.iapi.services.classfile.ClassHolder; |
28 | import org.apache.derby.iapi.services.classfile.ClassMember; |
29 | import org.apache.derby.iapi.services.classfile.ClassFormatOutput; |
30 | import org.apache.derby.iapi.services.loader.ClassFactory; |
31 | |
32 | import org.apache.derby.iapi.services.monitor.Monitor; |
33 | |
34 | import org.apache.derby.iapi.error.StandardException; |
35 | import org.apache.derby.iapi.reference.Property; |
36 | import org.apache.derby.iapi.reference.SQLState; |
37 | |
38 | import org.apache.derby.iapi.util.ByteArray; |
39 | import org.apache.derby.iapi.services.classfile.VMOpcode; |
40 | |
41 | import java.lang.reflect.Modifier; |
42 | |
43 | import org.apache.derby.iapi.services.sanity.SanityManager; |
44 | import org.apache.derby.iapi.services.classfile.VMDescriptor; |
45 | |
46 | import org.apache.derby.impl.services.bytecode.GClass; |
47 | |
48 | import java.io.IOException; |
49 | |
50 | /** |
51 | * ClassBuilder is used to construct a java class's byte array |
52 | * representation. |
53 | * |
54 | * Limitations: |
55 | * No checking for language use violations such as invalid modifiers |
56 | * or duplicate field names. |
57 | * All classes must have a superclass; java.lang.Object must be |
58 | * supplied if there is no superclass. |
59 | * |
60 | * <p> |
61 | * When a class is first created, it has: |
62 | * <ul> |
63 | * <li> a superclass |
64 | * <li> modifiers |
65 | * <li> a name |
66 | * <li> a package |
67 | * <li> no superinterfaces, methods, fields, or constructors |
68 | * <li> an empty static initializer |
69 | * <li> an empty initializer |
70 | * </ul> |
71 | * <p> |
72 | * MethodBuilder implementations are required to supply a way for |
73 | * Generators to give them code. Most typically, they may have |
74 | * a stream to which the Generator writes the code that is of |
75 | * the type to satisfy what the Generator is writing. |
76 | * <p> |
77 | * BCClass is a ClassBuilder implementation for generating java bytecode |
78 | * directly. |
79 | * |
80 | */ |
81 | class BCClass extends GClass { |
82 | |
83 | /** |
84 | * Simple text indicating any limits execeeded while generating |
85 | * the class file. |
86 | */ |
87 | String limitMsg; |
88 | |
89 | // |
90 | // ClassBuilder interface |
91 | // |
92 | /** |
93 | * add a field to this class. Fields cannot |
94 | * be initialized here, they must be initialized |
95 | * in the static initializer code (static fields) |
96 | * or in the constructors. |
97 | * <p> |
98 | * static fields also added to this list, |
99 | * with the modifier set appropriately. |
100 | */ |
101 | public LocalField addField(String javaType, String name, int modifiers) { |
102 | |
103 | Type type = factory.type(javaType); |
104 | // put it into the class holder right away. |
105 | ClassMember field = classHold.addMember(name, type.vmName(), modifiers); |
106 | int cpi = classHold.addFieldReference(field); |
107 | |
108 | return new BCLocalField(type, cpi); |
109 | } |
110 | |
111 | /** |
112 | * At the time the class is completed and bytecode |
113 | * generated, if there are no constructors then |
114 | * the default no-arg constructor will be defined. |
115 | */ |
116 | public ByteArray getClassBytecode() throws StandardException { |
117 | |
118 | // return if already done |
119 | if (bytecode != null) return bytecode; |
120 | |
121 | try { |
122 | |
123 | if (SanityManager.DEBUG) { |
124 | if (SanityManager.DEBUG_ON("ClassLineNumbers")) { |
125 | |
126 | ClassFormatOutput sout = new ClassFormatOutput(2); |
127 | |
128 | int cpiUTF = classHold.addUtf8("GC.java"); |
129 | |
130 | sout.putU2(cpiUTF); |
131 | |
132 | classHold.addAttribute("SourceFile", sout); |
133 | } |
134 | } |
135 | |
136 | // the class is now complete, get its bytecode. |
137 | bytecode = classHold.getFileFormat(); |
138 | |
139 | } catch (IOException ioe) { |
140 | throw StandardException.newException( |
141 | SQLState.GENERATED_CLASS_LINKAGE_ERROR, ioe, getFullName()); |
142 | } |
143 | |
144 | // release resources, we have the code now. |
145 | // name is not released, it may still be accessed. |
146 | classHold = null; |
147 | |
148 | if (SanityManager.DEBUG) { |
149 | if (SanityManager.DEBUG_ON("DumpClassFile")) { |
150 | /* Dump the file in derby.system.home */ |
151 | String systemHome = System.getProperty(Property.SYSTEM_HOME_PROPERTY,"."); |
152 | writeClassFile(systemHome,false,null); |
153 | } |
154 | } |
155 | |
156 | if (SanityManager.DEBUG) { |
157 | if (SanityManager.DEBUG_ON("ByteCodeGenInstr")) { |
158 | SanityManager.DEBUG("ByteCodeGenInstr", |
159 | "GEN complete for class "+name); |
160 | } |
161 | } |
162 | |
163 | if (limitMsg != null) |
164 | throw StandardException.newException( |
165 | SQLState.GENERATED_CLASS_LIMIT_EXCEEDED, getFullName(), limitMsg); |
166 | return bytecode; |
167 | } |
168 | |
169 | |
170 | /** |
171 | * the class's unqualified name |
172 | */ |
173 | public String getName() { |
174 | return name; |
175 | } |
176 | |
177 | /** |
178 | * a method. Once it is created, thrown |
179 | * exceptions, statements, and local variable declarations |
180 | * must be added to it. It is put into its defining class |
181 | * when it is created. |
182 | * <verbatim> |
183 | Java: #modifiers #returnType #methodName() {} |
184 | // modifiers is the | of the JVM constants for |
185 | // the modifiers such as static, public, etc. |
186 | </verbatim> |
187 | * <p> |
188 | * This is used to start a constructor as well; pass in |
189 | * null for the returnType when used in that manner. |
190 | * |
191 | * See java.lang.reflect.Modifiers |
192 | * @param modifiers the | of the Modifiers |
193 | * constants representing the visibility and control of this |
194 | * method. |
195 | * @param returnType the return type of the method as its |
196 | * Java language type name. |
197 | * @param methodName the name of the method. |
198 | * |
199 | * @return the method builder. |
200 | */ |
201 | public MethodBuilder newMethodBuilder(int modifiers, String returnType, |
202 | String methodName) { |
203 | |
204 | return newMethodBuilder(modifiers, returnType, |
205 | methodName, (String[]) null); |
206 | |
207 | } |
208 | |
209 | |
210 | /** |
211 | * a method with parameters. Once it is created, thrown |
212 | * exceptions, statements, and local variable declarations |
213 | * must be added to it. It is put into its defining class |
214 | * when it is created. |
215 | * <verbatim> |
216 | Java: #modifiers #returnType #methodName() {} |
217 | // modifiers is the | of the JVM constants for |
218 | // the modifiers such as static, public, etc. |
219 | </verbatim> |
220 | * <p> |
221 | * This is used to start a constructor as well; pass in |
222 | * null for the returnType when used in that manner. |
223 | * |
224 | * See java.lang.reflect.Modifiers |
225 | * @param modifiers the | of the Modifiers |
226 | * constants representing the visibility and control of this |
227 | * method. |
228 | * @param returnType the return type of the method as its |
229 | * Java language type name. |
230 | * @param methodName the name of the method. |
231 | * @param parms an array of ParameterDeclarations representing the |
232 | * method's parameters |
233 | * |
234 | * @return the method builder. |
235 | */ |
236 | public MethodBuilder newMethodBuilder(int modifiers, String returnType, |
237 | String methodName, String[] parms) { |
238 | |
239 | if (SanityManager.DEBUG) { |
240 | SanityManager.ASSERT(returnType!=null); |
241 | } |
242 | |
243 | BCMethod m = new BCMethod(this, |
244 | returnType, |
245 | methodName, |
246 | modifiers, |
247 | parms, |
248 | factory); |
249 | |
250 | return m; |
251 | |
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>", |
280 | modifiers, |
281 | (String []) null, |
282 | factory); |
283 | |
284 | return m; |
285 | } |
286 | // |
287 | // class interface |
288 | // |
289 | |
290 | String getSuperClassName() { |
291 | return superClassName; |
292 | } |
293 | |
294 | /** |
295 | * Let those that need to get to the |
296 | * classModify tool to alter the class definition. |
297 | */ |
298 | ClassHolder modify() { |
299 | return classHold; |
300 | } |
301 | |
302 | /* |
303 | ** Method descriptor caching |
304 | */ |
305 | |
306 | BCClass(ClassFactory cf, String packageName, int classModifiers, |
307 | String className, String superClassName, |
308 | BCJava factory) { |
309 | |
310 | super(cf, packageName.concat(className)); |
311 | |
312 | if (SanityManager.DEBUG) { |
313 | if (SanityManager.DEBUG_ON("ByteCodeGenInstr")) { |
314 | SanityManager.DEBUG("ByteCodeGenInstr", |
315 | "GEN starting for class "+className); |
316 | } |
317 | } |
318 | |
319 | // by the time the constructor is done, we have: |
320 | // |
321 | // package #packageName; |
322 | // #classModifiers class #className extends #superClassName |
323 | // { } |
324 | // |
325 | |
326 | name = className; |
327 | if (superClassName == null) |
328 | superClassName = "java.lang.Object"; |
329 | this.superClassName = superClassName; |
330 | |
331 | classType = factory.type(getFullName()); |
332 | |
333 | classHold = new ClassHolder(qualifiedName, factory.type(superClassName).vmNameSimple, classModifiers); |
334 | |
335 | this.factory = factory; |
336 | } |
337 | |
338 | protected ClassHolder classHold; |
339 | |
340 | protected String superClassName; |
341 | protected String name; |
342 | |
343 | BCJava factory; |
344 | final Type classType; |
345 | |
346 | ClassFactory getClassFactory() { |
347 | return cf; |
348 | } |
349 | |
350 | public void newFieldWithAccessors(String getter, String setter, |
351 | int methodModifers, |
352 | boolean staticField, String type) { |
353 | |
354 | String vmType = factory.type(type).vmName(); |
355 | methodModifers |= Modifier.FINAL; |
356 | |
357 | |
358 | // add a field, field has same name as get method |
359 | int fieldModifiers = Modifier.PRIVATE; |
360 | if (staticField) |
361 | fieldModifiers |= Modifier.STATIC; |
362 | |
363 | ClassMember field = classHold.addMember(getter, vmType, fieldModifiers); |
364 | int cpi = classHold.addFieldReference(field); |
365 | |
366 | /* |
367 | ** add the get method |
368 | */ |
369 | |
370 | String sig = BCMethodDescriptor.get(BCMethodDescriptor.EMPTY, vmType, factory); |
371 | |
372 | ClassMember method = classHold.addMember(getter, sig, methodModifers); |
373 | |
374 | CodeChunk chunk = new CodeChunk(); |
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 : VMOpcode.GETFIELD), cpi); |
382 | |
383 | // and return it |
384 | short vmTypeId = BCJava.vmTypeId(vmType); |
385 | |
386 | chunk.addInstr(CodeChunk.RETURN_OPCODE[vmTypeId]); |
387 | |
388 | int typeWidth = Type.width(vmTypeId); |
389 | chunk.complete(null, classHold, method, typeWidth, 1); |
390 | |
391 | /* |
392 | ** add the set method |
393 | */ |
394 | String[] pda = new String[1]; |
395 | pda[0] = vmType; |
396 | sig = new BCMethodDescriptor(pda, VMDescriptor.VOID, factory).toString(); |
397 | method = classHold.addMember(setter, sig, methodModifers); |
398 | chunk = new CodeChunk(); |
399 | |
400 | // load 'this' if required |
401 | if (!staticField) |
402 | chunk.addInstr(VMOpcode.ALOAD_0); // this |
403 | // push the only parameter |
404 | chunk.addInstr((short) (CodeChunk.LOAD_VARIABLE_FAST[vmTypeId] + 1)); |
405 | |
406 | // and set the field |
407 | chunk.addInstrU2((staticField ? VMOpcode.PUTSTATIC : VMOpcode.PUTFIELD), cpi); |
408 | |
409 | chunk.addInstr(VMOpcode.RETURN); |
410 | |
411 | chunk.complete(null, classHold, method, typeWidth + (staticField ? 0 : 1), 1 + typeWidth); |
412 | } |
413 | |
414 | /** |
415 | * Add the fact that some class limit was exceeded while generating |
416 | * the class. We create a set ofg them and report at the end, this |
417 | * allows the generated class file to still be dumped. |
418 | * @param mb |
419 | * @param limitName |
420 | * @param limit |
421 | * @param value |
422 | */ |
423 | void addLimitExceeded(BCMethod mb, String limitName, int limit, int value) |
424 | { |
425 | StringBuffer sb = new StringBuffer(); |
426 | if (limitMsg != null) |
427 | { |
428 | sb.append(limitMsg); |
429 | sb.append(", "); |
430 | } |
431 | |
432 | sb.append("method:"); |
433 | sb.append(mb.getName()); |
434 | sb.append(" "); |
435 | sb.append(limitName); |
436 | sb.append(" ("); |
437 | sb.append(value); |
438 | sb.append(" > "); |
439 | sb.append(limit); |
440 | sb.append(")"); |
441 | |
442 | limitMsg = sb.toString(); |
443 | } |
444 | |
445 | } |