@tracecode/harness 0.9.2 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/package.json +1 -1
- package/workers/cpp/cpp-worker.js +367 -43
- package/workers/csharp/csharp-worker.js +582 -27
- package/workers/javascript/javascript-project-worker.js +1 -1
- package/workers/javascript/javascript-worker.js +105 -5
- package/workers/python/pyodide-worker.js +7 -24
- package/workers/python/runtime-core.js +331 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ All notable changes to this project are documented here.
|
|
|
4
4
|
|
|
5
5
|
This repo uses Git tags as release boundaries. Version notes below summarize what shipped in each tagged release.
|
|
6
6
|
|
|
7
|
+
## [0.9.3] - 2026-06-05
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Added true browser batch execution for JavaScript, TypeScript, Python, C#, and C++ so multi-case runs prepare or compile once and execute the full input batch in one worker call.
|
|
12
|
+
- Kept JavaScript, TypeScript, and Python batch cases isolated with fresh globals and freshly materialized mutable inputs, including linked-list/object inputs that user code can mutate.
|
|
13
|
+
- Added compile-once browser batch drivers for C# and C++ named-function, solution-method, and ops-class execution paths.
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Fixed Python browser batch handling for default imports, script-mode inputs, and custom class materialization.
|
|
18
|
+
- Added regression coverage for batch global isolation, mutable input isolation, C# browser batch execution, and C++ compile-once batch behavior.
|
|
19
|
+
|
|
7
20
|
## [0.9.2] - 2026-06-05
|
|
8
21
|
|
|
9
22
|
### Fixed
|
package/package.json
CHANGED
|
@@ -6868,6 +6868,214 @@ ${traceReturn}
|
|
|
6868
6868
|
`;
|
|
6869
6869
|
}
|
|
6870
6870
|
|
|
6871
|
+
function buildBatchDriverSource(userCode, functionName, inputBatch, options = {}) {
|
|
6872
|
+
userCode = normalizeCppUserSource(userCode, options);
|
|
6873
|
+
const firstInputs = Array.isArray(inputBatch) && inputBatch.length > 0 && inputBatch[0] && typeof inputBatch[0] === 'object'
|
|
6874
|
+
? inputBatch[0]
|
|
6875
|
+
: {};
|
|
6876
|
+
const aliases = collectCppTypeAliases(userCode);
|
|
6877
|
+
const signature = parseMethodSignature(userCode, functionName, {
|
|
6878
|
+
parameterCount: Object.keys(firstInputs || {}).length,
|
|
6879
|
+
inputNames: Object.keys(firstInputs || {}),
|
|
6880
|
+
});
|
|
6881
|
+
const typeContext = sourceDeclaresSolutionClass(userCode)
|
|
6882
|
+
? buildCppDriverTypeContext(userCode, 'Solution', signature, aliases)
|
|
6883
|
+
: buildCppDriverTypeContext(userCode, functionName, signature, aliases);
|
|
6884
|
+
const driverSignature = qualifyCppSignatureForDriver(signature, typeContext, aliases);
|
|
6885
|
+
const usesSolutionClass = options.executionStyle !== 'function' || sourceDeclaresSolutionClass(userCode);
|
|
6886
|
+
const declarations = [];
|
|
6887
|
+
const argumentNames = [];
|
|
6888
|
+
|
|
6889
|
+
driverSignature.parameters.forEach((parameter, index) => {
|
|
6890
|
+
const localName = `__tc_arg_${index}`;
|
|
6891
|
+
const type = materializedCppType(parameter.type, aliases);
|
|
6892
|
+
declarations.push(` ${type} ${localName} = tracecode::read_json_input<${type}>(__tc_case, ${cppStringLiteral(parameter.name)}, ${index});`);
|
|
6893
|
+
argumentNames.push(localName);
|
|
6894
|
+
});
|
|
6895
|
+
|
|
6896
|
+
const returnsNull = isNullCppReturnType(driverSignature.returnType, aliases);
|
|
6897
|
+
const returnsVoid = normalizeCppType(localCppType(driverSignature.returnType), aliases) === 'void';
|
|
6898
|
+
const noStoredResult = returnsVoid || returnsNull;
|
|
6899
|
+
const voidOutputParameter = returnsVoid && driverSignature.parameters.length > 0 && isSnapshotSerializableCppType(driverSignature.parameters[0].type, aliases)
|
|
6900
|
+
? driverSignature.parameters[0]
|
|
6901
|
+
: null;
|
|
6902
|
+
const resultJsonExpression = noStoredResult
|
|
6903
|
+
? returnsNull
|
|
6904
|
+
? '"null"'
|
|
6905
|
+
: voidOutputParameter
|
|
6906
|
+
? cppJsonExpressionForValue('__tc_arg_0', voidOutputParameter.type, userCode)
|
|
6907
|
+
: '"null"'
|
|
6908
|
+
: cppJsonExpressionForValue('__tc_result', driverSignature.returnType, userCode);
|
|
6909
|
+
const callExpression = `${usesSolutionClass ? `solution.${functionName}` : functionName}(${argumentNames.join(', ')})`;
|
|
6910
|
+
const invokeAndStore = noStoredResult ? ` ${callExpression};` : ` auto __tc_result = ${callExpression};`;
|
|
6911
|
+
|
|
6912
|
+
return `${buildGeneratedIncludes(userCode, driverSignature)}
|
|
6913
|
+
using namespace std;
|
|
6914
|
+
${buildTracecodeFallbackAliases(userCode)}
|
|
6915
|
+
|
|
6916
|
+
#line 1 "${CPP_USER_SOURCE_FILE}"
|
|
6917
|
+
${userCode}
|
|
6918
|
+
${buildCppJsonObjectAdapters(typeContext, aliases)}
|
|
6919
|
+
|
|
6920
|
+
#line 1 "TraceCodeDriver.cpp"
|
|
6921
|
+
int main() {
|
|
6922
|
+
tracecode::JsonValue __tc_cases = tracecode::parse_json(tracecode::read_stdin_all());
|
|
6923
|
+
if (__tc_cases.kind != tracecode::JsonValue::Kind::Array) {
|
|
6924
|
+
std::fputs("C++ batch input must be a JSON array.\\n", stderr);
|
|
6925
|
+
return 1;
|
|
6926
|
+
}
|
|
6927
|
+
std::string __tc_results = "[";
|
|
6928
|
+
for (std::size_t __tc_case_index = 0; __tc_case_index < __tc_cases.array_values.size(); ++__tc_case_index) {
|
|
6929
|
+
const tracecode::JsonValue& __tc_case = __tc_cases.array_values[__tc_case_index];
|
|
6930
|
+
if (__tc_case_index > 0) __tc_results += ",";
|
|
6931
|
+
${usesSolutionClass ? ' Solution solution;\n' : ''}${declarations.join('\n')}
|
|
6932
|
+
${invokeAndStore}
|
|
6933
|
+
__tc_results += ${resultJsonExpression};
|
|
6934
|
+
}
|
|
6935
|
+
__tc_results += "]";
|
|
6936
|
+
tracecode::write_result_json_raw(__tc_results);
|
|
6937
|
+
return 0;
|
|
6938
|
+
}
|
|
6939
|
+
`;
|
|
6940
|
+
}
|
|
6941
|
+
|
|
6942
|
+
function buildOpsClassBatchDriverSource(userCode, className, inputBatch, options = {}) {
|
|
6943
|
+
userCode = normalizeCppUserSource(userCode, options);
|
|
6944
|
+
const firstInputs = Array.isArray(inputBatch) && inputBatch.length > 0 && inputBatch[0] && typeof inputBatch[0] === 'object'
|
|
6945
|
+
? inputBatch[0]
|
|
6946
|
+
: {};
|
|
6947
|
+
const aliases = collectCppTypeAliases(userCode);
|
|
6948
|
+
const typeContext = buildCppDriverTypeContext(userCode, className, null, aliases);
|
|
6949
|
+
const { operations, argumentsList } = getOpsClassInputs(firstInputs || {});
|
|
6950
|
+
let firstOperationIndex = 1;
|
|
6951
|
+
let constructorArgumentIndex = 0;
|
|
6952
|
+
if (operations[0] === '__init__') {
|
|
6953
|
+
firstOperationIndex = 1;
|
|
6954
|
+
constructorArgumentIndex = 0;
|
|
6955
|
+
} else if (operations[0] === className) {
|
|
6956
|
+
firstOperationIndex = 1;
|
|
6957
|
+
constructorArgumentIndex = 0;
|
|
6958
|
+
} else if (operations.length > 0) {
|
|
6959
|
+
firstOperationIndex = 0;
|
|
6960
|
+
constructorArgumentIndex = -1;
|
|
6961
|
+
} else {
|
|
6962
|
+
throw new Error(`C++ ops-class inputs must start with constructor operation "${className}".`);
|
|
6963
|
+
}
|
|
6964
|
+
|
|
6965
|
+
const lines = [];
|
|
6966
|
+
const constructorArgs = constructorArgumentIndex >= 0 ? normalizeOpsArguments(argumentsList[constructorArgumentIndex]) : [];
|
|
6967
|
+
const constructorSignature = qualifyCppSignatureForDriver(
|
|
6968
|
+
parseConstructorSignature(userCode, className, aliases, {
|
|
6969
|
+
parameterCount: constructorArgs.length,
|
|
6970
|
+
}),
|
|
6971
|
+
typeContext,
|
|
6972
|
+
aliases
|
|
6973
|
+
);
|
|
6974
|
+
if (constructorArgs.length !== constructorSignature.parameters.length) {
|
|
6975
|
+
throw new Error(`C++ ops-class constructor "${className}" expected ${constructorSignature.parameters.length} args, received ${constructorArgs.length}.`);
|
|
6976
|
+
}
|
|
6977
|
+
const constructorArgNames = constructorArgs.map((_value, index) => {
|
|
6978
|
+
const localName = `__tc_ctor_arg_${index}`;
|
|
6979
|
+
const type = materializedCppType(constructorSignature.parameters[index].type, aliases);
|
|
6980
|
+
lines.push(` ${type} ${localName} = tracecode::json_to<${type}>(__tc_ops_arg_at(__tc_ops_item_at(*__tc_arguments, ${constructorArgumentIndex}), ${index}));`);
|
|
6981
|
+
return localName;
|
|
6982
|
+
});
|
|
6983
|
+
lines.push(constructorArgs.length === 0
|
|
6984
|
+
? ` ${className} __tc_instance;`
|
|
6985
|
+
: ` ${className} __tc_instance(${constructorArgNames.join(', ')});`);
|
|
6986
|
+
lines.push(' std::vector<std::string> __tc_case_outputs;');
|
|
6987
|
+
if (constructorArgumentIndex >= 0) {
|
|
6988
|
+
lines.push(' __tc_case_outputs.push_back("null");');
|
|
6989
|
+
}
|
|
6990
|
+
|
|
6991
|
+
for (let index = firstOperationIndex; index < operations.length; index += 1) {
|
|
6992
|
+
const operation = operations[index];
|
|
6993
|
+
if (typeof operation !== 'string' || !operation.trim()) {
|
|
6994
|
+
throw new Error(`C++ ops-class operation at index ${index} must be a method name.`);
|
|
6995
|
+
}
|
|
6996
|
+
const signatureOperation = resolveCppObjectMethodMacro(userCode, operation);
|
|
6997
|
+
const signature = qualifyCppSignatureForDriver(parseMethodSignature(userCode, signatureOperation), typeContext, aliases);
|
|
6998
|
+
const args = normalizeOpsArguments(argumentsList[index]);
|
|
6999
|
+
if (args.length !== signature.parameters.length) {
|
|
7000
|
+
throw new Error(`C++ ops-class method "${operation}" expected ${signature.parameters.length} args, received ${args.length}.`);
|
|
7001
|
+
}
|
|
7002
|
+
const argNames = [];
|
|
7003
|
+
signature.parameters.forEach((parameter, argIndex) => {
|
|
7004
|
+
const localName = `__tc_op_${index}_arg_${argIndex}`;
|
|
7005
|
+
const type = materializedCppType(parameter.type, aliases);
|
|
7006
|
+
lines.push(` ${type} ${localName} = tracecode::json_to<${type}>(__tc_ops_arg_at(__tc_ops_item_at(*__tc_arguments, ${index}), ${argIndex}));`);
|
|
7007
|
+
argNames.push(localName);
|
|
7008
|
+
});
|
|
7009
|
+
if (normalizeCppType(signature.returnType, aliases) === 'void' || isNullCppReturnType(signature.returnType, aliases)) {
|
|
7010
|
+
lines.push(` __tc_instance.${signatureOperation}(${argNames.join(', ')});`);
|
|
7011
|
+
lines.push(' __tc_case_outputs.push_back("null");');
|
|
7012
|
+
} else {
|
|
7013
|
+
lines.push(` auto __tc_op_${index}_result = __tc_instance.${signatureOperation}(${argNames.join(', ')});`);
|
|
7014
|
+
lines.push(` __tc_case_outputs.push_back(${cppJsonExpressionForValue(`__tc_op_${index}_result`, signature.returnType, userCode)});`);
|
|
7015
|
+
}
|
|
7016
|
+
}
|
|
7017
|
+
const operationChecks = operations.map((operation, index) => ` if (__tc_operations && __tc_operations->kind == tracecode::JsonValue::Kind::Array && __tc_operations->array_values.size() > ${index} && __tc_operations->array_values[${index}].kind == tracecode::JsonValue::Kind::String && __tc_operations->array_values[${index}].string_value != ${cppStringLiteral(String(operation))}) {
|
|
7018
|
+
std::fputs("C++ ops-class case operation name differs from the first case.\\n", stderr);
|
|
7019
|
+
return 1;
|
|
7020
|
+
}`);
|
|
7021
|
+
|
|
7022
|
+
return `${buildGeneratedIncludes(userCode, { parameters: [] })}
|
|
7023
|
+
using namespace std;
|
|
7024
|
+
${buildTracecodeFallbackAliases(userCode)}
|
|
7025
|
+
|
|
7026
|
+
#line 1 "${CPP_USER_SOURCE_FILE}"
|
|
7027
|
+
${userCode}
|
|
7028
|
+
${buildCppJsonObjectAdapters(typeContext, aliases)}
|
|
7029
|
+
|
|
7030
|
+
#line 1 "TraceCodeDriver.cpp"
|
|
7031
|
+
int main() {
|
|
7032
|
+
tracecode::JsonValue __tc_cases = tracecode::parse_json(tracecode::read_stdin_all());
|
|
7033
|
+
if (__tc_cases.kind != tracecode::JsonValue::Kind::Array) {
|
|
7034
|
+
std::fputs("C++ ops-class batch input must be a JSON array.\\n", stderr);
|
|
7035
|
+
return 1;
|
|
7036
|
+
}
|
|
7037
|
+
const tracecode::JsonValue __tc_null_value;
|
|
7038
|
+
auto __tc_ops_item_at = [&__tc_null_value](const tracecode::JsonValue& values, std::size_t index) -> const tracecode::JsonValue& {
|
|
7039
|
+
if (values.kind == tracecode::JsonValue::Kind::Array && index < values.array_values.size()) return values.array_values[index];
|
|
7040
|
+
return __tc_null_value;
|
|
7041
|
+
};
|
|
7042
|
+
auto __tc_ops_arg_at = [&__tc_ops_item_at](const tracecode::JsonValue& values, std::size_t index) -> const tracecode::JsonValue& {
|
|
7043
|
+
if (values.kind == tracecode::JsonValue::Kind::Array) return __tc_ops_item_at(values, index);
|
|
7044
|
+
return values;
|
|
7045
|
+
};
|
|
7046
|
+
std::string __tc_results = "[";
|
|
7047
|
+
for (std::size_t __tc_case_index = 0; __tc_case_index < __tc_cases.array_values.size(); ++__tc_case_index) {
|
|
7048
|
+
const tracecode::JsonValue& __tc_case = __tc_cases.array_values[__tc_case_index];
|
|
7049
|
+
const tracecode::JsonValue* __tc_operations = tracecode::object_get(__tc_case, "operations");
|
|
7050
|
+
if (!__tc_operations) __tc_operations = tracecode::object_get(__tc_case, "ops");
|
|
7051
|
+
const tracecode::JsonValue* __tc_arguments = tracecode::object_get(__tc_case, "arguments");
|
|
7052
|
+
if (!__tc_arguments) __tc_arguments = tracecode::object_get(__tc_case, "args");
|
|
7053
|
+
if (!__tc_arguments || __tc_arguments->kind != tracecode::JsonValue::Kind::Array) {
|
|
7054
|
+
std::fputs("C++ ops-class case must include arguments or args array.\\n", stderr);
|
|
7055
|
+
return 1;
|
|
7056
|
+
}
|
|
7057
|
+
if (__tc_operations && __tc_operations->kind == tracecode::JsonValue::Kind::Array && __tc_operations->array_values.size() != ${operations.length}) {
|
|
7058
|
+
std::fputs("C++ ops-class case operations length differs from the first case.\\n", stderr);
|
|
7059
|
+
return 1;
|
|
7060
|
+
}
|
|
7061
|
+
${operationChecks.join('\n')}
|
|
7062
|
+
if (__tc_case_index > 0) __tc_results += ",";
|
|
7063
|
+
${lines.join('\n')}
|
|
7064
|
+
std::string __tc_case_json = "[";
|
|
7065
|
+
for (std::size_t __tc_i = 0; __tc_i < __tc_case_outputs.size(); ++__tc_i) {
|
|
7066
|
+
if (__tc_i > 0) __tc_case_json += ",";
|
|
7067
|
+
__tc_case_json += __tc_case_outputs[__tc_i];
|
|
7068
|
+
}
|
|
7069
|
+
__tc_case_json += "]";
|
|
7070
|
+
__tc_results += __tc_case_json;
|
|
7071
|
+
}
|
|
7072
|
+
__tc_results += "]";
|
|
7073
|
+
tracecode::write_result_json_raw(__tc_results);
|
|
7074
|
+
return 0;
|
|
7075
|
+
}
|
|
7076
|
+
`;
|
|
7077
|
+
}
|
|
7078
|
+
|
|
6871
7079
|
function scriptLineCount(source) {
|
|
6872
7080
|
return String(source || '').split(/\r?\n/).length;
|
|
6873
7081
|
}
|
|
@@ -8231,7 +8439,9 @@ async function compileAndRun(source, functionName, inputs, options = {}) {
|
|
|
8231
8439
|
}
|
|
8232
8440
|
const fs = toolchain.baseFs.clone();
|
|
8233
8441
|
const resourceDir = findClangResourceDir(fs);
|
|
8234
|
-
const
|
|
8442
|
+
const preparedDriverSource = typeof options.preparedDriverSource === 'string' ? options.preparedDriverSource : null;
|
|
8443
|
+
const stdinText = typeof options.stdinText === 'string' ? options.stdinText : JSON.stringify(inputs || {});
|
|
8444
|
+
const scriptRequest = !preparedDriverSource && isScriptExecutionRequest(functionName, options);
|
|
8235
8445
|
const signature = scriptRequest
|
|
8236
8446
|
? { line: 1 }
|
|
8237
8447
|
: options.executionStyle === 'ops-class'
|
|
@@ -8240,15 +8450,18 @@ async function compileAndRun(source, functionName, inputs, options = {}) {
|
|
|
8240
8450
|
parameterCount: Object.keys(inputs || {}).length,
|
|
8241
8451
|
inputNames: Object.keys(inputs || {}),
|
|
8242
8452
|
});
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8453
|
+
let driverSource = preparedDriverSource;
|
|
8454
|
+
if (!driverSource) {
|
|
8455
|
+
const driverStartedAt = now();
|
|
8456
|
+
emitRequestProgress('driver-build:start', { tracing: Boolean(options.tracing) });
|
|
8457
|
+
driverSource = scriptRequest
|
|
8458
|
+
? buildScriptDriverSource(source, options)
|
|
8459
|
+
: options.executionStyle === 'ops-class'
|
|
8460
|
+
? buildOpsClassDriverSource(source, functionName, inputs || {}, options)
|
|
8461
|
+
: buildDriverSource(source, functionName, inputs || {}, options);
|
|
8462
|
+
timings.driverBuildMs = elapsedMs(driverStartedAt);
|
|
8463
|
+
emitRequestProgress('driver-build:complete', { tracing: Boolean(options.tracing) });
|
|
8464
|
+
}
|
|
8252
8465
|
|
|
8253
8466
|
fs.addDirectory('/tmp');
|
|
8254
8467
|
fs.addFile('/tmp/TraceCodeDriver.cpp', driverSource);
|
|
@@ -8351,7 +8564,7 @@ async function compileAndRun(source, functionName, inputs, options = {}) {
|
|
|
8351
8564
|
const runStartedAt = now();
|
|
8352
8565
|
emitRequestProgress('program-run:start', { tracing: Boolean(options.tracing) });
|
|
8353
8566
|
const program = await runWasi(programModule, ['program.wasm'], fs, {
|
|
8354
|
-
stdinBytes: staticStdinBytesFromText(
|
|
8567
|
+
stdinBytes: staticStdinBytesFromText(stdinText),
|
|
8355
8568
|
});
|
|
8356
8569
|
timings.runMs = elapsedMs(runStartedAt);
|
|
8357
8570
|
emitRequestProgress('program-run:complete', { tracing: Boolean(options.tracing), runMs: timings.runMs, exitCode: program.exitCode });
|
|
@@ -8428,7 +8641,9 @@ async function compileAndRunWithExternalCompiler(source, functionName, inputs, s
|
|
|
8428
8641
|
const timings = {
|
|
8429
8642
|
...(options.timings && typeof options.timings === 'object' ? options.timings : {}),
|
|
8430
8643
|
};
|
|
8431
|
-
const
|
|
8644
|
+
const preparedDriverSource = typeof options.preparedDriverSource === 'string' ? options.preparedDriverSource : null;
|
|
8645
|
+
const stdinText = typeof options.stdinText === 'string' ? options.stdinText : JSON.stringify(inputs || {});
|
|
8646
|
+
const scriptRequest = !preparedDriverSource && isScriptExecutionRequest(functionName, options);
|
|
8432
8647
|
const signature = scriptRequest
|
|
8433
8648
|
? { line: 1 }
|
|
8434
8649
|
: options.executionStyle === 'ops-class'
|
|
@@ -8437,19 +8652,26 @@ async function compileAndRunWithExternalCompiler(source, functionName, inputs, s
|
|
|
8437
8652
|
parameterCount: Object.keys(inputs || {}).length,
|
|
8438
8653
|
inputNames: Object.keys(inputs || {}),
|
|
8439
8654
|
});
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
|
|
8655
|
+
let driverSource = preparedDriverSource;
|
|
8656
|
+
if (!driverSource) {
|
|
8657
|
+
const driverStartedAt = now();
|
|
8658
|
+
emitRequestProgress('driver-build:start', { tracing: Boolean(options.tracing), compiler: 'external' });
|
|
8659
|
+
const rawDriverSource = scriptRequest
|
|
8660
|
+
? buildScriptDriverSource(source, options)
|
|
8661
|
+
: options.executionStyle === 'ops-class'
|
|
8662
|
+
? buildOpsClassDriverSource(source, functionName, inputs || {}, options)
|
|
8663
|
+
: buildDriverSource(source, functionName, inputs || {}, options);
|
|
8664
|
+
driverSource = rawDriverSource.replace(
|
|
8665
|
+
'#include "/tracecode_runtime.hpp"',
|
|
8666
|
+
'#include "tracecode_runtime.hpp"'
|
|
8667
|
+
);
|
|
8668
|
+
timings.driverBuildMs = elapsedMs(driverStartedAt);
|
|
8669
|
+
emitRequestProgress('driver-build:complete', { tracing: Boolean(options.tracing), compiler: 'external' });
|
|
8670
|
+
}
|
|
8671
|
+
driverSource = driverSource.replace(
|
|
8448
8672
|
'#include "/tracecode_runtime.hpp"',
|
|
8449
8673
|
'#include "tracecode_runtime.hpp"'
|
|
8450
8674
|
);
|
|
8451
|
-
timings.driverBuildMs = elapsedMs(driverStartedAt);
|
|
8452
|
-
emitRequestProgress('driver-build:complete', { tracing: Boolean(options.tracing), compiler: 'external' });
|
|
8453
8675
|
|
|
8454
8676
|
const cacheKey = getProgramCacheKey('yowasp-worker', driverSource);
|
|
8455
8677
|
let programModule = getCachedProgramModule(cacheKey);
|
|
@@ -8516,7 +8738,7 @@ async function compileAndRunWithExternalCompiler(source, functionName, inputs, s
|
|
|
8516
8738
|
const runStartedAt = now();
|
|
8517
8739
|
emitRequestProgress('program-run:start', { tracing: Boolean(options.tracing), compiler: 'external' });
|
|
8518
8740
|
const program = await runWasi(programModule, ['program.wasm'], new InMemoryFileSystem(), {
|
|
8519
|
-
stdinBytes: staticStdinBytesFromText(
|
|
8741
|
+
stdinBytes: staticStdinBytesFromText(stdinText),
|
|
8520
8742
|
});
|
|
8521
8743
|
timings.runMs = elapsedMs(runStartedAt);
|
|
8522
8744
|
emitRequestProgress('program-run:complete', {
|
|
@@ -8598,7 +8820,9 @@ async function compileAndRunWithYowasp(toolchain, source, functionName, inputs,
|
|
|
8598
8820
|
const timings = {
|
|
8599
8821
|
...(options.timings && typeof options.timings === 'object' ? options.timings : {}),
|
|
8600
8822
|
};
|
|
8601
|
-
const
|
|
8823
|
+
const preparedDriverSource = typeof options.preparedDriverSource === 'string' ? options.preparedDriverSource : null;
|
|
8824
|
+
const stdinText = typeof options.stdinText === 'string' ? options.stdinText : JSON.stringify(inputs || {});
|
|
8825
|
+
const scriptRequest = !preparedDriverSource && isScriptExecutionRequest(functionName, options);
|
|
8602
8826
|
const signature = scriptRequest
|
|
8603
8827
|
? { line: 1 }
|
|
8604
8828
|
: options.executionStyle === 'ops-class'
|
|
@@ -8607,19 +8831,26 @@ async function compileAndRunWithYowasp(toolchain, source, functionName, inputs,
|
|
|
8607
8831
|
parameterCount: Object.keys(inputs || {}).length,
|
|
8608
8832
|
inputNames: Object.keys(inputs || {}),
|
|
8609
8833
|
});
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8834
|
+
let driverSource = preparedDriverSource;
|
|
8835
|
+
if (!driverSource) {
|
|
8836
|
+
const driverStartedAt = now();
|
|
8837
|
+
emitRequestProgress('driver-build:start', { tracing: Boolean(options.tracing), compiler: 'yowasp' });
|
|
8838
|
+
const rawDriverSource = scriptRequest
|
|
8839
|
+
? buildScriptDriverSource(source, options)
|
|
8840
|
+
: options.executionStyle === 'ops-class'
|
|
8841
|
+
? buildOpsClassDriverSource(source, functionName, inputs || {}, options)
|
|
8842
|
+
: buildDriverSource(source, functionName, inputs || {}, options);
|
|
8843
|
+
driverSource = rawDriverSource.replace(
|
|
8844
|
+
'#include "/tracecode_runtime.hpp"',
|
|
8845
|
+
'#include "tracecode_runtime.hpp"'
|
|
8846
|
+
);
|
|
8847
|
+
timings.driverBuildMs = elapsedMs(driverStartedAt);
|
|
8848
|
+
emitRequestProgress('driver-build:complete', { tracing: Boolean(options.tracing), compiler: 'yowasp' });
|
|
8849
|
+
}
|
|
8850
|
+
driverSource = driverSource.replace(
|
|
8618
8851
|
'#include "/tracecode_runtime.hpp"',
|
|
8619
8852
|
'#include "tracecode_runtime.hpp"'
|
|
8620
8853
|
);
|
|
8621
|
-
timings.driverBuildMs = elapsedMs(driverStartedAt);
|
|
8622
|
-
emitRequestProgress('driver-build:complete', { tracing: Boolean(options.tracing), compiler: 'yowasp' });
|
|
8623
8854
|
const stdoutChunks = [];
|
|
8624
8855
|
const stderrChunks = [];
|
|
8625
8856
|
const collect = (chunks) => (bytes) => {
|
|
@@ -8694,7 +8925,7 @@ async function compileAndRunWithYowasp(toolchain, source, functionName, inputs,
|
|
|
8694
8925
|
const runStartedAt = now();
|
|
8695
8926
|
emitRequestProgress('program-run:start', { tracing: Boolean(options.tracing), compiler: 'yowasp' });
|
|
8696
8927
|
const program = await runWasi(programModule, ['program.wasm'], new InMemoryFileSystem(), {
|
|
8697
|
-
stdinBytes: staticStdinBytesFromText(
|
|
8928
|
+
stdinBytes: staticStdinBytesFromText(stdinText),
|
|
8698
8929
|
});
|
|
8699
8930
|
timings.runMs = elapsedMs(runStartedAt);
|
|
8700
8931
|
emitRequestProgress('program-run:complete', {
|
|
@@ -8885,28 +9116,121 @@ async function handleCompileRun(payload) {
|
|
|
8885
9116
|
|
|
8886
9117
|
async function handleCompileRunBatch(payload) {
|
|
8887
9118
|
const startedAt = now();
|
|
9119
|
+
const source = payload && typeof payload.code === 'string' ? payload.code : '';
|
|
9120
|
+
const functionName = payload && typeof payload.functionName === 'string' ? payload.functionName : '';
|
|
9121
|
+
const executionStyle = payload?.executionStyle || 'solution-method';
|
|
8888
9122
|
const inputBatch = Array.isArray(payload?.inputBatch)
|
|
8889
9123
|
? payload.inputBatch.map((inputs) => (inputs && typeof inputs === 'object' ? inputs : {}))
|
|
8890
9124
|
: [];
|
|
9125
|
+
const baseTimings = () => ({ totalMs: elapsedMs(startedAt) });
|
|
9126
|
+
const failedBatchResult = (message) => ({
|
|
9127
|
+
success: false,
|
|
9128
|
+
results: [],
|
|
9129
|
+
error: message,
|
|
9130
|
+
consoleOutput: [],
|
|
9131
|
+
timings: baseTimings(),
|
|
9132
|
+
});
|
|
9133
|
+
|
|
9134
|
+
if (!source.trim()) {
|
|
9135
|
+
return failedBatchResult('C++ source is empty.');
|
|
9136
|
+
}
|
|
9137
|
+
|
|
8891
9138
|
if (inputBatch.length === 0) {
|
|
9139
|
+
return failedBatchResult('C++ batch execution requires a non-empty inputBatch array.');
|
|
9140
|
+
}
|
|
9141
|
+
|
|
9142
|
+
if (!functionName.trim() && executionStyle !== 'function') {
|
|
9143
|
+
return failedBatchResult('C++ named execution requires a function name.');
|
|
9144
|
+
}
|
|
9145
|
+
|
|
9146
|
+
if (!functionName.trim() && executionStyle === 'function') {
|
|
9147
|
+
// Script-style C++ has no stable per-case input contract today, so keep its
|
|
9148
|
+
// existing per-case behavior instead of pretending it is compile-once batch.
|
|
9149
|
+
const results = [];
|
|
9150
|
+
for (const inputs of inputBatch) {
|
|
9151
|
+
results.push(await handleCompileRun({ ...payload, inputs }));
|
|
9152
|
+
}
|
|
9153
|
+
return {
|
|
9154
|
+
success: results.every((result) => result.success === true),
|
|
9155
|
+
results,
|
|
9156
|
+
consoleOutput: results.flatMap((result) => result.consoleOutput ?? []),
|
|
9157
|
+
timings: { ...baseTimings(), batchMode: 'per-case-fallback', batchFallbackReason: 'script-without-function-name' },
|
|
9158
|
+
};
|
|
9159
|
+
}
|
|
9160
|
+
|
|
9161
|
+
let driverSource;
|
|
9162
|
+
const driverBuildStartedAt = now();
|
|
9163
|
+
try {
|
|
9164
|
+
driverSource = executionStyle === 'ops-class'
|
|
9165
|
+
? buildOpsClassBatchDriverSource(source, functionName, inputBatch, { executionStyle })
|
|
9166
|
+
: buildBatchDriverSource(source, functionName, inputBatch, { executionStyle });
|
|
9167
|
+
} catch (error) {
|
|
8892
9168
|
return {
|
|
8893
9169
|
success: false,
|
|
8894
|
-
results:
|
|
8895
|
-
|
|
9170
|
+
results: inputBatch.map(() => ({
|
|
9171
|
+
success: false,
|
|
9172
|
+
output: null,
|
|
9173
|
+
error: error instanceof Error ? error.message : String(error),
|
|
9174
|
+
consoleOutput: [],
|
|
9175
|
+
timings: baseTimings(),
|
|
9176
|
+
})),
|
|
9177
|
+
error: error instanceof Error ? error.message : String(error),
|
|
8896
9178
|
consoleOutput: [],
|
|
8897
|
-
timings:
|
|
9179
|
+
timings: baseTimings(),
|
|
8898
9180
|
};
|
|
8899
9181
|
}
|
|
8900
9182
|
|
|
9183
|
+
const firstInputs = inputBatch[0] || {};
|
|
9184
|
+
const batchResult = await compileAndRun(source, functionName, firstInputs, {
|
|
9185
|
+
executionStyle,
|
|
9186
|
+
preparedDriverSource: driverSource,
|
|
9187
|
+
stdinText: JSON.stringify(inputBatch),
|
|
9188
|
+
timings: {
|
|
9189
|
+
driverBuildMs: elapsedMs(driverBuildStartedAt),
|
|
9190
|
+
batchMode: 'compile-once',
|
|
9191
|
+
batchCaseCount: inputBatch.length,
|
|
9192
|
+
},
|
|
9193
|
+
});
|
|
9194
|
+
const batchTimings = {
|
|
9195
|
+
...(batchResult.timings && typeof batchResult.timings === 'object' ? batchResult.timings : {}),
|
|
9196
|
+
totalMs: elapsedMs(startedAt),
|
|
9197
|
+
};
|
|
9198
|
+
const consoleOutput = batchResult.consoleOutput ?? [];
|
|
9199
|
+
const outputs = Array.isArray(batchResult.output) ? batchResult.output : [];
|
|
9200
|
+
const success = batchResult.success === true && outputs.length === inputBatch.length;
|
|
9201
|
+
const runtimeError = batchResult.error ||
|
|
9202
|
+
(outputs.length !== inputBatch.length
|
|
9203
|
+
? `C++ batch returned ${outputs.length} result(s) for ${inputBatch.length} case(s).`
|
|
9204
|
+
: 'C++ batch execution failed.');
|
|
8901
9205
|
const results = [];
|
|
8902
|
-
for (
|
|
8903
|
-
|
|
9206
|
+
for (let index = 0; index < inputBatch.length; index += 1) {
|
|
9207
|
+
const caseTimings = index === 0
|
|
9208
|
+
? batchTimings
|
|
9209
|
+
: {
|
|
9210
|
+
compileMs: 0,
|
|
9211
|
+
linkMs: 0,
|
|
9212
|
+
wasmCompileMs: 0,
|
|
9213
|
+
runMs: 0,
|
|
9214
|
+
totalMs: 0,
|
|
9215
|
+
compileCacheHit: batchTimings.compileCacheHit,
|
|
9216
|
+
batchMode: 'compile-once',
|
|
9217
|
+
};
|
|
9218
|
+
results.push({
|
|
9219
|
+
success,
|
|
9220
|
+
output: success ? outputs[index] : null,
|
|
9221
|
+
...(success ? {} : { error: runtimeError }),
|
|
9222
|
+
consoleOutput,
|
|
9223
|
+
...(batchResult.timeoutReason ? { timeoutReason: batchResult.timeoutReason } : {}),
|
|
9224
|
+
...(batchResult.diagnosticStage ? { diagnosticStage: batchResult.diagnosticStage } : {}),
|
|
9225
|
+
timings: caseTimings,
|
|
9226
|
+
});
|
|
8904
9227
|
}
|
|
8905
9228
|
return {
|
|
8906
|
-
success
|
|
9229
|
+
success,
|
|
8907
9230
|
results,
|
|
8908
|
-
consoleOutput
|
|
8909
|
-
|
|
9231
|
+
consoleOutput,
|
|
9232
|
+
...(success ? {} : { error: runtimeError }),
|
|
9233
|
+
timings: batchTimings,
|
|
8910
9234
|
};
|
|
8911
9235
|
}
|
|
8912
9236
|
|