feat:更改配置项

This commit is contained in:
Evan
2026-09-20 13:59:10 +08:00
parent c4641aee19
commit bb78d8bf5c
4 changed files with 98 additions and 34 deletions

View File

@@ -21,7 +21,7 @@ function getConfigForEnv(env) {
case 'test': case 'test':
return { url: 'https://test-service.001code.cn', disabledev: false }; return { url: 'https://test-service.001code.cn', disabledev: false };
case 'prod': case 'prod':
return { url: 'https://service.001code.cn', disabledev: true }; return { url: 'https://service.001code.com', disabledev: true };
case 'start': case 'start':
default: default:
return { url: 'http://127.0.0.1:8000', disabledev: false }; return { url: 'http://127.0.0.1:8000', disabledev: false };

View File

@@ -1,4 +1,4 @@
// Regression tests for variable scopes in the editor interpreter. // Regression tests for variable scopes and return control flow in the editor interpreter.
// Run: node scripts/test-cpp-regressions.cjs // Run: node scripts/test-cpp-regressions.cjs
const assert = require('assert'); const assert = require('assert');
const fs = require('fs'); const fs = require('fs');
@@ -15,7 +15,7 @@ const compiled = babel.transformSync(source, {
const silentConsole = {log() {}, warn() {}, error() {}}; const silentConsole = {log() {}, warn() {}, error() {}};
const context = {exports: {}, require, setTimeout, console: silentConsole, window: {}}; const context = {exports: {}, require, setTimeout, console: silentConsole, window: {}};
vm.runInNewContext(compiled, context, {filename: parserPath}); vm.runInNewContext(compiled, context, {filename: parserPath});
const {CppCodeExecutor, validateCppSyntax} = context.exports; const {CppCodeExecutor, validateCppSyntax, createCppExecutor} = context.exports;
const main = body => `int main() {\n${body}\n}`; const main = body => `int main() {\n${body}\n}`;
// Complete program from the first email screenshot; the second repeats this issue. // Complete program from the first email screenshot; the second repeats this issue.
const customerEmailCode = `void test() { const customerEmailCode = `void test() {
@@ -44,15 +44,30 @@ async function run(code, expected, configure) {
if (configure) configure(executor, context.window); if (configure) configure(executor, context.window);
const validation = await validateCppSyntax(code); const validation = await validateCppSyntax(code);
assert.strictEqual(validation.isValid, true, JSON.stringify(validation.errors)); assert.strictEqual(validation.isValid, true, JSON.stringify(validation.errors));
await executor.run(); const exitCode = await executor.run();
if (Object.prototype.hasOwnProperty.call(expected, 'exitCode')) assert.strictEqual(exitCode, expected.exitCode);
if (expected.moves) assert.deepStrictEqual(moves, expected.moves); if (expected.moves) assert.deepStrictEqual(moves, expected.moves);
if (expected.turns) assert.deepStrictEqual(turns, expected.turns); if (expected.turns) assert.deepStrictEqual(turns, expected.turns);
assert.strictEqual(executor.variables, executor.globalVariables, 'scope must unwind after execution'); assert.strictEqual(executor.variables, executor.globalVariables, 'scope must unwind after execution');
assert.strictEqual(executor.callStack.length, 0); assert.strictEqual(executor.callStack.length, 0);
assert.strictEqual(executor.currentExecutionContext, null);
assert.strictEqual(executor.isRunning, false);
return executor; return executor;
} }
const cases = [ const cases = [
['screenshot: main return 1 is a normal exit', main('playerMove(4);\nreturn 1;'), {moves: [4], exitCode: 1}],
['return 1 skips remaining main statements', main('return 1;\nplayerMove(9);'), {moves: [], exitCode: 1}],
['return 0 also stops main early', main('return 0;\nplayerMove(9);'), {moves: [], exitCode: 0}],
['negative exit code', main('return -1;'), {moves: [], exitCode: -1}],
['parenthesized return expression', main('int status = 2;\nreturn (status + 1);'), {moves: [], exitCode: 3}],
['main fallthrough returns zero', main('playerMove(1);'), {moves: [1], exitCode: 0}],
['return from for loop exits main', main('for (int i = 0; i < 3; i++) {\nplayerMove(i);\nreturn 1;\n}\nplayerMove(9);'), {moves: [0], exitCode: 1}],
['return zero inside loop is not ignored', main('for (int i = 0; i < 3; i++) {\nreturn 0;\n}\nplayerMove(9);'), {moves: [], exitCode: 0}],
['return through while and if exits main', main('while (true) {\nif (true) {\nreturn 2;\n}\nplayerMove(9);\n}\nplayerMove(8);'), {moves: [], exitCode: 2}],
['function return 0 skips only its own remaining statements', 'int f() {\nreturn 0;\nplayerMove(9);\n}\n' + main('f();\nplayerMove(1);\nreturn 1;'), {moves: [1], exitCode: 1}],
['function return inside loop resumes caller', 'int f() {\nfor (int i = 0; i < 3; i++) {\nreturn 1;\n}\nplayerMove(9);\n}\n' + main('f();\nplayerMove(1);'), {moves: [1], exitCode: 0}],
['identifier starting with return is not a return statement', main('int returnValue = 1;\nreturnValue = 2;\nplayerMove(returnValue);\nreturn 0;'), {moves: [2], exitCode: 0}],
['customer email complete program (screenshots 1 and 2)', customerEmailCode, {moves: [0], turns: [-1]}], ['customer email complete program (screenshots 1 and 2)', customerEmailCode, {moves: [0], turns: [-1]}],
['nested loops', main('for (int i = 0; i < 2; i++) {\nfor (int i = 0; i < 2; i++) {\n}\nplayerMove(i);\n}'), {moves: [0, 1]}], ['nested loops', main('for (int i = 0; i < 2; i++) {\nfor (int i = 0; i < 2; i++) {\n}\nplayerMove(i);\n}'), {moves: [0, 1]}],
['loop initializer restores shadowed value', main('int i = 9;\nfor (int i = 0; i < 2; i++) {\n}\nplayerMove(i);'), {moves: [9]}], ['loop initializer restores shadowed value', main('int i = 9;\nfor (int i = 0; i < 2; i++) {\n}\nplayerMove(i);'), {moves: [9]}],
@@ -104,5 +119,21 @@ const cases = [
console.log(`PASS scope cleanup on ${stop ? 'stop' : 'error'}`); console.log(`PASS scope cleanup on ${stop ? 'stop' : 'error'}`);
} }
console.log(`${cases.length + 4} regression checks passed`); const highlighted = [];
context.window = {isScriptRunOver: false, setHighlightedLine: line => highlighted.push(line)};
const manager = createCppExecutor();
for (const status of [1, 0]) {
assert.strictEqual(await manager.execute(main(`return ${status};`)), status);
assert.strictEqual(manager.currentExecutor, null);
assert.strictEqual(highlighted[highlighted.length - 1], null);
}
console.log('PASS editor executor manager resolves nonzero exit and can run again');
context.window.cppGameAPI = {playerMove: async () => {throw new Error('RETURN_INTERRUPT');}};
await assert.rejects(manager.execute('void f() {\nplayerMove(1);\n}\n' + main('f();')), /RETURN_INTERRUPT/);
assert.strictEqual(manager.currentExecutor, null);
assert.strictEqual(highlighted[highlighted.length - 1], null);
console.log('PASS real game errors are not mistaken for a return signal');
console.log(`${cases.length + 6} regression checks passed`);
})().catch(error => {console.error(error); process.exitCode = 1;}); })().catch(error => {console.error(error); process.exitCode = 1;});

