swc-project / swc

Rust-based platform for the Web
https://swc.rs
Apache License 2.0
31.18k stars 1.23k forks source link

swc/core panic using parse() and transform() APIs #9533

Open TheLarkInn opened 2 months ago

TheLarkInn commented 2 months ago

Describe the bug

Error: failed to handle: index out of bounds: the len is 15 but the index is 18

Stack backtrace:
   0: <unknown>
   1: <unknown>
   2: <unknown>
   3: <unknown>
   4: <unknown>
   5: <unknown>
   6: _ZZN4node14ThreadPoolWork12ScheduleWorkEvENUlP9uv_work_sE_4_FUNES2_
   7: worker
             at /home/iojs/build/ws/out/../deps/uv/src/threadpool.c:122:5
   8: <unknown>
   9: <unknown>
Error: failed to handle: index out of bounds: the len is 15 but the index is 18

Stack backtrace:
   0: <unknown>
   1: <unknown>
   2: <unknown>
   3: <unknown>
   4: <unknown>
   5: <unknown>
   6: _ZZN4node14ThreadPoolWork12ScheduleWorkEvENUlP9uv_work_sE_4_FUNES2_
   7: worker
             at /home/iojs/build/ws/out/../deps/uv/src/threadpool.c:122:5
   8: <unknown>
   9: <unknown>
Error: failed to handle: index out of bounds: the len is 8 but the index is 9

Stack backtrace:
   0: <unknown>
   1: <unknown>
   2: <unknown>
   3: <unknown>
   4: <unknown>
   5: <unknown>
   6: _ZZN4node14ThreadPoolWork12ScheduleWorkEvENUlP9uv_work_sE_4_FUNES2_
   7: worker
             at /home/iojs/build/ws/out/../deps/uv/src/threadpool.c:122:5
   8: <unknown>
   9: <unknown>
Error: failed to handle: index out of bounds: the len is 8 but the index is 9

Stack backtrace:
   0: <unknown>
   1: <unknown>
   2: <unknown>
   3: <unknown>
   4: <unknown>
   5: <unknown>
   6: _ZZN4node14ThreadPoolWork12ScheduleWorkEvENUlP9uv_work_sE_4_FUNES2_
   7: worker
             at /home/iojs/build/ws/out/../deps/uv/src/threadpool.c:122:5
   8: <unknown>
   9: <unknown>

Input code

No response

Config

const options: Options = {
      cwd: buildFolderPath,
      root: srcDir,
      rootMode: 'root',
      configFile: false,
      swcrc: false,
      minify: false,

      inputSourceMap: false,
      sourceRoot,
      isModule: true,

      module: moduleConfig,
      jsc: {
        target,
        externalHelpers: tsconfig.options.importHelpers,
        parser: undefined,
        transform: {
          legacyDecorator: experimentalDecorators,
          react: {},
          useDefineForClassFields
        }
      }
    };

Playground link (or link to the minimal reproduction)

https://play.swc.rs/?version=1.7.23&code=H4sIAAAAAAAAA%2B2WW4vjNhSA3wP5D3rbp4kt323ahbZk3zoMzCwLW0pWko9jTRzJWHIu%2F76yk6UTHWhL6U4pHZOALH860rnqwKnXgyVCK2NJozX5nnxZLp5aaYj7PY9ulpGG7YDAie37DggHwUYDRFrSMkOUtq1UW2I1qTU5StsS2wLpO3beDnpUNWHuL514YPW8Ag4wnNGiYVRqmtqPnZXTPrYd3ApD9HAd9lp3K7JcLBe%2F8FF2dWUHpkwvO%2FiVPF2HTkJFgqMedqZnAkyga9PfHYFfBkLv91oFFjrYgx3OwZ4ptoUhMIMIHt3iDtYHUPZHZmBlzXJBCAkrMj3hKQ85ressjqFIyR35blQ7pY%2Fq%2FUxRj8pCkXNERb6skDcNomKfSmlDEZX4VAJCICpFsooUy8oQlfEcUTnaMU5KRBVIVpwkiCpvKJ41wLPYp7CfPw3Sfg0caXZ%2Fy9ed5NePzyZ4GOSBifNPHTNGNlIwK7V6OvewenbupyE6Zp0iw1DP%2BSllOUVupZFPNbxBKtNb53PORBOHiEo8qhRNCLfUt0kSqWo4XVODpv5ZeUZRCNLMP2tdCBQ2NMcaZdiGBbIOzVE409KXBXmOQjDyfFtnYQEosaNb33JRsgxqREWIqgXyWhT7VBmlgCjPtwKiIs0QlfoUYzjmosynwpDjc%2BU%2BVUCOdyzQjglH8Rt5ti%2BiJGUoJmLP9mUSxgnKq9izfZEXLnQQFSFZLMGUZ%2FuSJsCQ7WPP9kXSFHH%2BZ3n1jxelS5JNBejbJDFMd5wJfujlah7O%2BfwKev18mXwFzUzLPgCz4wCvpRuy6aso%2BSC7jg3XehxfawK5xjBPS5EzF72bz5%2FvE6VroMnT3Eo9uFbqk9ubRo%2BihXrsYHpbH9b3H7uHcjxspoNtzHqTbD58vF8%2FRptZfla9FE%2BzTKRiqqsTDcN8r%2F%2FRwywJWr2HQGpnsNk0wdFpPtpgtQpq6E0wHmYNXzR8oqKuuqbz%2Fi%2FrVFIkWeHqP6pAcXFL5bQuUlTz4rLyjxeePOpyCvLOTSm2h%2Fr9O9IzJcXOXQCTLqNxMaAF6wLBhq0OBthKMznr94tSDMyCWUl9lzU0r6PIXVk0DUPaBOYoNhc334WrOF%2Bl8zJzVpadNr02QXveSlCwGkwV52WVpNVyMUslzmJEN4RP7bWp5n67AzU17AXh7ts0cSHdVLlcGMvEjjhj7VwkCqje%2Btp%2Ft6%2F9L7eVb23fW9v3f2z73q7Xv369Om65%2BPIbQXnmoFsSAAA%3D&config=H4sIAAAAAAAAA1WPSw7DIAwF9zkF8rrbdtE79BAWdSIifrKJVBTl7iUE0maH3xsz8jooBbNoeKq1PMsQkYX4nEsi2Sf8lARIOxTNJia49XaWvRrRCtVoOxpIyBOluiX3hoMNQajjLXPGmzH%2FC3VwkUnkCu4o%2BsnSVTc0JbjwXmrZDkk50qF%2FwA%2FqsvNjMPLqm4kXGrYvhlQioBQBAAA%3D

