2014-10-06 2 views
1

Set에서 LinkedHashSet의 인스턴스로 인스턴스화하는 메서드 호출을 다시 작성하는 스칼라 컴파일러 플러그인을 작성하려고합니다. 불행히도 나는 이것을 이미 수행하고있는 실례를 찾을 수 없다. 다음 코드는 no-symbol does not have an owner와 함께 실패메서드 호출을 다시 작성하기위한 스칼라 컴파일러 플러그인

기록을 위해
object DemoErasureComponent extends PluginComponent with TypingTransformers with Transform { 
val global: DemoPlugin.this.global.type = DemoPlugin.this.global 

import global._ 

override val runsAfter = List("erasure") 

val phaseName = "rewrite-sets" 

def newTransformer(unit: CompilationUnit) = new SetTransformer(unit) 

class SetTransformer(unit: CompilationUnit) extends TypingTransformer(unit) { 

    override def transform(tree: Tree): Tree = tree match { 
    case [email protected]([email protected]([email protected](predef, set), name), args) if name.toString == "Set" => 
     localTyper.typed(treeCopy.Apply(tree, Ident(newTermName("LinkedHashSet")), args)) 

    case t => super.transform(tree) 
    } 
} 

, 나는 지금까지 이러한 리소스를 발견했습니다

답변

2
localTyper.typed(treeCopy.Apply(tree, Ident(newTermName("LinkedHashSet")), args)) 

여기서 tree의 유형 및 기호를 복사하는 트리 복사를 통해 새 Apply 노드를 만듭니다.

이 노드를 typecheck하면 타이 패퍼는 이미 입력 된대로 자식으로 반복되지 않으므로 Ident은 코드 생성 단계에서 충돌을 일으킬 수있는 형식 및 기호없이 통과하게됩니다.

무응답 Ident을 만들고이를 typechecking하는 대신 유틸리티 메서드를 사용하여 완전히 수정 된 참조를 TreeGen에 만드는 것이 더 일반적입니다.

gen.mkAttributedRef(typeOf[scala.collection.immutable.LinkedHashSet].typeSymbol) 

case도 꽤 의심 스럽습니다. 그런 문자열을 비교할 필요는 없습니다. 항상 Symbol을 비교하는 것이 좋습니다.

또한 컴파일러 플러그인을 삽입하는 단계에서 트리 모양을 알고 있어야합니다. 아래 그림은 티퍼 후에 트리가 apply 메소드에 대한 호출을 포함하도록 확장되었으며 지우기 후에 변수 인수가 단일 인수로 랩핑됩니다.

% qscalac -Xprint:parser,typer,erasure sandbox/test.scala 
[[syntax trees at end of     parser]] // test.scala 
package <empty> { 
    object Test extends scala.AnyRef { 
    def <init>() = { 
     super.<init>(); 
    () 
    }; 
    Set(1, 2, 3) 
    } 
} 

[[syntax trees at end of      typer]] // test.scala 
package <empty> { 
    object Test extends scala.AnyRef { 
    def <init>(): Test.type = { 
     Test.super.<init>(); 
    () 
    }; 
    scala.this.Predef.Set.apply[Int](1, 2, 3) 
    } 
} 

[[syntax trees at end of     erasure]] // test.scala 
package <empty> { 
    object Test extends Object { 
    def <init>(): Test.type = { 
     Test.super.<init>(); 
    () 
    }; 
    scala.this.Predef.Set().apply(scala.this.Predef.wrapIntArray(Array[Int]{1, 2, 3})) 
    } 
} 

악몽의 비트 일 수 HashMaps의 반복 순서에 대해 가정을함으로써 발생되는 비 - 결정 성을 추적. 하지만 이런 종류의 재 작성에주의해야합니다. 시스템 내의 정책으로 비 결정적 세트 및 맵을 사용해서는 안되는 경우, 을 직접 제거하면 사용이 충분하지 않습니다. .toSet 또는 toMap에 대한 모든 통화는 어쨌든 사용하게됩니다.

테스트 모드에서 표준 라이브러리의 바이트 코드를 계측하거나 패치 된 표준 라이브러리를 사용하여 모두 (아마도 스택 추적 기록)을 잡는 것이 더 나을 것입니다. 또는, 더 세밀 대안으로, 당신은 collection.immutable.HashSet(1, 2, 3).count(_ => true)foreach에 대한 호출과 같은 양성 용도를 필터링해야합니다,하지만 (HashTrieSet#foreach처럼 배치하는 전화를 찾을 수 있습니다.

+0

을 줄이 대해서 typeof [scala.collection.immutable.LinkedHashSet를' ] .typeSymbol'은 다음과 같이 실패합니다 :'[error (main/compile : compile) scala.ScalaReflectionException : class demo.DemoErasureComponent in compiler mirror not found. '... 그러나 이것은 작동하는 것 같습니다 :'rootMirror.getRequiredClass ("scala .collection.mutable.Set ")'... 내가 뭘 잘못하고 있는지 생각해?나는'typeOf'가 연속체 나 내가 체크 한 다른 컴파일러 플러그인에서 사용되지 않는다는 것을 알아 차렸다. – gbasler