View File

@@ -548,6 +548,14 @@ class CppVariableScope extends Map {
} }
} }
// return 是跨越循环/条件块的控制信号,只在所属函数边界消费。
// 使用独立类型,避免吞掉恰好带有相同消息的真实运行错误。
class CppReturnSignal {
constructor(value) {
this.value = value;
}
}
export class CppCodeExecutor { export class CppCodeExecutor {
constructor(code, setHighlightedLine) { constructor(code, setHighlightedLine) {
this.code = code; this.code = code;
@@ -566,7 +574,7 @@ export class CppCodeExecutor {
try { try {
this.isRunning = true; this.isRunning = true;
this.parseCode(); this.parseCode();
await this.withVariableScope(() => this.executeMain(), this.globalVariables); return await this.withVariableScope(() => this.executeMain(), this.globalVariables);
} catch (error) { } catch (error) {
console.error('C++执行错误:', error); console.error('C++执行错误:', error);
throw error; throw error;
@@ -814,12 +822,26 @@ export class CppCodeExecutor {
while (i < lines.length) { while (i < lines.length) {
const line = lines[i].trim(); const line = lines[i].trim();
if (!line || line.startsWith('//') || line === '{' || line === '}' || line === 'return 0;' || if (!line || line.startsWith('//') || line === '{' || line === '}' ||
line.match(/^\s*}\s*else\s*\{\s*$/) || line.startsWith('else')) { line.match(/^\s*}\s*else\s*\{\s*$/) || line.startsWith('else')) {
i++; i++;
continue; continue;
} }
// return 不能标记成 continue否则循环会直接跳过它。
if (/^return\b/.test(line)) {
statements.push({
type: 'control',
subtype: 'return',
content: line,
lineNumber: isMainFunction ? this.getActualLineNumber(i) : i + 1,
originalContent: line,
contextPath: isMainFunction ? 'main' : 'function'
});
i++;
continue;
}
// 检查是否是自定义函数调用(包括带括号的实参,如 f((n + 1)); // 检查是否是自定义函数调用(包括带括号的实参,如 f((n + 1));
const customCall = this.parseCustomFunctionCall(line); const customCall = this.parseCustomFunctionCall(line);
if (customCall) { if (customCall) {
@@ -848,8 +870,8 @@ export class CppCodeExecutor {
statements.push(whileLoop); statements.push(whileLoop);
i = whileLoop.endIndex; i = whileLoop.endIndex;
} else { } else {
// 检查是否是break或continue或return语句 // 检查是否是break或continue语句
if (line === 'break;' || line === 'continue;' || line.startsWith('return')) { if (line === 'break;' || line === 'continue;') {
statements.push({ statements.push({
type: 'control', type: 'control',
subtype: line === 'break;' ? 'break' : 'continue', subtype: line === 'break;' ? 'break' : 'continue',
@@ -1327,10 +1349,17 @@ export class CppCodeExecutor {
} }
async executeMain() { async executeMain() {
try {
for (const statement of this.statements) { for (const statement of this.statements) {
if (this.shouldStop()) break; if (this.shouldStop()) break;
await this.executeStatement(statement); await this.executeStatement(statement);
} }
} catch (signal) {
if (!(signal instanceof CppReturnSignal)) throw signal;
return signal.value;
}
// main 正常执行到末尾,相当于 return 0。
return 0;
} }
async executeStatement(statement) { async executeStatement(statement) {
@@ -1361,8 +1390,11 @@ export class CppCodeExecutor {
await this.executeFunctionCall(statement); await this.executeFunctionCall(statement);
break; break;
case 'control': case 'control':
if (statement.content.startsWith('return')) { if (statement.subtype === 'return') {
throw new Error('RETURN_INTERRUPT'); const match = statement.content.match(/^return\b([\s\S]*?);(?:\s*\/\/.*)?$/);
if (!match) throw new Error(`无法解析return语句: ${statement.content}`);
const expression = match[1].trim();
throw new CppReturnSignal(expression ? this.evaluateExpression(expression) : undefined);
} }
// break和continue在各自的循环中处理这里只记录 // break和continue在各自的循环中处理这里只记录
debugLog(`🔄 控制语句: ${statement.subtype}`); debugLog(`🔄 控制语句: ${statement.subtype}`);
@@ -1887,7 +1919,8 @@ export class CppCodeExecutor {
await this.executeStatement(adjustedStatement); await this.executeStatement(adjustedStatement);
} }
} catch (e) { } catch (e) {
if (e.message !== 'RETURN_INTERRUPT') throw e; if (!(e instanceof CppReturnSignal)) throw e;
return e.value;
} }
console.log(`✅ 函数${functionName}执行完成`); console.log(`✅ 函数${functionName}执行完成`);
@@ -2719,7 +2752,7 @@ export const createCppExecutor = () => {
const executor = new CppCodeExecutor(code, window.setHighlightedLine); const executor = new CppCodeExecutor(code, window.setHighlightedLine);
this.currentExecutor = executor; this.currentExecutor = executor;
try { try {
await executor.run(); return await executor.run();
} finally { } finally {
this.currentExecutor = null; this.currentExecutor = null;
} }

View File

@@ -1,12 +1,12 @@
// config.js // config.js
// const CONFIG = { // const CONFIG = {
// DataServerBaseUrl: "https://service.001code.cn", // 在这里定义服务器地址 // DataServerBaseUrl: "https://service.001code.com", // 在这里定义服务器地址
// // DataServerBaseUrl: "https://service.001code.cn", // 在这里定义服务器地址 // // DataServerBaseUrl: "https://service.001code.com", // 在这里定义服务器地址
// // DataServerBaseUrl: "https://service.001code.cn", // // DataServerBaseUrl: "https://service.001code.com",
// // DataServerBaseUrl: "https://service.001code.cn", // // DataServerBaseUrl: "https://service.001code.com",
// // DataServerBaseUrl: "https://service.001code.cn", // // DataServerBaseUrl: "https://service.001code.com",
// // DataServerBaseUrl: "https://service.001code.cn", // // DataServerBaseUrl: "https://service.001code.com",
// version : "2025010301", // version : "2025010301",
// unitycdndir : 'https://oss-rpwqy.jjwlcdn.com/001_code_unity_res_20250826', // unitycdndir : 'https://oss-rpwqy.jjwlcdn.com/001_code_unity_res_20250826',
// unitycdnbuilddir : 'https://oss-rpwqy.jjwlcdn.com/001_code_unity_res_20250826/Build', // unitycdnbuilddir : 'https://oss-rpwqy.jjwlcdn.com/001_code_unity_res_20250826/Build',
@@ -78,12 +78,12 @@
// const CONFIG = { // const CONFIG = {
// // DataServerBaseUrl: "https://service.001code.cn", // 在这里定义服务器地址 // // DataServerBaseUrl: "https://service.001code.com", // 在这里定义服务器地址
// // DataServerBaseUrl: "https://service.001code.cn", // 在这里定义服务器地址 // // DataServerBaseUrl: "https://service.001code.com", // 在这里定义服务器地址
// // DataServerBaseUrl: "https://service.001code.cn", // // DataServerBaseUrl: "https://service.001code.com",
// DataServerBaseUrl: "https://service.001code.cn", // DataServerBaseUrl: "https://service.001code.com",
// // DataServerBaseUrl: "https://service.001code.cn", // // DataServerBaseUrl: "https://service.001code.com",
// // DataServerBaseUrl: "https://service.001code.cn", // // DataServerBaseUrl: "https://service.001code.com",
// version : "2025011001", // version : "2025011001",
// unitycdndir : 'https://oss-rpwqy.jjwlcdn.com/001_code_unity_res_20250826', // unitycdndir : 'https://oss-rpwqy.jjwlcdn.com/001_code_unity_res_20250826',
// unitycdnbuilddir : 'https://oss-rpwqy.jjwlcdn.com/001_code_unity_res_20250826/Build', // unitycdnbuilddir : 'https://oss-rpwqy.jjwlcdn.com/001_code_unity_res_20250826/Build',
@@ -157,12 +157,12 @@
const CONFIG = { const CONFIG = {
// DataServerBaseUrl: "https://service.001code.cn", // 在这里定义服务器地址 // DataServerBaseUrl: "https://service.001code.com", // 在这里定义服务器地址
// DataServerBaseUrl: "https://service.001code.cn", // 在这里定义服务器地址 // DataServerBaseUrl: "https://service.001code.com", // 在这里定义服务器地址
// DataServerBaseUrl: "https://service.001code.cn", // DataServerBaseUrl: "https://service.001code.com",
DataServerBaseUrl: "https://service.001code.cn", DataServerBaseUrl: "https://service.001code.com",
// DataServerBaseUrl: "https://service.001code.cn", // DataServerBaseUrl: "https://service.001code.com",
// DataServerBaseUrl: "https://service.001code.cn", // DataServerBaseUrl: "https://service.001code.com",
version: "2026071701", version: "2026071701",
/** OSS 真实地址(生产环境浏览器直连) */ /** OSS 真实地址(生产环境浏览器直连) */