@@ -232,6 +232,205 @@ abstract class BTypes[G <: Global](val __global_dont_use: G) {
232
232
}
233
233
}
234
234
235
+ /**
236
+ * InnerClass and EnclosingMethod attributes (EnclosingMethod is displayed as OUTERCLASS in asm).
237
+ *
238
+ * In this summary, "class" means "class or interface".
239
+ *
240
+ * JLS: http://docs.oracle.com/javase/specs/jls/se8/html/index.html
241
+ * JVMS: http://docs.oracle.com/javase/specs/jvms/se8/html/index.html
242
+ *
243
+ * Terminology
244
+ * -----------
245
+ *
246
+ * - Nested class (JLS 8): class whose declaration occurs within the body of another class
247
+ *
248
+ * - Top-level class (JLS 8): non-nested class
249
+ *
250
+ * - Inner class (JLS 8.1.3): nested class that is not (explicitly or implicitly) static
251
+ *
252
+ * - Member class (JLS 8.5): class directly enclosed in the body of a class (and not, for
253
+ * example, defined in a method). Member classes cannot be anonymous. May be static.
254
+ *
255
+ * - Local class (JLS 14.3): nested, non-anonymous class that is not a member of a class
256
+ * - cannot be static (therefore they are "inner" classes)
257
+ * - can be defined in a method, a constructor or in an initializer block
258
+ *
259
+ * - Initializer block (JLS 8.6 / 8.7): block of statements in a java class
260
+ * - static initializer: executed before constructor body
261
+ * - instance initializer: exectued when class is initialized (instance creation, static
262
+ * field access, ...)
263
+ *
264
+ *
265
+ * InnerClass
266
+ * ----------
267
+ *
268
+ * The JVMS 4.7.6 requires an entry for every class mentioned in a CONSTANT_Class_info in the
269
+ * constant pool (CP) that is not a member of a package (JLS 7.1).
270
+ *
271
+ * The JLS 13.1, points 9. / 10. requires: a class must reference (in the CP)
272
+ * - its immediately enclosing class
273
+ * - all of its member classes
274
+ * - all local and anonymous classes that appear elsewhere (method, constructor, initializer
275
+ * block, field initializer)
276
+ *
277
+ * In a comment, the 4.7.6 spec says: this implies an entry in the InnerClass attribute for
278
+ * - All enclosing classes (except the outermost, which is top-level)
279
+ * - My comment: not sure how this is implied, below (*) a Java counter-example.
280
+ * In any case, the Java compiler seems to add all enclosing classes, even if they are not
281
+ * otherwise mentioned in the CP. So we should do the same.
282
+ * - All nested classes (including anonymous and local, but not transitively)
283
+ *
284
+ * Fields in the InnerClass entries:
285
+ * - inner class: the (nested) class C we are talking about
286
+ * - outer class: the class of which C is a member. Has to be null for non-members, i.e. for
287
+ * local and anonymous classes.
288
+ * - inner name: A string with the simple name of the inner class. Null for anonymous classes.
289
+ * - flags: access property flags, details in JVMS, table in 4.7.6.
290
+ *
291
+ *
292
+ * Note 1: when a nested class is present in the InnerClass attribute, all of its enclosing
293
+ * classes have to be present as well (by the rules above). Example:
294
+ *
295
+ * class Outer { class I1 { class I2 { } } }
296
+ * class User { Outer.I1.I2 foo() { } }
297
+ *
298
+ * The return type "Outer.I1.I2" puts "Outer$I1$I2" in the CP, therefore the class is added to the
299
+ * InnerClass attribute. For this entry, the "outer class" field will be "Outer$I1". This in turn
300
+ * adds "Outer$I1" to the CP, which requires adding that class to the InnerClass attribute.
301
+ * (For local / anonymous classes this would not be the case, since the "outer class" attribute
302
+ * would be empty. However, no class (other than the enclosing class) can refer to them, as they
303
+ * have no name.)
304
+ *
305
+ * In the current implementation of the Scala compiler, when adding a class to the InnerClass
306
+ * attribute, all of its enclosing classes will be added as well. Javac seems to do the same,
307
+ * see (*).
308
+ *
309
+ *
310
+ * Note 2: If a class name is mentioned only in a CONSTANT_Utf8_info, but not in a
311
+ * CONSTANT_Class_info, the JVMS does not require an entry in the InnerClass attribute. However,
312
+ * the Java compiler seems to add such classes anyway. For example, when using an annotation, the
313
+ * annotation class is stored as a CONSTANT_Utf8_info in the CP:
314
+ *
315
+ * @O.Ann void foo() { }
316
+ *
317
+ * adds "const #13 = Asciz LO$Ann;;" in the constant pool. The "RuntimeInvisibleAnnotations"
318
+ * attribute refers to that constant pool entry. Even though there is no other reference to
319
+ * `O.Ann`, the java compiler adds an entry for that class to the InnerClass attribute (which
320
+ * entails adding a CONSTANT_Class_info for the class).
321
+ *
322
+ *
323
+ *
324
+ * EnclosingMethod
325
+ * ---------------
326
+ *
327
+ * JVMS 4.7.7: the attribute must be present "if and only if it represents a local class
328
+ * or an anonymous class" (i.e. not for member classes).
329
+ *
330
+ * Fields:
331
+ * - class: the enclosing class
332
+ * - method: the enclosing method (or constructor). Null if the class is not enclosed by a
333
+ * method, i.e. for
334
+ * - local or anonymous classes defined in (static or non-static) initializer blocks
335
+ * - anonymous classes defined in initializer blocks or field initializers
336
+ *
337
+ * Note: the field is required for anonymous classes defined within local variable
338
+ * initializers (within a method), Java example below (**).
339
+ *
340
+ * Currently, the Scala compiler sets "method" to the class constructor for classes
341
+ * defined in initializer blocks or field initializers. This is probably OK, since the
342
+ * Scala compiler desugars these statements into to the primary constructor.
343
+ *
344
+ *
345
+ * (*)
346
+ * public class Test {
347
+ * void foo() {
348
+ * class Foo1 {
349
+ * // constructor statement block
350
+ * {
351
+ * class Foo2 {
352
+ * class Foo3 { }
353
+ * }
354
+ * }
355
+ * }
356
+ * }
357
+ * }
358
+ *
359
+ * The class file Test$1Foo1$1Foo2$Foo3 has no reference to the class Test$1Foo1, however it
360
+ * still contains an InnerClass attribute for Test$1Foo1.
361
+ * Maybe this is just because the Java compiler follows the JVMS comment ("InnerClasses
362
+ * information for each enclosing class").
363
+ *
364
+ *
365
+ * (**)
366
+ * void foo() {
367
+ * // anonymous class defined in local variable initializer expression.
368
+ * Runnable x = true ? (new Runnable() {
369
+ * public void run() { return; }
370
+ * }) : null;
371
+ * }
372
+ *
373
+ * The EnclosingMethod attribute of the anonymous class mentions "foo" in the "method" field.
374
+ *
375
+ *
376
+ * Java Compatibility
377
+ * ------------------
378
+ *
379
+ * In the InnerClass entry for classes in top-level modules, the "outer class" is emitted as the
380
+ * mirror class (or the existing companion class), i.e. C1 is nested in T (not T$).
381
+ * For classes nested in a nested object, the "outer class" is the module class: C2 is nested in T$N$
382
+ * object T {
383
+ * class C1
384
+ * object N { class C2 }
385
+ * }
386
+ *
387
+ * Reason: java compat. It's a "best effort" "solution". If you want to use "C1" from Java, you
388
+ * can write "T.C1", and the Java compiler will translate that to the classfile T$C1.
389
+ *
390
+ * If we would emit the "outer class" of C1 as "T$", then in Java you'd need to write "T$.C1"
391
+ * because the java compiler looks at the InnerClass attribute to find if an inner class exists.
392
+ * However, the Java compiler would then translate the '.' to '$' and you'd get the class name
393
+ * "T$$C1". This class file obviously does not exist.
394
+ *
395
+ * Directly using the encoded class name "T$C1" in Java does not work: since the classfile
396
+ * describes a nested class, the Java compiler hides it from the classpath and will report
397
+ * "cannot find symbol T$C1". This means that the class T.N.C2 cannot be referenced from a
398
+ * Java source file in any way.
399
+ *
400
+ *
401
+ * STATIC flag
402
+ * -----------
403
+ *
404
+ * Java: static nested classes have the "static" flag in the InnerClass attribute. This is not the
405
+ * case for local classes defined within a static method, even though such classes, as they are
406
+ * defined in a static context, don't take an "outer" instance.
407
+ * Non-static nested classes (inner classes, including local classes defined in a non-static
408
+ * method) take an "outer" instance on construction.
409
+ *
410
+ * Scala: Explicitouter adds an "outer" parameter to nested classes, except for classes defined
411
+ * in a static context, i.e. when all outer classes are module classes.
412
+ * package p
413
+ * object O1 {
414
+ * class C1 // static
415
+ * object O2 {
416
+ * def f = {
417
+ * class C2 { // static
418
+ * class C3 // non-static, needs outer
419
+ * }
420
+ * }
421
+ * }
422
+ * }
423
+ *
424
+ * Int the InnerClass attribute, the `static` flag is added for all classes defined in a static
425
+ * context, i.e. also for C2. This is different than in Java.
426
+ *
427
+ *
428
+ * Mirror Classes
429
+ * --------------
430
+ *
431
+ * TODO: innerclass attributes on mirror class, bean info class
432
+ */
433
+
235
434
/**
236
435
* Class or Interface type.
237
436
*
0 commit comments