Deno 작업 일지

deno측과 기간 제한 없는 컨설턴트 계약을 맺었는데, 이를 위해 한 일들을 정리하기 위한 글이다.

버전 1.4

deno_lint

  • no-undef: (#304)
  • no-fallthrough: (#306)
  • no-redclare: (#307)
  • no-import-assign: (#312)
  • no-global-assign: (#315)

Hard 로 표시되어있던 스코프 / 코드 path 분석 관련 린트들을 구현했다.

swc

  • resolver 버그 수정 (#1056)
  • jsdoc 파서 수정 (#1059)
  • typescript_strip 패스 수정 (#1060)
  • resolver 버그 수정 (#1064)
  • parser 버그 수정 (#1068)
({ ...foo.bar } = baz);

이게 올바른 문법이 됐다.

  • resolver 버그 수정 (#1070)

#1064에서 새로 생긴 버그였다.

  • resolver 버그 수정 (#1071)

타입 관련 기능을 추가하면서 새로운 버그가 많이 생겼다.

이 버그는 코드에서 아래쪽에 있는 변수 미리 확인할 때 in_type 플래그를 false로 바꿔주지 않아서 생긴 버그로, 고치는 건 간단했다.


여기까지가 deno 1.4 버전에 들어간 패치들이다.


버전 1.5,0

deno_lint

  • no-undef: arguments 관련 버그 수정 (#319)
  • no-undef: import 관련 버그 수정 (#323)
  • no-unreachable: switch 관련 버그 수정 (#325)
  • prefer-const 멘토링 (#337)

역시 컨설턴트가 짱이다. 쉬운데 귀찮은 건 직접 구현 안하고 이렇게 이렇게 짤 수 있다 이런 것만 말해주면 되니까 정말 편하다.

  • no-unreachable: if 관련 버그 수정 (#341)

deno

  • swc_bundler 통합 (#7461)

deno bundle 명령어가 타입스크립트 컴파일러 대신 swc의 번들러를 이용하도록 변경하는 패치이다. 이건 swc의 입장에서도 매우 중요한 PR이라고 생각한다. 작업 도중에 swc의 버그를 몇 개 발견해서 패치했다.

리뷰 기다리는 데 오래 걸렸다.

swc

  • typescript::strip: import 관련 옵션 추가 (#1060)

  • swc_bundler 버그 수정 (#1075)

Circular import와 동시성 버그가 복잡하게 얽힌 이슈였다.


  • swc_bundler 버그 수정 (#1078)

위에서 발견했던 문제는 해결했는데 실제로 돌려보니까 버그가 몇개 더 있었다. 우선 fixer가 적용이 안 돼서 출력된 코드가 올바른 문법이 아닌 문제가 있었는데, 이는 금방 고쳤다.

spack (swc_bundler) 같은 경우 병렬 처리를 효율적으로 하기 위해 메인 쓰레드에서 그래프를 그리고 처리 계획을 세운 뒤 뒤 각 쓰레드에서 머지 작업을 진행한다. 그런데 계획을 세우는 코드에 버그가 있었다. cyclic import가 된 모듈이 일반적인 import 를 가지고 있는 경우 해당 import들이 처리 계획에 포함되지 않는 문제가 있었다. 좀 골치아프긴 했지만, 그래도 어떻게 잘 해결했다.

그러고 deno bundle을 돌려보니 버그가 또 있었다. 이번엔 span hygiene 문제였다.

발생 조건은 다음과 같다.

  • A라는 모듈에 a를 export 한다
  • B라는 모듈에 A에서 a를 import 해서 사용한다.
  • C라는 모듈이 export * from './A'; export * from './B';를 사용한다.
  • main 모듈이 C에서 import를 한다.

정확한 발생 조건을 찾는데도 꽤 걸렸다. 테스트를 계속 추가해가면서 결국 버그의 원인을 찾았는데, export * from './a' 할 때 span hygiene 처리가 없어서 발생한 문제였다.

  • swc_bundler 버그 수정 (#1083)

테스트삼아 deno 표준 라이브러리 이것저것들을 deno bundle로 묶어보다가 에러를 찾았다.

역시나 발생 조건이 까다로웠다.

  • A가 export * from './B'; export * from './C'를 한다.
  • B가 import { foo } from './C';를 한다.
  • 메인 모듈이 A를 export한다.

마지막 조건이 빠진 경우엔 아무 문제 없이 작동해서 #1078에서 못 잡아냈다.


  • import assertions 지원 (#1100)

  • swc_bundler 버그 처리 (#1105)

  • swc_bundler: 스택 오버플로우 해결 (#1127)

  • swc_bundler: rexport 처리 Fix (#1128)

  • swc_bundler: alias, circular import (#1141)

  • export * as ns from 'foo'; (#1142)

ES2020 스펙에 이게 들어왔기 때문에 별도의 트랜스컴파일 없이 번들러가 직접 처리하도록 변경했다.


  • swc_bundler: 한 모듈에 대해 import / export 동시에 사용 (#1152)

  • swc_bundler: 한 모듈에 대해 import / export 동시에 사용 (#1152)

  • swc_bundler: 여러 버그 수정 (#1154)

  • swc_ecma_transforms: DCE 패스 버그 수정 (#1157)

  • swc_bundler: 여러 버그 수정 (#1159)

  • swc_bundler: 모듈 처리 순서 고장 (#1166)

번들링의 결과물이 같도록 모듈 처리 순서를 고정했다.


  • VisitAll 추가: (#1176)

  • 여러가지 버그 해결: (#1189)

여기까지가 deno 1.5,9 에 들어간 내 작업물들이다.


1.5.1 ~ 1.5.4

swc

  • swc_bundler 버그 수정: (#1194)

deno 1.5가 배포되면서 여러 버그 리포트가 왔다.


  • resolver 버그 수정: (#1200)
(() => {
  function foo() {
    return new Bar();
  }
  class Bar {}
})();

위와 같은 코드에서 클래스 정의와 클래스 생성할 때 쓰인 Id의 SyntaxContext가 달라서 생긴 문제였다.


  • 번들러 버그 수정 (#1205)

이 PR은 두가지 문제를 수정했다.

우선 Circular import를 처리하려면 두 모듈간의 의존성을 분석해서 각 Statement에 인덱스를 줘야 하는데, 관련 로직에 enum을 처리하기 위한 코드가 없었다. 역시 Circular import는 만악의 근원이다. 아무리 구현 난이도를 생각 안 하고 만들었다지만... 이건 관련 로직을 추가하는 걸로 해결됐는데, es 스펙에 따른 실행 순서 때문에 좀 복잡했다. 이 문제는 Id 사용 중 함수 호출의 callee 위치에 있는 것들을 제외하는 방식으로 해결했다.

두번째 문제는 선언되기 전에 사용되는 문제였는데, 모듈 merging 이전에 의존 관계 기준으로 모듈을 정렬하는 코드에 문제가 있었다. 중첩 for 문으로 해결했는데, 이는 정렬 코드를 O(n^2)로 만들지만 어차피 import는 그렇게 많지 않기 때문에 문제가 없을 것이라 생각했다.


  • 번들러 rework (#1212)

작업하다보니 근본적인 설계 미스 때문에 딱 버그만 고칠 수가 없어서 재설계했다. 해당 작업은 https://github.com/kdy1/swc/pull/8 에 기록되어있다.


  • Codegen: 최대한 input 보존 (#1221)

  • 번들러 버그 수정: (#1242)

  • 번들러 버그 수정: (#1245)

  • 번들러 버그 수정: (#1246)

1.6.0

swc

  • 번들러 버그 수정 (#1234)

  • codegen 버그 수정 (#1287)

Ryan Dahl하고 미팅할 때 나온 얘긴데 이 PR이 deno doc의 버그를 고쳤다고 한다.

deno master

  • 번들러 버그 수정 (#1247)

오래 걸렸다. 쓰는 라이브러리에 버그가 있어서 우회하느라... 별짓을 다 시도해봤다.

  • 번들러 버그 수정 (#1296)

5시간 컷. 난 원래 디버깅이 매우 빠른 편이다.

  • Dynamic import 포함 X (#12297)

#1296에서 생긴 regression이다. 고치는 건 간단해서 금방 고쳐서 새 버전 배포했고 deno 메인 브랜치에도 적용됐다.

2021년 2월 4일 업데이트

바빠서 한동안 업데이트를 못 했다. 앞으론 버전까지 적진 않고 관련된 PR만 간단하게 적어놓겠다.


  • 번들러 버그 수정 (#1234)

순환 의존성과 export * 가 동시에 존재할 때 발생하던 버그와, import할 때 deglobbing을 하는데 이에 관련된 버그를 고쳤다.


swc에는 에러 메시지를 rustc 처럼 보여주기 위한 시스템이 있는데 deno에서도 이를 쓸 수 있도록 관련된 시스템을 공개 API로 전환했다.


  • 코드젠 / 번들러 버그 수정 (#1242)

유니코드의 코드젠에 관련된 버그와 같은 모듈을 여러 번 import 했을 때 발생하는 버그를 고쳤다.


  • export * 처리할 때 default 특수 처리 (#1245)

export *를 할 때 default export를 제외하도록 만들었다. EcmaScript 스펙을 잘 모르고 구현해서 생겼던 버그인데 고치는 건 간단했다.


  • 번들러 버그 수정 (#1246)

  • 번들러 버그 수정 (#1247)

  • 번들러 버그 수정 (#1264)

  • fixer: ?? 처리 (#1270)

swc는 AST 가지고 뭔가 작업하기 전에 괄호를 전부 없애는데, 나중에 코드젠해도 의미가 달라지지 않도록 괄호를 다시 넣어주는 패스가 fixer이다. 근데 이 패스가 ??를 제대로 처리하지 못하는 버그가 있었다.


  • parser: Infinite loop 방지 (#1274)

swc의 파서에 예상하지 못한 문자열을 받으면 bump를 호출하지 않고 에러만 반환하는 버그가 있었다. 일반적으로 밟긴 힘든 버그고, deno REPL에서 Ctrl + D였나를 눌렀을 때 이 에러가 발생했다고 한다.


  • codegen: 문자열 관련 버그 수정 (#1287)

'"에 관련된 버그를 좀 근본적인 방식으로 해결했다. 이제 AST에 관련 정보를 저장한다.


  • 번들러 버그 수정 (#1296)

  • bundler: dynamic import 버그 수정 (#1297)

dynamic import를 아직 지원하지 않는데 데이터는 읽어오는 버그가 있었다. 일단은 읽어오는 코드를 주석처리했다. 지우지 않은 건 나중에 dynamic import 구현할 때 다시 살릴 생각이기 때문이다.


  • dce 버그 수정 (#1301)

  • swc_ecma_transforms 쪼개기 (#1311)

컴파일이 너무 오래 걸려서 crate를 여러 개로 쪼갰다. 쪼개고 나니까 나도 버그 수정할 때 시간이 적게 걸려서 좋았다.


  • 번들러 버그 수정 (#1318)

  • 매크로 제거 (#1319)

컴파일 시간을 줄이기 위해 매크로를 하나 없앴다.


  • 타입스크립트 namepsace 지원 (#1325)

  • 타입스크립트 4.2 대응 (#1330)

  • 타입스크립트 enum 지원 (#1340)

  • 번들러 버그 수정 (#1342)

  • 번들러 버그 수정 (#1346)

Flattening 번들러가 어렵긴 어렵다. 정적 분석만으로 런타임의 동작을 다 따라해야 하기 때문인데, 나중에 minifer가 다 완성되고 나면 왜 내가 기를 쓰고 flattening 번들러를 만들었는지 알게 될 것이다.


  • 번들러 버그 수정 (#1349)

  • type-only namepsace 지원 (#1361)

타입스크립트 네임스페이스 구현할 때 이 경우를 생각 못했다.


  • bundler: 패스 순서 변경 (#1363)

IIFE 핸들러를 fixer 이후에 실행하도록 변경했다.


  • bundler: Infinite loop 방지 (#1369)

  • 번들러 버그 수정 (#1373)