raku-type-callframe
class CallFrame {}
CallFrame는 보통 callframe 서브루틴으로 프로그램의 현재 상태에서 잡아낸 프레임이에요.
my $frame = callframe;
say "The above line of code ran at {$frame.file}:{$frame.line}.";
인자 없이 부르면 callframe을 호출한 그 줄에 대한 프레임 정보를 줘요. 이때 파일·줄 주석은 $?FILE과 $?LINE에 들어 있는 값과 똑같아요.
하지만 숫자를 넘겨서 다른 프레임 단계를 지정할 수도 있어요. 양수를 넘기면 프레임 단계를 위로 올라가고, 음수를 넘기면 callframe 메서드·클래스 자신이 이 정보를 만들기 위해 실행되고 있는 지점 안쪽으로 내려가요.
한 가지 주의할 점은, 프레임이 반드시 메서드나 서브루틴 호출에만 대응하지는 않는다는 거예요. Raku는 블록 같은 것에도 프레임을 만들거든요. 그래서 특정 메서드 호출에 대한 callframe이 필요하다고 해서, 그게 몇 단계 위인지 정해져 있다고 가정하면 안 돼요.
각 프레임은 주석(annotation)을 저장해요. 파일·줄 주석은 편의 메서드로 바로 접근할 수 있고, 현재 실행 중인 프레임의 코드 블록 참조는 code 메서드로 얻을 수 있어요. 또 프레임은 자기와 함께 저장된 레시컬 변수들을 모두 잡아두는데, 그건 프레임 객체에 my를 호출하면 얻을 수 있어요.
callframe 인터페이스로 호출한 루틴을 찾아서 그 호출자의 패키지를 출력하는 짧은 예시를 볼게요.
sub calling-frame() {
for 1..* -> $level {
given callframe($level) -> $frame {
when $frame ~~ CallFrame {
next unless $frame.code ~~ Routine;
say $frame.code.package;
last;
}
default {
say "no calling routine or method found";
last;
}
}
}
}
calling-frame;
호출자 정보만 추적하면 되는 경우라면, Backtrace가 더 나은 방법일 수 있어요. CallFrame는 특정 프레임에 대해 더 많은 정보를 주지만, 호출 스택을 열거하기에는 인터페이스가 번거로워요.
Methods
참고로 6.d 버전부터 CallFrame에 .raku(2019.11 릴리스 이전에는 .perl)를 호출할 수 있어요.
method code
method code()
현재 블록에 대한 호출 가능한 코드를 돌려줘요. callframe(0)이 돌려준 객체에서 호출하면 &?BLOCK에 들어 있는 값과 같아요.
my $frame;
for ^3 { FIRST $frame = callframe; say $_ * 3 };
say $frame.code()
이 경우 $frame 변수는 루프 안쪽 블록에 대한 Code를 담게 돼요.
method file
method file()
파일 주석을 조회하는 단축 메서드예요. 그래서 아래 코드는 True를 출력해요.
my $frame = callframe(0);
say $frame.file eq $frame.annotations<file>;
method line
method line()
줄 주석을 조회하는 단축 메서드예요. 예를 들어 아래 두 호출은 완전히 같아요.
say callframe(1).line;
say callframe(1).annotations<line>;
method annotations
method annotations()
호출자의 주석, 즉 line과 file을 담고 있는 Map을 돌려줘요. 주석 정보를 얻을 때는 편의 메서드들을 쓰는 게 더 간편해요.
say callframe.annotations.^name; # OUTPUT: «Map»
say callframe.annotations<file> eq callframe.file; # OUTPUT: «True»
method my
method my()
프레임의 레시컬 스코프와 연결된 모든 변수와 그 값을 이름으로 담은 Hash를 돌려줘요.
sub some-value {
my $the-answer = 42;
callframe(0);
}
my $frame = some-value();
say $frame.my<$the-answer>; # OUTPUT: «42»
Routines
sub callframe
sub callframe(Int:D $level = 0)
주어진 단계에 대한 CallFrame 객체를 돌려줘요. 단계를 안 주면 기본값은 0이에요. 양수 단계는 프레임 스택을 위로, 음수 단계는 아래로(callframe 호출과 그 안쪽으로) 이동해요.
해당 단계에 호출 정보가 없으면 Mu를 돌려줘요. 음수 단계는 예외로 이어질 수 있어요.