SWC Info output

No response

Expected behavior

Error doesn't occur.

Actual behavior

Errors with obscure panic

Version

@swc/core: 1.7.23

Additional context

We are using the parse and transform API's async in a pseudo example seen below:

  const parsePromise: Promise<void> = Async.forEachAsync(
    sourceFilePaths,
    async (srcFilePath: string) => {
      const tsx: boolean = endsWithCharacterX(srcFilePath);
      const parserOptions: Buffer = tsx ? serializedTsxParserOptions : serializedNonTsxParserOptions;

      const start: number = performance.now();
      try {
        logger.terminal.writeDebugLine(`Parsing`, srcFilePath);
        const jsonAST: string = await parseFile(srcFilePath, parserOptions);
        serializedASTByFilePath.set(srcFilePath, jsonAST);
      } catch (error) {
        errors.push([srcFilePath, error as Error]);
        return;
      } finally {
        const end: number = performance.now();
        parseTimes.push([srcFilePath, end - start]);
      }
    },
    {
      concurrency: 4
    }
  );
  await Async.forEachAsync(
    outputs.iterateLeafNodes(),
    async (entry: [string, ITransformItem]) => {
      const { srcFilePath, relativeSrcFilePath, options, jsFilePath, mapFilePath } = entry[1];

      const jsonAST: string | undefined = serializedASTByFilePath.get(srcFilePath);
      if (!jsonAST) {
        errors.push([jsFilePath, new Error(`Missing AST for '${srcFilePath}'`)]);
        return;
      }

      let result: Output | undefined;

      const start: number = performance.now();
      try {
        logger.terminal.writeDebugLine(`Transpiling: ${srcFilePath}`);

        // TODO: Consider transformFile instead of parse + transform from bindings
        result = await transform(jsonAST, true, options);
      } catch (error) {
        errors.push([jsFilePath, error as Error]);
        return;
      } finally {
        const end: number = performance.now();
        transformTimes.push([jsFilePath, end - start]);
      }

      if (result) {
        let { code, map } = result;

        if (mapFilePath && map) {
          code += `\n//#sourceMappingUrl=./${basename(mapFilePath)}`;
          const parsedMap: ISourceMap = JSON.parse(map);
          parsedMap.sources[0] = relativeSrcFilePath;
          map = JSON.stringify(parsedMap);
          writeQueue.push([`${buildFolderPath}${mapFilePath}`, Buffer.from(map)]);
        }

        writeQueue.push([`${buildFolderPath}${jsFilePath}`, Buffer.from(code)]);
      }
    },
    {
      concurrency: 4
    }
  ).then(
    () => writeQueue.finish(),
    (error) => {
      writeQueue.finish();
      throw error;
    }
  );
TheLarkInn commented 2 months ago

Is there any debug mode to show which file or call this panic'd on? Happy to provide more useful debugging information. Still trying to track down whether this is a file specific error.

TheLarkInn commented 2 months ago

I used the playground on a file (thinking this was a file-specific/syntax problem) and then it worked successfully. So I saw there was an API (since we don't really need to parse, and we have the files) the transformFile() API instead, and this seemed to work.

So there is potentially something either with parse() or transform()

dmichon-msft commented 2 months ago

This may also be a product of us using the underlying bindings directly to reduce duplicated serialization work. We had been using parseFile() + transform() instead of transformFile() to support passing the same AST to multiple different transforms, e.g. to emit both an ESM version and a CommonJS version of the same source module.

The behavior of our test rig should be analogous to calling transformFile where options.plugin = x => x.

kdy1 commented 1 month ago

We had been using parseFile() + transform() instead of transformFile() to support passing the same AST to multiple different transforms

This is slower than using transformFile twice