diff --git a/packages/hydrojudge/src/check.ts b/packages/hydrojudge/src/check.ts deleted file mode 100644 index 1223c6dab..000000000 --- a/packages/hydrojudge/src/check.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { findFileSync } from '@hydrooj/utils/lib/utils'; -import checkers from './checkers'; -import compile from './compile'; -import { SystemError } from './error'; -import { CheckConfig, Execute } from './interface'; - -const testlibSrc = findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h'); - -export async function check(config: CheckConfig): Promise<[number, number, string]> { - if (!checkers[config.checker_type]) throw new SystemError('Unknown checker type {0}', [config.checker_type]); - const { - code, status, score, message, - } = await checkers[config.checker_type](config); - if (code) throw new SystemError('Checker returned {0}.', [code]); - return [status, score, message]; -} - -export async function compileChecker(getLang: Function, checkerType: string, checker: string, copyIn: any): Promise { - if (['default', 'strict'].includes(checkerType)) { - return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) }; - } - if (!checkers[checkerType]) throw new SystemError('Unknown checker type {0}.', [checkerType]); - if (checkerType === 'testlib') copyIn['testlib.h'] = { src: testlibSrc }; - const s = checker.replace('@', '.').split('.'); - let lang; - let langId = s.pop(); - while (s.length) { - lang = getLang(langId, false); - if (lang) break; - langId = `${s.pop()}.${langId}`; - } - if (!lang) throw new SystemError('Unknown checker language.'); - // TODO cache compiled checker - return await compile(lang, { src: checker }, copyIn); -} diff --git a/packages/hydrojudge/src/checkers.ts b/packages/hydrojudge/src/checkers.ts index 91bca0cd4..9cb9a3c24 100644 --- a/packages/hydrojudge/src/checkers.ts +++ b/packages/hydrojudge/src/checkers.ts @@ -1,6 +1,6 @@ /* eslint-disable no-template-curly-in-string */ import { STATUS } from '@hydrooj/utils/lib/status'; -import { SystemError } from './error'; +import { FormatError, SystemError } from './error'; import { CheckConfig } from './interface'; import { run } from './sandbox'; import { parse } from './testlib'; @@ -9,10 +9,9 @@ type Checker = (config: CheckConfig) => Promise<{ status: number, score: number, message: string, - code?: number, }>; -const checkers: Record = { +const checkers: Record = new Proxy({ async default(config) { const { stdout } = await run('/usr/bin/diff -BZ usrout answer', { copyIn: { @@ -196,6 +195,11 @@ const checkers: Record = { } return parse(stderr, config.score); }, -}; +}, { + get(self, key) { + if (!self[key]) throw new FormatError('Unknown checker type {0}', [key]); + return self[key]; + }, +}); export = checkers; diff --git a/packages/hydrojudge/src/compile.ts b/packages/hydrojudge/src/compile.ts index 679461917..669cceb05 100644 --- a/packages/hydrojudge/src/compile.ts +++ b/packages/hydrojudge/src/compile.ts @@ -1,11 +1,13 @@ import { LangConfig } from '@hydrooj/utils/lib/lang'; import { STATUS } from '@hydrooj/utils/lib/status'; -import { CompileError } from './error'; +import { findFileSync } from '@hydrooj/utils/lib/utils'; +import checkers from './checkers'; +import { CompileError, FormatError } from './error'; import { Execute } from './interface'; import { CopyInFile, del, run } from './sandbox'; import { compilerText } from './utils'; -export = async function compile( +export default async function compile( lang: LangConfig, code: CopyInFile, copyIn: Record = {}, next?: Function, ): Promise { const target = lang.target || 'foo'; @@ -24,7 +26,7 @@ export = async function compile( ); if (status !== STATUS.STATUS_ACCEPTED) throw new CompileError({ status, stdout, stderr }); if (!fileIds[target]) throw new CompileError({ stderr: 'Executable file not found.' }); - if (next) next({ compilerText: compilerText(stdout, stderr) }); + next?.({ compilerText: compilerText(stdout, stderr) }); return { execute, copyIn: { ...copyIn, [target]: { fileId: fileIds[target] } }, @@ -38,4 +40,25 @@ export = async function compile( clean: () => Promise.resolve(null), time, }; -}; +} + +const testlibSrc = findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h'); + +export async function compileChecker(getLang: Function, checkerType: string, checker: string, copyIn: any): Promise { + if (['default', 'strict'].includes(checkerType)) { + return { execute: '', copyIn: {}, clean: () => Promise.resolve(null) }; + } + if (!checkers[checkerType]) throw new FormatError('Unknown checker type {0}.', [checkerType]); + if (checkerType === 'testlib') copyIn['testlib.h'] = { src: testlibSrc }; + const s = checker.replace('@', '.').split('.'); + let lang; + let langId = s.pop(); + while (s.length) { + lang = getLang(langId, false); + if (lang) break; + langId = `${s.pop()}.${langId}`; + } + if (!lang) throw new FormatError('Unknown checker language.'); + // TODO cache compiled checker + return await compile(lang, { src: checker }, copyIn); +} diff --git a/packages/hydrojudge/src/interface.ts b/packages/hydrojudge/src/interface.ts index 1014c4c15..a9f4ce2b4 100644 --- a/packages/hydrojudge/src/interface.ts +++ b/packages/hydrojudge/src/interface.ts @@ -9,7 +9,6 @@ export interface Execute { export interface CheckConfig { execute: string; - checker_type: string; input: CopyInFile; output: CopyInFile; user_stdout: CopyInFile; diff --git a/packages/hydrojudge/src/judge/default.ts b/packages/hydrojudge/src/judge/default.ts index ca55dd038..e570b643e 100644 --- a/packages/hydrojudge/src/judge/default.ts +++ b/packages/hydrojudge/src/judge/default.ts @@ -1,7 +1,7 @@ import { basename } from 'path'; import { STATUS } from '@hydrooj/utils/lib/status'; -import { check, compileChecker } from '../check'; -import compile from '../compile'; +import checkers from '../checkers'; +import compile, { compileChecker } from '../compile'; import { runFlow } from '../flow'; import { Logger } from '../log'; import { CmdFile, del, run } from '../sandbox'; @@ -48,18 +48,17 @@ function judgeCase(c: NormalizedCase, sid: string) { } else if (memory_usage_kb > c.memory * 1024) { status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED; } else { - [status, score, message] = await check({ + ({ status, score, message } = await checkers[ctx.config.checker_type]({ execute: ctx.checker.execute, copyIn: ctx.checker.copyIn || {}, input: { src: c.input }, output: { src: c.output }, user_stdout: stdout, user_stderr: stderr, - checker_type: ctx.config.checker_type, score: c.score, detail: ctx.config.detail ?? true, env: { ...ctx.env, HYDRO_TESTCASE: c.id.toString() }, - }); + })); } } else if (status === STATUS.STATUS_RUNTIME_ERROR && code) { if (code < 32) message = signals[code]; diff --git a/packages/hydrojudge/src/judge/submit_answer.ts b/packages/hydrojudge/src/judge/submit_answer.ts index 1b1fb7a54..29b51cb57 100644 --- a/packages/hydrojudge/src/judge/submit_answer.ts +++ b/packages/hydrojudge/src/judge/submit_answer.ts @@ -1,7 +1,8 @@ import { basename } from 'path'; import { readFile } from 'fs-extra'; import { STATUS } from '@hydrooj/utils/lib/status'; -import { check, compileChecker } from '../check'; +import checkers from '../checkers'; +import { compileChecker } from '../compile'; import { runFlow } from '../flow'; import { del, run } from '../sandbox'; import { NormalizedCase } from '../utils'; @@ -47,18 +48,17 @@ function judgeCase(c: NormalizedCase, sid: string) { fileIds.push(...Object.values(res.fileIds)); } if (status === STATUS.STATUS_ACCEPTED) { - [status, score, message] = await check({ + ({ status, score, message } = await checkers[ctx.config.checker_type]({ execute: ctx.checker.execute, copyIn: ctx.checker.copyIn || {}, input: { src: c.input }, output: { src: c.output }, user_stdout: file, user_stderr: { content: '' }, - checker_type: ctx.config.checker_type, score: c.score, detail: ctx.config.detail ?? true, env: { ...ctx.env, HYDRO_TESTCASE: c.id.toString() }, - }); + })); } await Promise.all(fileIds.map(del)).catch(() => { /* Ignore file doesn't exist */ }); ctxSubtask.score = Score[ctxSubtask.subtask.type](ctxSubtask.score, score);