소스 매핑

소스 매핑 (Source Mappings)

컴파일러는 AST 출력의 일부로, AST의 각 노드가 나타내는 소스 코드 범위를 제공해요. 이 정보는 AST 기반으로 에러를 보고하는 정적 분석 도구부터, 지역 변수와 그 사용처를 강조하는 디버깅 도구까지 다양한 용도로 쓰여요. additionally 바이트코드에서 그 명령을 만들어낸 소스 코드 범위로의 매핑도 생성할 수 있어요.

출처: 문서

본문

컴파일러는 AST 출력의 일부로, AST의 각 노드가 표현하는 소스 코드 범위를 제공해요. 이는 AST를 기반으로 에러를 보고하는 정적 분석 도구와, 지역 변수와 그 사용을 강조하는 디버깅 도구에 이르기까지 다양한 용도로 사용될 수 있어요.

게다가 컴파일러는 바이트코드에서 그 명령을 만들어낸 소스 코드 범위로의 매핑도 생성할 수 있어요. 이것은 바이트코드 수준에서 동작하는 정적 분석 도구와, 디버거 안에서 소스 코드의 현재 위치를 표시하거나 브레이크포인트 처리를 할 때에도 중요해요. 이 매핑은 점프 유형(jump type)과 수정자 깊이(modifier depth, 아래 참고) 같은 다른 정보도 담고 있어요.

두 종류의 소스 매핑 모두 소스 파일을 가리키기 위해 정수 식별자를 사용해요. 소스 파일의 식별자는 output['sources'][sourceName]['id']에 저장돼요. 여기서 output은 표준-JSON 컴파일러 인터페이스를 JSON으로 파싱한 결과예요.

일부 유틸리티 루틴에 대해 컴파일러는 원래 입력에 포함되지 않지만 소스 매핑에서 참조되는 "내부(internal)" 소스 파일을 생성해요. 이런 소스 파일과 그 식별자는 output['contracts'][sourceName][contractName]['evm']['bytecode']['generatedSources']를 통해 얻을 수 있어요.

참고 (Note)

특정 소스 파일과 연관되지 않은 명령의 경우, 소스 매핑은 정수 식별자 -1을 할당해요. 이는 컴파일러가 생성한 인라인 어셈블리 문에서 비롯된 바이트코드 섹션에서 발생할 수 있어요.

AST 내부의 소스 매핑은 다음 표기법을 사용해요:

s:l:f

여기서 s는 소스 파일에서 범위 시작점까지의 바이트 오프셋, l은 소스 범위의 길이(바이트), f는 앞서 언급한 소스 인덱스예요.

바이트코드의 소스 매핑 인코딩은 더 복잡해요. s:l:f:j:m을 ;로 구분한 목록이에요. 각 요소는 명령 하나에 대응해요. 즉 바이트 오프셋을 쓸 수 없고 명령 오프셋을 사용해야 해요(push 명령은 1바이트보다 길기 때문이에요). 필드 s, l, f는 위와 같아요. j는 점프 명령이 함수로 들어가는지(i), 함수에서 돌아오는지(o), 아니면 예를 들어 루프의 일부로 일반 점프인지(-)를 나타내요. 마지막 필드 m은 "수정자 깊이(modifier depth)"를 나타내는 정수예요. 이 깊이는 수정자에서 플레이스홀더 문(_)에 들어갈 때마다 증가하고, 빠져나올 때 감소해요. 이를 통해 디버거는 같은 수정자가 두 번 사용되거나 단일 수정자 안에서 여러 플레이스홀더 문이 사용되는 까다로운 경우를 추적할 수 있어요.

특히 바이트코드의 소스 매핑을 압축하기 위해 다음 규칙을 사용해요:

  • 필드가 비어 있으면 앞 요소의 값을 사용해요.
  • :이 빠져 있으면 뒤따르는 모든 필드를 비어 있는 것으로 간주해요.

즉, 다음 소스 매핑은 같은 정보를 나타내요:

1:2:1;1:9:1;2:1:2;2:1:2;2:1:2
1:2:1;:9;2:1:2;;

verbatim 내장 함수를 사용하면 소스 매핑이 유효하지 않게 된다는 점에 유의해야 해요. 이 내장 함수는 잠재적으로 여러 개일 수 있는 명령 대신 단일 명령으로 간주되기 때문이에요.

더 알아보기 (Learn more)