OPAL: Почему SingleOriginReference больше нельзя найти после экстернализации кода в метод?

Я занимаюсь разработкой статического анализа Java Bytecode с использованием инфраструктуры OPAL.

В настоящее время мне нужно изменить структуру кода, чтобы добавить некоторые функции.

Это возникло в большом методе, из которого я должен вывести деталь в отдельный метод:

def singleCallUpperTypeBounds(
  caller: Method,
  pc: Int,
  calleeDescriptor: MethodDescriptor,
  project: Project[URL],
  callGraph: CallGraph,
  propertyStore: PropertyStore): Iterable[(Int, Set[FieldType])] = {
  val classFile = project.classFile(caller)
  val callDescriptor = caller.body.get.instructions(pc) match {
    case INVOKEVIRTUAL(_, _, d) ⇒ d
    case INVOKESPECIAL(_, _, d) ⇒ d
    case INVOKESTATIC(_, _, d) ⇒ d
    case INVOKEINTERFACE(_, _, d) ⇒ d
  }
  val analysisResult = if (!notClientCallable(caller, propertyStore) || worklist.contains(caller))
    BaseAI.perform(classFile, caller, new DefaultDomain(project, classFile, caller))(None)
  else {
    val callerTypeMap = intermediateResult.getOrElse(caller, {
      worklist = worklist.+:(caller)
      val result = singleMethodUpperTypeBounds(caller, project, callGraph, propertyStore)
      worklist = worklist.diff(Seq(caller))
      result
    })
    //Create all combinations of the upper type bounds of the parameters.
    //If a parameter is not in the TypeMap, 
    //e.g. because it is a primitive value, add it as a one element set.
    val typeCombinations = allCombinations(caller.descriptor.parameterTypes.zipWithIndex.map {
      case (t, index) =>
        callerTypeMap.getOrElse(index,
          Set[FieldType](caller.descriptor.parameterTypes(index)))
    })
    println(typeCombinations)
    //TODO Use the type combinations
    BaseAI.perform(classFile, caller, new DefaultDomain(project, classFile, caller))(None)
  }
  if (analysisResult.evaluatedInstructions.contains(pc))
    for {
      parameterIndex ← callDescriptor.parameterTypes.zipWithIndex.collect {
        //we are not interested in primitive array types
        case (t: ReferenceType, index) if {
          //may be the case for sinature polymorphic methods
          if (index >= calleeDescriptor.parametersCount) {
            true
          } else {
            val expectedType = calleeDescriptor.parameterType(index)
            !(expectedType.isArrayType && expectedType.asArrayType.elementType.isBaseType)
          }
        } ⇒ index
      }
      compileTimeType = callDescriptor.parameterType(parameterIndex)
      stackIndex = (callDescriptor.parametersCount - 1) - parameterIndex
    } yield {
      val operand = analysisResult.operandsArray(pc)(stackIndex)
      val runTimeTypes: Set[FieldType] = operand match {
        case v: analysisResult.domain.SingleOriginReferenceValue ⇒
          v.upperTypeBound.foldLeft(Set[FieldType]())((set, t) ⇒ set + t)
        case analysisResult.domain.MultipleReferenceValues(singleOriginReferenceValues) ⇒
          singleOriginReferenceValues.foldLeft(Set[FieldType]())((set, sorv) ⇒ set ++
            sorv.upperTypeBound.foldLeft(Set[FieldType]())((s, t) ⇒ s + t))
      }
      (parameterIndex, runTimeTypes)
    }
  //If the call was not evaluated, it is on a dead path. So ignore this call.
  else {
    Set[(Int, Set[FieldType])]()
  }
}

Вот почему я перенес большой блок if в конце в отдельный метод:

def evaluateAIResult(
  analysisResult: AIResult,
  pc: Int,
  calleeDescriptor: MethodDescriptor,
  callDescriptor: MethodDescriptor): Iterable[(Int, Set[FieldType])] = {
  if (analysisResult.evaluatedInstructions.contains(pc))
    for {
      parameterIndex ← callDescriptor.parameterTypes.zipWithIndex.collect {
        //we are not interested in primitive array types
        case (t: ReferenceType, index) if {
          //may be the case for sinature polymorphic methods
          if (index >= calleeDescriptor.parametersCount) {
            true
          } else {
            val expectedType = calleeDescriptor.parameterType(index)
            !(expectedType.isArrayType && expectedType.asArrayType.elementType.isBaseType)
          }
        } ⇒ index
      }
      compileTimeType = callDescriptor.parameterType(parameterIndex)
      stackIndex = (callDescriptor.parametersCount - 1) - parameterIndex
    } yield {
      val operand = analysisResult.operandsArray(pc)(stackIndex)
      val runTimeTypes: Set[FieldType] = operand match {
        case v: analysisResult.domain.SingleOriginReferenceValue ⇒
          v.upperTypeBound.foldLeft(Set[FieldType]())((set, t) ⇒ set + t)
        case analysisResult.domain.MultipleReferenceValues(singleOriginReferenceValues) ⇒
          singleOriginReferenceValues.foldLeft(Set[FieldType]())((set, sorv) ⇒ set ++
            sorv.upperTypeBound.foldLeft(Set[FieldType]())((s, t) ⇒ s + t))
      }
      (parameterIndex, runTimeTypes)
    }
  //If the call was not evaluated, it is on a dead path. So ignore this call.
  else {
    Set[(Int, Set[FieldType])]()
  }
}

По какой-то причине я теперь получаю некоторые ошибки для этих строк в Scala IDE:

case v: analysisResult.domain.SingleOriginReferenceValue ⇒
          v.upperTypeBound.foldLeft(Set[FieldType]())((set, t) ⇒ set + t)
case analysisResult.domain.MultipleReferenceValues(singleOriginReferenceValues) ⇒
          singleOriginReferenceValues.foldLeft(Set[FieldType]())((set, sorv) ⇒ set ++
            sorv.upperTypeBound.foldLeft(Set[FieldType]())((s, t) ⇒ s + t))

Сообщения об ошибках следующие:

Тип SingleOriginReferenceValue не является членом org.opalj.ai.Domain

а также

Значение MultipleReferenceValues ​​не является членом org.opalj.ai.Domain

До того, как я перенес этот if-блок в отдельный метод, эти сообщения об ошибках не появлялись. Изменение этих строк на

case v: SingleOriginReferenceValue ⇒
          v.upperTypeBound.foldLeft(Set[FieldType]())((set, t) ⇒ set + t)
case MultipleReferenceValues(singleOriginReferenceValues) ⇒
          singleOriginReferenceValues.foldLeft(Set[FieldType]())((set, sorv) ⇒ set ++
            sorv.upperTypeBound.foldLeft(Set[FieldType]())((s, t) ⇒ s + t))

и импорт импорта org.opalj.ai.domain.l1.ReferenceValues.SingleOriginReferenceValue import org.opalj.ai.domain.l1.ReferenceValues.MultipleReferenceValues ​​также не помогает.

Может кто-нибудь сказать мне, что здесь происходит?

1 ответ

Решение

В этом случае вам нужно указать (для помощника), что вам требуется AIResult с конкретным типом домена. (OPAL-AI интенсивно использует так называемые зависимые от пути типы.)

Следующее изменение сигнатуры вспомогательного метода должно помочь:

def evaluateAIResult(
  analysisResult: AIResult { val domain: l1.DefaultDomain /*or whatever your domain requirements are*/},
  pc: Int,
  calleeDescriptor: MethodDescriptor,
  callDescriptor: MethodDescriptor): Iterable[(Int, Set[FieldType])] = {...
Другие вопросы по тегам