Reject EM_JS/EM_ASYNC_JS/EM_ASM outside *.js.cpp.hpp files.
| 11 | |
| 12 | |
| 13 | class AsmJsLocationChecker(FileContentChecker): |
| 14 | """Reject EM_JS/EM_ASYNC_JS/EM_ASM outside *.js.cpp.hpp files.""" |
| 15 | |
| 16 | def __init__(self) -> None: |
| 17 | self.violations: dict[str, list[tuple[int, str]]] = {} |
| 18 | |
| 19 | def should_process_file(self, file_path: str) -> bool: |
| 20 | normalized = file_path.replace("\\", "/") |
| 21 | if "/src/" not in normalized and not normalized.startswith("src/"): |
| 22 | return False |
| 23 | if "/third_party/" in normalized: |
| 24 | return False |
| 25 | return normalized.endswith((".h", ".hpp", ".cpp", ".cpp.hpp")) |
| 26 | |
| 27 | def check_file_content(self, file_content: FileContent) -> list[str]: |
| 28 | normalized = file_content.path.replace("\\", "/") |
| 29 | if normalized.endswith(".js.cpp.hpp"): |
| 30 | return [] |
| 31 | if not any( |
| 32 | token in file_content.content |
| 33 | for token in ("EM_JS(", "EM_ASYNC_JS(", "EM_ASM(") |
| 34 | ): |
| 35 | return [] |
| 36 | |
| 37 | violations: list[tuple[int, str]] = [] |
| 38 | in_multiline_comment = False |
| 39 | for line_number, line in enumerate(file_content.lines, 1): |
| 40 | stripped = line.strip() |
| 41 | |
| 42 | if "/*" in line: |
| 43 | in_multiline_comment = True |
| 44 | if in_multiline_comment and "*/" in line: |
| 45 | in_multiline_comment = False |
| 46 | continue |
| 47 | if in_multiline_comment or stripped.startswith("//"): |
| 48 | continue |
| 49 | |
| 50 | code = stripped.split("//", 1)[0].strip() |
| 51 | if not code: |
| 52 | continue |
| 53 | if _ASM_JS_MACRO.search(code): |
| 54 | violations.append( |
| 55 | ( |
| 56 | line_number, |
| 57 | "Emscripten JS glue macros must live in a *.js.cpp.hpp file: " |
| 58 | f"{stripped}", |
| 59 | ) |
| 60 | ) |
| 61 | |
| 62 | if violations: |
| 63 | self.violations[file_content.path] = violations |
| 64 | |
| 65 | return [] |
| 66 | |
| 67 | |
| 68 | def main() -> None: |
no outgoing calls