summaryrefslogtreecommitdiffstats
path: root/bundle-plugin/src/main/scala/com/yahoo/container/plugin/classanalysis/AnalyzeClassVisitor.scala
blob: b57a07d5c302140277a45ee476c2de0a82e9ff6d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.container.plugin.classanalysis

import org.objectweb.asm._
import com.yahoo.osgi.annotation.{ExportPackage, Version}
import collection.mutable

/**
 * Picks up classes used in class files.
 * @author  tonytv
 */
private class AnalyzeClassVisitor extends ClassVisitor(Opcodes.ASM5) with AnnotationVisitorTrait with AttributeVisitorTrait {
  private var name : String = null
  protected val imports : ImportsSet = mutable.Set()
  protected var exportPackageAnnotation: Option[ExportPackageAnnotation] = None


  override def visitAttribute(attribute: Attribute): Unit = super.visitAttribute(attribute)

  override def visitMethod(access: Int, name: String, desc: String, signature: String,
                           exceptions: Array[String]): MethodVisitor = {

    imports ++= (Type.getReturnType(desc) +: Type.getArgumentTypes(desc)).flatMap(getClassName)

    imports ++= Option(exceptions) getOrElse(Array()) flatMap internalNameToClassName

    AnalyzeSignatureVisitor.analyzeMethod(signature, this)
    new AnalyzeMethodVisitor(this)
  }

  override def visitField(access: Int, name: String, desc: String, signature: String, value: AnyRef): FieldVisitor = {
    imports ++= getClassName(Type.getType(desc)).toList

    AnalyzeSignatureVisitor.analyzeField(signature, this)
    new FieldVisitor(Opcodes.ASM5) with SubVisitorTrait with AttributeVisitorTrait with AnnotationVisitorTrait {
      val analyzeClassVisitor = AnalyzeClassVisitor.this

      override def visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor = super.visitAnnotation(desc, visible)
      override def visitAttribute(attribute: Attribute): Unit = super.visitAttribute(attribute)
      override def visitEnd(): Unit = super.visitEnd()
    }
  }

  override def visit(version: Int, access: Int, name: String, signature: String, superName: String, interfaces: Array[String]) {
    this.name = internalNameToClassName(name).get

    imports ++= (superName +: interfaces) flatMap internalNameToClassName
    AnalyzeSignatureVisitor.analyzeClass(signature, this)
  }

  override def visitInnerClass(name: String, outerName: String, innerName: String, access: Int) {}
  override def visitOuterClass(owner: String, name: String, desc: String) {}
  override def visitSource(source: String, debug: String) {}
  override def visitEnd() {}

  def addImports(imports: TraversableOnce[String]) {
    this.imports ++= imports
  }

  override def visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor = {
    if (Type.getType(desc).getClassName == classOf[ExportPackage].getName) {
      visitExportPackage()
    } else {
      super.visitAnnotation(desc, visible)
    }
  }

  def visitExportPackage(): AnnotationVisitor = {
    def defaultVersionValue[T](name: String) = classOf[Version].getMethod(name).getDefaultValue().asInstanceOf[T]

    new AnnotationVisitor(Opcodes.ASM5) {
      var major: Int = defaultVersionValue("major")
      var minor: Int = defaultVersionValue("minor")
      var micro: Int = defaultVersionValue("micro")
      var qualifier: String = defaultVersionValue("qualifier")

      override def visit(name: String, value: AnyRef) {
        def valueAsInt = value.asInstanceOf[Int]

        name match {
          case "major" => major = valueAsInt
          case "minor" => minor = valueAsInt
          case "micro" => micro = valueAsInt
          case "qualifier" => qualifier = value.asInstanceOf[String]
        }
      }

      override def visitEnd() {
        exportPackageAnnotation = Some(ExportPackageAnnotation(major, minor, micro, qualifier))
      }

      override def visitEnum(name: String, desc: String, value: String) {}
      override def visitArray(name: String): AnnotationVisitor = this
      override def visitAnnotation(name: String, desc: String): AnnotationVisitor = this
    }
  }

  def result = {
    assert(!imports.contains("int"))
    new ClassFileMetaData(name, imports.toSet, exportPackageAnnotation)
  }
}