judge: hack

This commit is contained in:
undefined committed 2022-09-06 13:02:46 +08:00
1 parent 7f967060cd
commit 15ea8232a8
5 files changed
+165 -49

No files matched your search

+22 -8
View File
@@ -42,15 +42,12 @@ export default async function compile(
};
}
const testlibSrc = findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h');
const testlibFile = {
src: findFileSync('@hydrooj/hydrojudge/vendor/testlib/testlib.h')
};
export async function compileChecker(getLang: Function, checkerType: string, checker: string, copyIn: any): Promise<Execute> {
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('.');
const guessLanguage = (filename: string, getLang: Function) => {
const s = filename.replace('@', '.').split('.');
let lang;
let langId = s.pop();
while (s.length) {
@@ -58,7 +55,24 @@ export async function compileChecker(getLang: Function, checkerType: string, che
if (lang) break;
langId = `${s.pop()}.${langId}`;
}
return lang;
};
export async function compileChecker(getLang: Function, checkerType: string, checker: string, copyIn: any): Promise<Execute> {
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'] = testlibFile;
const lang = guessLanguage(checker, getLang);
if (!lang) throw new FormatError('Unknown checker language.');
// TODO cache compiled checker
return await compile(lang, { src: checker }, copyIn);
}
export async function compileValidator(getLang: Function, validator: string, copyIn: any): Promise<Execute> {
const lang = guessLanguage(validator, getLang);
if (!lang) throw new FormatError('Unknown validator language.');
// TODO cache compiled checker
return await compile(lang, { src: validator }, { ...copyIn, 'testlib.h': testlibFile });
}
+95
View File
@@ -0,0 +1,95 @@
/* eslint-disable no-sequences */
import { basename } from 'path';
import { STATUS } from '@hydrooj/utils/lib/status';
import checkers from '../checkers';
import compile, { compileChecker, compileValidator } from '../compile';
import { Execute } from '../interface';
import { CmdFile, del, run } from '../sandbox';
import signals from '../signals';
import { parseMemoryMB, parseTimeMS } from '../utils';
import { Context } from './interface';
export async function judge(ctx: Context) {
const { config, session } = ctx;
ctx.next({ status: STATUS.STATUS_COMPILING, progress: 0 });
const userExtraFiles = Object.fromEntries(
(config.user_extra_files || []).map((i) => [basename(i), { src: i }]),
);
const judgeExtraFiles = Object.fromEntries(
(config.judge_extra_files || []).map((i) => [basename(i), { src: i }]),
);
const markCleanup = (i: Execute) => (ctx.clean.push(i.clean), i);
const [execute, checker, validator, input] = await Promise.all([
compile(session.getLang(ctx.lang), ctx.code, userExtraFiles, ctx.next).then(markCleanup),
compileChecker(session.getLang, config.checker_type, config.checker, judgeExtraFiles).then(markCleanup),
compileValidator(session.getLang, config.validator, judgeExtraFiles).then(markCleanup),
ctx.session.fetchFile(ctx.input),
]);
ctx.next({ status: STATUS.STATUS_JUDGING, progress: 0 });
const validateResult = await run(
validator.execute,
{
stdin: { src: input },
copyIn: judgeExtraFiles,
time: parseTimeMS(ctx.config.time || '1s') * execute.time,
memory: parseMemoryMB(ctx.config.memory || '256m'),
},
);
if (validateResult.status !== STATUS.STATUS_ACCEPTED) {
const message = `${validateResult.files.stdout || ''}\n${validateResult.files.stderr || ''}`.trim();
return ctx.end({ status: STATUS.STATUS_FORMAT_ERROR, message });
}
const copyIn = { ...ctx.execute.copyIn };
const { filename } = ctx.config;
if (filename) copyIn[`${filename}.in`] = { src: input };
const res = await run(
execute.execute,
{
stdin: filename ? null : { src: input },
copyIn,
copyOutCached: [filename ? `${filename}.out?` : 'stdout'],
time: parseTimeMS(ctx.config.time || '1s') * execute.time,
memory: parseMemoryMB(ctx.config.memory || '256m'),
},
);
const { code, time, memory } = res;
let { status } = res;
let stdout: CmdFile = { fileId: res.fileIds[filename ? `${filename}.out` : 'stdout'] };
const stderr = { fileId: res.fileIds['stderr'] };
if (!stdout.fileId) stdout = { content: '' };
let message: any = '';
if (status === STATUS.STATUS_ACCEPTED) {
if (time > ctx.config.time * execute.time) {
status = STATUS.STATUS_TIME_LIMIT_EXCEEDED;
} else if (memory > ctx.config.memory * 1024) {
status = STATUS.STATUS_MEMORY_LIMIT_EXCEEDED;
} else {
({ status, message } = await checkers[ctx.config.checker_type]({
execute: checker.execute,
copyIn: checker.copyIn || {},
input: { src: input },
output: null,
user_stdout: stdout,
user_stderr: stderr,
score: 100,
detail: ctx.config.detail ?? true,
env: { ...ctx.env, HYDRO_TESTCASE: '0' },
}));
}
} else if (status === STATUS.STATUS_RUNTIME_ERROR && code) {
if (code < 32) message = signals[code];
else message = { message: 'Your program returned {0}.', params: [code] };
}
await Promise.all(
Object.values(res.fileIds).map((id) => del(id)),
).catch(() => { /* Ignore file doesn't exist */ });
if (message) ctx.next({ message });
return ctx.end({
status: status === STATUS.STATUS_ACCEPTED ? STATUS.STATUS_HACK_UNSUCCESSFUL : STATUS.STATUS_HACK_SUCCESSFUL,
score: 0,
time,
memory,
});
}
+2 -1
View File
@@ -1,4 +1,5 @@
import * as def from './default';
import * as hack from './hack';
import * as interactive from './interactive';
import { Context } from './interface';
import * as objective from './objective';
@@ -6,5 +7,5 @@ import * as run from './run';
import * as submit_answer from './submit_answer';
export = {
default: def, interactive, run, submit_answer, objective,
default: def, interactive, run, submit_answer, objective, hack,
} as Record<string, { judge(ctx: Context): Promise<void> }>;