CoolFace
Apppublic

Ratan1729/code-execution

sourceHugging Faceupdated 8mo agoView on Hugging Face
1likes
process.executor.js338 linesDownload Raw Back to services
1// ─── Process Executor Service ────────────────────────2// Core engine: runs code directly via child_process (no Docker)3// For Hugging Face Spaces and environments without Docker-in-Docker4// BATCH MODE: compile once, run all test cases sequentially5 6const { spawn } = require("child_process");7const path = require("path");8const config = require("../config");9const {10  createWorkDir,11  writeFile,12  readFile,13  cleanup,14  ensureDir,15} = require("../utils/file.util");16const logger = require("../utils/logger.util");17 18class ProcessExecutor {19  /**20   * Execute code with a SINGLE input (used by /api/execute)21   * Wraps batchExecute with 1 test case22   */23  async execute({ language, code, input = "", timeLimit, memoryLimit }) {24    const results = await this.batchExecute({25      language,26      code,27      inputs: [input],28      timeLimit,29      memoryLimit,30    });31    return results[0];32  }33 34  /**35   * Execute code against MULTIPLE inputs sequentially.36   * Compile once, run N times — just like Docker version but using subprocess.37   *38   * @param {Object} params39   * @param {string} params.language - c | cpp | python40   * @param {string} params.code - Source code41   * @param {string[]} params.inputs - Array of stdin inputs42   * @param {number} params.timeLimit - Time limit per test case (seconds)43   * @param {number} params.memoryLimit - Memory limit (MB)44   * @returns {Object[]} Array of execution results45   */46  async batchExecute({ language, code, inputs = [], timeLimit, memoryLimit }) {47    const langConfig = config.LANGUAGES[language];48    if (!langConfig) {49      throw new Error(`Unsupported language: ${language}`);50    }51 52    timeLimit = Math.min(53      timeLimit || config.DEFAULT_TIME_LIMIT,54      config.MAX_TIME_LIMIT,55    );56    memoryLimit = Math.min(57      memoryLimit || config.DEFAULT_MEMORY_LIMIT,58      config.MAX_MEMORY_LIMIT,59    );60 61    const numCases = inputs.length;62    const workDir = await createWorkDir();63    const codeDir = path.join(workDir, "code");64 65    try {66      // Write code file67      const codeFile = path.join(codeDir, langConfig.fileName);68      await writeFile(codeFile, code);69 70      // ─── Phase 1: Compilation ───71      const startCompile = process.hrtime.bigint();72      const compileResult = await this._compile(language, codeDir, workDir);73      const compileTimeMs = Number(74        (process.hrtime.bigint() - startCompile) / 1_000_000n,75      );76 77      if (compileResult.error) {78        // Compilation failed — return CE for all test cases79        return inputs.map(() => ({80          verdict: "CE",81          output: "",82          error: this._truncate(compileResult.error, 5000),83          executionTime: 0,84          memoryUsed: 0,85          exitCode: 1,86          wallTime: compileTimeMs,87          language,88          timeLimit,89          memoryLimit,90        }));91      }92 93      logger.info(`🔨 Compiled ${language} in ${compileTimeMs}ms`);94 95      // ─── Phase 2: Run each test case ───96      const results = [];97      const overallStart = process.hrtime.bigint();98 99      for (let i = 0; i < numCases; i++) {100        const result = await this._runTestCase(101          language,102          compileResult.execCmd,103          inputs[i] || "",104          timeLimit,105          memoryLimit,106          workDir,107        );108        results.push({109          ...result,110          language,111          timeLimit,112          memoryLimit,113        });114      }115 116      const wallTimeMs = Number(117        (process.hrtime.bigint() - overallStart) / 1_000_000n,118      );119      logger.info(`⚡ Executed ${numCases} test cases in ${wallTimeMs}ms`);120 121      return results;122    } catch (err) {123      logger.error(`Execution failed: ${err.message}`);124      return inputs.map(() => ({125        verdict: "IE",126        output: "",127        error: `Internal Error: ${err.message}`,128        executionTime: 0,129        memoryUsed: 0,130        exitCode: -1,131        wallTime: 0,132        language,133        timeLimit,134        memoryLimit,135      }));136    } finally {137      await cleanup(workDir);138    }139  }140 141  /**142   * Compile code based on language143   * Returns { execCmd } on success or { error } on failure144   */145  async _compile(language, codeDir, workDir) {146    const binaryPath = path.join(workDir, "solution");147 148    switch (language) {149      case "c": {150        const sourceFile = path.join(codeDir, "solution.c");151        const result = await this._runProcess(152          "gcc",153          ["-O2", "-Wall", "-o", binaryPath, sourceFile, "-lm"],154          { timeout: 10000 },155        );156        if (result.exitCode !== 0) {157          return {158            error: result.stderr || result.stdout || "Compilation failed",159          };160        }161        return { execCmd: [binaryPath] };162      }163 164      case "cpp": {165        const sourceFile = path.join(codeDir, "solution.cpp");166        const result = await this._runProcess(167          "g++",168          ["-O2", "-std=c++17", "-Wall", "-o", binaryPath, sourceFile, "-lm"],169          { timeout: 10000 },170        );171        if (result.exitCode !== 0) {172          return {173            error: result.stderr || result.stdout || "Compilation failed",174          };175        }176        return { execCmd: [binaryPath] };177      }178 179      case "python": {180        const sourceFile = path.join(codeDir, "solution.py");181        // Syntax check only182        const result = await this._runProcess(183          "python3",184          ["-m", "py_compile", sourceFile],185          { timeout: 10000 },186        );187        if (result.exitCode !== 0) {188          return { error: result.stderr || result.stdout || "Syntax error" };189        }190        return { execCmd: ["python3", sourceFile] };191      }192 193      default:194        return { error: `Unsupported language: ${language}` };195    }196  }197 198  /**199   * Run a single test case200   */201  async _runTestCase(202    language,203    execCmd,204    input,205    timeLimit,206    memoryLimit,207    workDir,208  ) {209    const startTime = process.hrtime.bigint();210 211    const result = await this._runProcess(execCmd[0], execCmd.slice(1), {212      timeout: timeLimit * 1000,213      input,214      memoryLimit,215    });216 217    const executionTime = Number(218      (process.hrtime.bigint() - startTime) / 1_000_000n,219    );220 221    // Determine verdict222    let verdict = "OK";223    if (result.timedOut) {224      verdict = "TLE";225    } else if (result.exitCode === 137) {226      verdict = "MLE";227    } else if (result.exitCode === 139 || result.signal === "SIGSEGV") {228      verdict = "RE";229      result.stderr = (result.stderr || "") + "\nSegmentation fault (SIGSEGV)";230    } else if (result.exitCode === 136 || result.signal === "SIGFPE") {231      verdict = "RE";232      result.stderr =233        (result.stderr || "") + "\nFloating point exception (SIGFPE)";234    } else if (result.exitCode === 134 || result.signal === "SIGABRT") {235      verdict = "RE";236      result.stderr = (result.stderr || "") + "\nAborted (SIGABRT)";237    } else if (result.exitCode !== 0) {238      verdict = "RE";239    }240 241    return {242      verdict,243      output: this._truncate(result.stdout, 10000),244      error: this._truncate(result.stderr, 5000),245      executionTime,246      memoryUsed: result.memoryUsed || 0,247      exitCode: result.exitCode,248      wallTime: executionTime,249    };250  }251 252  /**253   * Run a process with timeout and optional stdin input254   */255  _runProcess(cmd, args, options = {}) {256    return new Promise((resolve) => {257      const { timeout = 30000, input = "", memoryLimit = 256 } = options;258 259      let stdout = "";260      let stderr = "";261      let timedOut = false;262      let memoryUsed = 0;263 264      const proc = spawn(cmd, args, {265        stdio: ["pipe", "pipe", "pipe"],266        timeout,267      });268 269      // Set up timeout270      const timer = setTimeout(() => {271        timedOut = true;272        proc.kill("SIGKILL");273      }, timeout);274 275      // Write input276      if (input) {277        proc.stdin.write(input);278      }279      proc.stdin.end();280 281      // Collect stdout282      proc.stdout.on("data", (data) => {283        if (stdout.length < 100000) {284          stdout += data.toString();285        }286      });287 288      // Collect stderr289      proc.stderr.on("data", (data) => {290        if (stderr.length < 50000) {291          stderr += data.toString();292        }293      });294 295      proc.on("close", (code, signal) => {296        clearTimeout(timer);297 298        // Try to get memory usage (best effort, may not be accurate)299        try {300          if (proc.memoryUsage) {301            memoryUsed = Math.round(proc.memoryUsage().rss / 1024);302          }303        } catch {304          // Ignore memory measurement errors305        }306 307        resolve({308          exitCode: code ?? (timedOut ? 124 : -1),309          signal,310          stdout,311          stderr,312          timedOut,313          memoryUsed,314        });315      });316 317      proc.on("error", (err) => {318        clearTimeout(timer);319        resolve({320          exitCode: -1,321          stdout: "",322          stderr: err.message,323          timedOut: false,324          memoryUsed: 0,325        });326      });327    });328  }329 330  _truncate(str, maxLen) {331    if (!str) return "";332    if (str.length <= maxLen) return str;333    return str.substring(0, maxLen) + `\n... (truncated)`;334  }335}336 337module.exports = new ProcessExecutor();338