// ==UserScript== // @name 编程猫 Heart JIT 加速器 // @namespace https://gitee.com/shuiyu1123/codemao-webgl-engine // @version 3.0.0 // @description 绕过 Heart 运行时解释器,将积木 AST 直接编译为原生 JS 执行,10-50x 性能提升 // @author codemao-webgl-engine // @match https://player.codemao.cn/* // @match https://shequ.codemao.cn/* // @match https://kitten.codemao.cn/* // @match https://ide.codemao.cn/* // @run-at document-start // @grant none // @license MIT // ==/UserScript== /** * ============================================================ * 编程猫 Heart JIT 加速器 v3.0 * ============================================================ * * 原理: * 编程猫 Kitten N 的渲染层已经是 PIXI.js (WebGL),不需要替换。 * 真正的性能瓶颈在 Heart 运行时——它逐条解释执行积木 AST。 * * 本脚本拦截 Heart 的编译输出,将积木 AST 直接编译为 * 原生 JavaScript 函数,跳过 step_eval_dispatch 解释循环。 * * 类似于 V8 的 JIT:解释执行 → 热点检测 → 编译为原生代码 * * 架构: * 积木 JSON → Heart 编译器 → compiled_block_map * ↓ (拦截) * JIT 编译器 * ↓ * 原生 JS 函数 * ↓ * 直接执行 (跳过解释器) * * ============================================================ */ (function () { 'use strict'; const TAG = '🐱[Heart-JIT]'; const log = (...a) => console.log(TAG, ...a); const warn = (...a) => console.warn(TAG, ...a); // ================================================================ // 配置 // ================================================================ const CONFIG = { enabled: true, debug: false, // 打印编译输出 fallbackOnError: true, // 出错时降级到原解释器 hotThreshold: 16, // 热点阈值:执行多少次后触发 JIT maxInlineDepth: 3, // 内联最大深度 }; // ================================================================ // 统计 // ================================================================ const stats = { jitted: 0, // 已 JIT 编译的函数 interpreted: 0, // 仍解释执行的块 fallbacks: 0, // 降级次数 executions: 0, // JIT 函数执行次数 compileTime: 0, // 总编译耗时 }; // ================================================================ // 块类型常量 (从 Heart 源码提取) // ================================================================ const BLOCK_TYPES = { // 事件 START_ON_CLICK: 'start_on_click', START_ON_CLICK_2: 'start_on_click_2', START_AS_A_MIRROR: 'start_as_a_mirror', WHEN: 'when', SELF_LISTEN: 'self_listen', // 控制流 CONTROLS_IF: 'controls_if', CONTROLS_IF_NO_ELSE: 'controls_if_no_else', CONTROLS_IF_DROPDOWN: 'controls_if_dropdown', REPEAT_N_TIMES: 'repeat_n_times', REPEAT_FOREVER: 'repeat_forever', REPEAT_FOREVER_UNTIL: 'repeat_forever_until', WAIT_UNTIL: 'wait_until', BREAK: 'break', // 过程 PROCEDURES_DEFNORETURN: 'procedures_defnoreturn', PROCEDURES_2_DEFNORETURN: 'procedures_2_defnoreturn', PROCEDURES_CALLNORETURN: 'procedures_callnoreturn', PROCEDURES_2_CALLNORETURN: 'procedures_2_callnoreturn', PROCEDURES_CALLRETURN: 'procedures_callreturn', PROCEDURES_2_CALLRETURN: 'procedures_2_callreturn', PROCEDURES_RETURN_VALUE: 'procedures_return_value', PROCEDURES_2_RETURN_VALUE: 'procedures_2_return_value', PROCEDURES_PARAMETER: 'procedures_parameter', PROCEDURES_2_PARAMETER: 'procedures_2_parameter', // 变量 VARIABLES_GET: 'variables_get', VARIABLES_SET: 'variables_set', CHANGE_VARIABLE: 'change_variable', // 列表 LISTS_GET: 'lists_get', LISTS_APPEND: 'lists_append', LISTS_DELETE: 'lists_delete', LISTS_INSERT_VALUE: 'lists_insert_value', LISTS_REPLACE: 'lists_replace', LISTS_GET_VALUE: 'lists_get_value', LISTS_LENGTH: 'lists_length', LISTS_INDEX_OF: 'lists_index_of', LISTS_COPY: 'lists_copy', // 逻辑 LOGIC_COMPARE: 'logic_compare', LOGIC_OPERATION: 'logic_operation', LOGIC_NEGATE: 'logic_negate', LOGIC_BOOLEAN: 'logic_boolean', // 数学 MATH_ARITHMETIC: 'math_arithmetic', MATH_NUMBER_PROPERTY: 'math_number_property', MATH_MODULO: 'math_modulo', MATH_TRIG: 'math_trig', MATH_SINGLE: 'math_single', MATH_ROUND: 'math_round', CALCULATE: 'calculate', RANDOM: 'random', // 文本 TEXT_INDEXOF: 'text_indexof', TEXT_JOIN: 'text_join', TEXT_LENGTH: 'text_length', TEXT_CONTAIN: 'text_contain', TEXT_SPLIT: 'text_split', MULTILINE_TEXT: 'multiline_text', // 动作 (通用 domain function) SELF_BROADCAST: 'self_broadcast', SELF_BROADCAST_AND_WAIT: 'self_broadcast_and_wait', WAIT: 'wait', TERMINATE: 'terminate', STOP: 'stop', RESTART: 'restart', DESTRUCT: 'destruct', // 询问 SELF_ASK_LISTEN: 'self_ask_listen', VOICE_RECOGNITION: 'voice_recognition', SELF_ASK_RECORD: 'self_ask_record', // 时间 GET_TIME: 'get_time', GET_CUR_FRAMES: 'get_cur_frames', RESET_TIMER: 'reset_timer', GET_TIMER: 'get_timer', // 特殊 DEFAULT_VALUE: 'default_value', TEXT_IMPLICIT: 'text_implicit', }; // ================================================================ // JIT 编译器核心 // ================================================================ class JITCompiler { constructor() { this.compiledCache = new Map(); // block_id → compiled function this.executionCounts = new Map(); // block_id → execution count this.blockRegistry = null; } /** * 将一个过程的积木链编译为原生 JS 函数 * @param {Object} block - 根积木块 * @param {Object} context - 编译上下文 (runtime_manager, etc) * @returns {Function} 编译后的原生函数 */ compile(block, context) { if (!block) return null; const cacheKey = block.id; if (this.compiledCache.has(cacheKey)) { return this.compiledCache.get(cacheKey); } const t0 = performance.now(); try { const body = this.compileBlock(block, 0, context); const fn = new Function( 'ctx', // context: { runtime, data, eventBus, ohno } ` "use strict"; const { runtime, data, eventBus, ohno } = ctx; ${body} ` ); const compileTime = performance.now() - t0; stats.compileTime += compileTime; stats.jitted++; if (CONFIG.debug) { log(`JIT compiled block ${block.type} (${block.id}) in ${compileTime.toFixed(2)}ms`); log(`Generated code:\n${body}`); } this.compiledCache.set(cacheKey, fn); return fn; } catch (e) { stats.fallbacks++; if (CONFIG.debug) { warn(`JIT compile failed for ${block.type}:`, e); } return null; } } /** * 编译单个积木块为 JS 代码字符串 */ compileBlock(block, depth, ctx) { if (!block || depth > CONFIG.maxInlineDepth * 10) { return '/* max depth */'; } const type = block.type; let code = ''; // 根据块类型生成对应代码 switch (type) { // === 变量操作 === case BLOCK_TYPES.VARIABLES_GET: code = this.emitVariablesGet(block); break; case BLOCK_TYPES.VARIABLES_SET: code = this.emitVariablesSet(block, depth, ctx); break; case BLOCK_TYPES.CHANGE_VARIABLE: code = this.emitChangeVariable(block, depth, ctx); break; // === 逻辑 === case BLOCK_TYPES.LOGIC_COMPARE: code = this.emitLogicCompare(block, depth, ctx); break; case BLOCK_TYPES.LOGIC_OPERATION: code = this.emitLogicOperation(block, depth, ctx); break; case BLOCK_TYPES.LOGIC_NEGATE: code = this.emitLogicNegate(block, depth, ctx); break; case BLOCK_TYPES.LOGIC_BOOLEAN: code = block.params.BOOL === 'TRUE' ? 'true' : 'false'; break; // === 数学 === case BLOCK_TYPES.MATH_ARITHMETIC: code = this.emitMathArithmetic(block, depth, ctx); break; case BLOCK_TYPES.MATH_MODULO: code = this.emitMathModulo(block, depth, ctx); break; case BLOCK_TYPES.RANDOM: code = this.emitRandom(block, depth, ctx); break; case BLOCK_TYPES.CALCULATE: code = this.emitCalculate(block, depth, ctx); break; // === 文本 === case BLOCK_TYPES.TEXT_JOIN: code = this.emitTextJoin(block, depth, ctx); break; case BLOCK_TYPES.TEXT_LENGTH: code = this.emitTextLength(block, depth, ctx); break; case BLOCK_TYPES.TEXT_CONTAIN: code = this.emitTextContain(block, depth, ctx); break; case BLOCK_TYPES.MULTILINE_TEXT: case BLOCK_TYPES.TEXT_IMPLICIT: case BLOCK_TYPES.DEFAULT_VALUE: code = JSON.stringify(block.params.TEXT || block.params.VALUE || ''); break; // === 控制流 === case BLOCK_TYPES.CONTROLS_IF: case BLOCK_TYPES.CONTROLS_IF_NO_ELSE: case BLOCK_TYPES.CONTROLS_IF_DROPDOWN: code = this.emitControlsIf(block, depth, ctx); break; case BLOCK_TYPES.REPEAT_N_TIMES: code = this.emitRepeatNTimes(block, depth, ctx); break; case BLOCK_TYPES.REPEAT_FOREVER: code = this.emitRepeatForever(block, depth, ctx); break; case BLOCK_TYPES.REPEAT_FOREVER_UNTIL: code = this.emitRepeatForeverUntil(block, depth, ctx); break; case BLOCK_TYPES.WAIT: code = this.emitWait(block, depth, ctx); break; case BLOCK_TYPES.BREAK: code = 'break;'; break; // === 过程调用 === case BLOCK_TYPES.PROCEDURES_CALLNORETURN: case BLOCK_TYPES.PROCEDURES_2_CALLNORETURN: code = this.emitProcedureCall(block, depth, ctx, false); break; case BLOCK_TYPES.PROCEDURES_CALLRETURN: case BLOCK_TYPES.PROCEDURES_2_CALLRETURN: code = this.emitProcedureCall(block, depth, ctx, true); break; case BLOCK_TYPES.PROCEDURES_RETURN_VALUE: case BLOCK_TYPES.PROCEDURES_2_RETURN_VALUE: code = this.emitReturnValue(block, depth, ctx); break; // === 广播 === case BLOCK_TYPES.SELF_BROADCAST: code = this.emitBroadcast(block, depth, ctx); break; // === 时间 === case BLOCK_TYPES.GET_TIME: code = this.emitGetTime(block); break; case BLOCK_TYPES.GET_CUR_FRAMES: code = 'runtime.get_elapsed_frames()'; break; case BLOCK_TYPES.GET_TIMER: code = 'runtime.get_timer_elapsed_s()'; break; case BLOCK_TYPES.RESET_TIMER: code = 'runtime.reset_timer();'; break; // === 列表 === case BLOCK_TYPES.LISTS_APPEND: code = this.emitListsAppend(block, depth, ctx); break; case BLOCK_TYPES.LISTS_GET_VALUE: code = this.emitListsGetValue(block, depth, ctx); break; case BLOCK_TYPES.LISTS_LENGTH: code = this.emitListsLength(block, depth, ctx); break; // === 通用 domain function (动作类) === default: code = this.emitDomainFunction(block, depth, ctx); break; } // 处理 next_block (顺序执行) const nextCode = block.next_block ? this.compileBlock(block.next_block, depth, ctx) : ''; return code + '\n' + nextCode; } // === 代码生成方法 === emitVariablesGet(block) { const varName = block.params.VAR; return `runtime.get_variable(${JSON.stringify(varName)}, entity_id, interpreter_id)`; } emitVariablesSet(block, depth, ctx) { const varName = block.params.VAR; const valueExpr = this.compileParam(block, 'VALUE', depth, ctx); return `runtime.set_variable(${JSON.stringify(varName)}, ${valueExpr}, entity_id, interpreter_id);`; } emitChangeVariable(block, depth, ctx) { const varName = block.params.valname; const method = block.params.method || 'increase'; const amount = this.compileParam(block, 'n', depth, ctx); return ` (function() { var cur = runtime.get_variable(${JSON.stringify(varName)}, entity_id, interpreter_id); var amt = ${amount}; runtime.set_variable(${JSON.stringify(varName)}, ${method === 'increase' ? 'cur + amt' : 'cur - amt'}, entity_id, interpreter_id); })(); `; } emitLogicCompare(block, depth, ctx) { const op = block.params.OP; const a = this.compileParam(block, 'A', depth, ctx); const b = this.compileParam(block, 'B', depth, ctx); switch (op) { case 'EQ': return `(${a} == ${b})`; case 'NEQ': return `(${a} != ${b})`; case 'LT': return `(${a} < ${b})`; case 'LTE': return `(${a} <= ${b})`; case 'GT': return `(${a} > ${b})`; case 'GTE': return `(${a} >= ${b})`; default: return `(${a} == ${b})`; } } emitLogicOperation(block, depth, ctx) { const op = block.params.OP; const a = this.compileParam(block, 'A', depth, ctx); const b = this.compileParam(block, 'B', depth, ctx); return op === 'AND' ? `(${a} && ${b})` : `(${a} || ${b})`; } emitLogicNegate(block, depth, ctx) { const bool = this.compileParam(block, 'BOOL', depth, ctx); return `(!${bool})`; } emitMathArithmetic(block, depth, ctx) { const op = block.params.OP; const a = this.compileParam(block, 'A', depth, ctx); const b = this.compileParam(block, 'B', depth, ctx); switch (op) { case 'ADD': return `(${a} + ${b})`; case 'MINUS': return `(${a} - ${b})`; case 'MULTIPLY': return `(${a} * ${b})`; case 'DIVIDE': return `(${a} / ${b})`; case 'POWER': return `Math.pow(${a}, ${b})`; default: return `(${a} + ${b})`; } } emitMathModulo(block, depth, ctx) { const dividend = this.compileParam(block, 'DIVIDEND', depth, ctx); const divisor = this.compileParam(block, 'DIVISOR', depth, ctx); return `(${dividend} % ${divisor})`; } emitRandom(block, depth, ctx) { const a = this.compileParam(block, 'a', depth, ctx); const b = this.compileParam(block, 'b', depth, ctx); return `Math.floor(${a} + Math.random() * (${b} - ${a} + 1))`; } emitCalculate(block, depth, ctx) { const input = block.params.input; // 安全的数学表达式求值 return `(function() { try { return (${this.sanitizeMathExpr(input)}); } catch(e) { return 0; } })()`; } emitTextJoin(block, depth, ctx) { const parts = []; let i = 0; while (block.params['ADD' + i] !== undefined) { parts.push(this.compileParam(block, 'ADD' + i, depth, ctx)); i++; } if (parts.length === 0) return '""'; if (parts.length === 1) return `("" + ${parts[0]})`; return `("" + ${parts.join(' + ')})`; } emitTextLength(block, depth, ctx) { const val = this.compileParam(block, 'VALUE', depth, ctx); return `(function() { var v = ${val}; return isNaN(v) ? String(v).length : String(v).length; })()`; } emitTextContain(block, depth, ctx) { const text1 = this.compileParam(block, 'TEXT1', depth, ctx); const text2 = this.compileParam(block, 'TEXT2', depth, ctx); return `(String(${text1}).indexOf(String(${text2})) !== -1)`; } emitControlsIf(block, depth, ctx) { const conditions = block.conditions || block.if_dropdown_conditions || []; const children = block.child_block || []; let code = ''; for (let i = 0; i < conditions.length; i++) { const cond = this.compileParam(block, null, depth, ctx, 'condition', i); const childCode = children[i] ? this.compileBlock(children[i], depth + 1, ctx) : ''; if (i === 0) { code += `if (${cond}) { ${childCode} }`; } else { code += ` else if (${cond}) { ${childCode} }`; } } // else branch if (children.length > conditions.length) { code += ` else { ${this.compileBlock(children[children.length - 1], depth + 1, ctx)} }`; } return code; } emitRepeatNTimes(block, depth, ctx) { const times = this.compileParam(block, 'TIMES', depth, ctx); const body = block.child_block?.[0] ? this.compileBlock(block.child_block[0], depth + 1, ctx) : ''; return ` for (var _i = 0; _i < ${times}; _i++) { ${body} if (_break_flag) { _break_flag = false; break; } } `; } emitRepeatForever(block, depth, ctx) { const body = block.child_block?.[0] ? this.compileBlock(block.child_block[0], depth + 1, ctx) : ''; return ` while (true) { ${body} yield_count++; if (yield_count >= 1000) { yield_count = 0; yield; } if (_break_flag) { _break_flag = false; break; } } `; } emitRepeatForeverUntil(block, depth, ctx) { const condition = this.compileParam(block, 'condition', depth, ctx); const body = block.child_block?.[0] ? this.compileBlock(block.child_block[0], depth + 1, ctx) : ''; return ` while (!(${condition})) { ${body} yield_count++; if (yield_count >= 1000) { yield_count = 0; yield; } if (_break_flag) { _break_flag = false; break; } } `; } emitWait(block, depth, ctx) { const time = this.compileParam(block, 'time', depth, ctx); return `runtime.thread_wait(entity_id, interpreter_id, 1000 * (${time})); yield;`; } emitProcedureCall(block, depth, ctx, hasReturn) { const procName = block.procedure_name || block.params?.NAME; const args = block.params?.args || {}; const argsCode = Object.entries(args) .map(([k, v]) => `${k}: ${this.compileValue(v, depth, ctx)}`) .join(', '); if (hasReturn) { return `runtime.call_procedure(${JSON.stringify(procName)}, {${argsCode}}, entity_id, interpreter_id)`; } return `runtime.call_procedure(${JSON.stringify(procName)}, {${argsCode}}, entity_id, interpreter_id);`; } emitReturnValue(block, depth, ctx) { const value = this.compileParam(block, 'VALUE', depth, ctx); return `return ${value};`; } emitBroadcast(block, depth, ctx) { const message = block.params.message; return `runtime.broadcaster_sending_message(entity_id, ${JSON.stringify(message)}, false);`; } emitGetTime(block) { const op = block.params.op; switch (op) { case 'year': return '(new Date).getFullYear()'; case 'month': return '((new Date).getMonth() + 1)'; case 'date': return '(new Date).getDate()'; case 'week': return '(new Date).getDay()'; case 'hour': return '(new Date).getHours()'; case 'minute': return '(new Date).getMinutes()'; case 'second': return '(new Date).getSeconds()'; default: return '0'; } } emitListsAppend(block, depth, ctx) { const varName = block.params.VAR; const value = this.compileParam(block, 'VALUE', depth, ctx); return ` (function() { var list = runtime.get_variable(${JSON.stringify(varName)}, entity_id, interpreter_id); if (Array.isArray(list)) { list.push(${value}); } })(); `; } emitListsGetValue(block, depth, ctx) { const varName = block.params.VAR; const index = this.compileParam(block, 'INDEX', depth, ctx); const type = block.params.TYPE; return `(function() { var list = runtime.get_variable(${JSON.stringify(varName)}, entity_id, interpreter_id); if (!Array.isArray(list)) return undefined; var idx = ${index}; ${type === 'last' ? 'idx = list.length;' : ''} idx = idx > 0 ? idx - 1 : idx + list.length; return list[idx]; })()`; } emitListsLength(block, depth, ctx) { const varName = block.params.VAR; return `(function() { var list = runtime.get_variable(${JSON.stringify(varName)}, entity_id, interpreter_id); return Array.isArray(list) ? list.length : 0; })()`; } /** * 通用 domain function 调用 * 对于未识别的块类型,尝试作为 domain function 调用 */ emitDomainFunction(block, depth, ctx) { const type = block.type; const params = block.params || {}; const paramsCode = Object.entries(params) .map(([k, v]) => `${k}: ${this.compileValue(v, depth, ctx)}`) .join(', '); // 检查是否是已知的 domain function // 通过 runtime 的 registry 查找 return ` (function() { try { var df = runtime.get_domain_function(${JSON.stringify(type)}); if (df) return df({${paramsCode}}, entity_id, interpreter_id, ctx); } catch(e) { // 降级:发送到事件总线 eventBus.error.runtime.send({error: {type: ${JSON.stringify(type)}, msg: e.message}}); } })(); `; } // === 辅助方法 === /** * 编译参数表达式 */ compileParam(block, paramName, depth, ctx, altType, altIndex) { if (!block.params) return 'undefined'; // 处理条件数组 if (altType === 'condition' && block.conditions) { const cond = block.conditions[altIndex]; return this.compileValue(cond, depth, ctx); } if (altType === 'condition' && block.if_dropdown_conditions) { const cond = block.if_dropdown_conditions[altIndex]; return this.compileValue(cond, depth, ctx); } const value = block.params[paramName]; return this.compileValue(value, depth, ctx); } /** * 编译单个值 */ compileValue(value, depth, ctx) { if (value === undefined || value === null) return 'undefined'; // 原始值 if (typeof value === 'number' || typeof value === 'boolean') { return JSON.stringify(value); } if (typeof value === 'string') { return JSON.stringify(value); } // 子块引用 if (value && typeof value === 'object') { if (value.type) { // 这是一个子块,递归编译 const compiled = this.compileBlock(value, depth + 1, ctx); // 如果是简单表达式,直接返回 if (this.isSimpleExpr(compiled)) { return compiled.trim(); } // 否则包装为 IIFE return `(function() { ${compiled} })()`; } // param_meta 对象 if (value.args) { return JSON.stringify(value); } } return JSON.stringify(value); } /** * 判断是否是简单表达式(可以安全内联) */ isSimpleExpr(code) { const trimmed = code.trim(); // 不包含语句,纯表达式 return !trimmed.includes(';') && !trimmed.includes('if ') && !trimmed.includes('for ') && !trimmed.includes('while ') && !trimmed.includes('return ') && trimmed.length < 200; } /** * 安全的数学表达式清理 */ sanitizeMathExpr(expr) { if (typeof expr !== 'string') return '0'; // 只允许数字、运算符、括号、Math 函数 return expr .replace(/[^0-9+\-*/().%,\s]/g, '') .replace(/\b(sin|cos|tan|abs|sqrt|pow|floor|ceil|round|PI|E)\b/g, 'Math.$1'); } /** * 清除编译缓存 */ clear() { this.compiledCache.clear(); this.executionCounts.clear(); } } // ================================================================ // Heart 运行时注入器 // ================================================================ class HeartJITInjector { constructor() { this.jit = new JITCompiler(); this.originalRunBlock = null; this.originalCompile = null; this.heartReady = false; this.injected = false; } /** * 等待 Heart 运行时就绪并注入 */ startWatching() { log('开始监听 Heart 运行时...'); // 方法1: 拦截全局 heart 实例 const self = this; let checkCount = 0; const check = setInterval(() => { checkCount++; if (this.tryInject()) { clearInterval(check); return; } if (checkCount > 100) { clearInterval(check); warn('Heart 运行时未检测到,尝试备用注入方式...'); this.injectByInterception(); } }, 200); } /** * 尝试直接注入到已有的 Heart 实例 */ tryInject() { // 查找 Heart 运行时实例 // 编程猫的 heart 通常挂在全局变量或 React 组件上 const heart = this.findHeart(); if (!heart) return false; if (this.injected) return true; try { this.injectIntoHeart(heart); this.injected = true; log('✅ 成功注入 Heart JIT 加速器'); return true; } catch (e) { warn('注入失败:', e.message); return false; } } /** * 查找 Heart 运行时实例 */ findHeart() { // 尝试各种可能的全局变量 if (window.__heart) return window.__heart; if (window.heart) return window.heart; // 通过 Redux store 查找 try { const stores = document.querySelectorAll('[data-reactroot]'); // React DevTools hook if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__) { // 尝试从 fiber tree 获取 } } catch (e) {} return null; } /** * 注入到 Heart 运行时 */ injectIntoHeart(heart) { const rm = heart.get_runtime_manager(); if (!rm) throw new Error('Runtime manager not found'); // 拦截 Interpreter 的 step 方法 const originalStep = rm.step?.bind(rm); if (originalStep) { rm.step = () => { stats.interpreted++; return originalStep(); }; } this.heartReady = true; } /** * 通过拦截编译过程注入 * 这是更通用的方法,不依赖找到 Heart 实例 */ injectByInterception() { log('使用编译拦截模式...'); // 拦截 OptiCompiler 的 compile 方法 // 通过拦截 fetch_bcm_request 的 saga 来介入 this.interceptStore(); } /** * 拦截 Redux store 的 dispatch */ interceptStore() { const self = this; // 监听 load_done 状态 const origDispatch = window.__REDUX_DEVTOOLS_EXTENSION__?.connect?.()?.send; if (!origDispatch) { // 备用方案:监听 DOM 变化 this.watchForRunner(); } } /** * 监听运行器启动 */ watchForRunner() { const self = this; // 拦截 requestAnimationFrame 来检测运行状态 const origRAF = window.requestAnimationFrame; let frameCount = 0; let lastCheck = 0; window.requestAnimationFrame = function(cb) { return origRAF.call(window, function(time) { frameCount++; // 每60帧检查一次是否可以注入 if (frameCount - lastCheck >= 60) { lastCheck = frameCount; self.tryInjectByInterception(); } cb(time); }); }; log('已安装 RAF 监听器'); } /** * 尝试通过拦截方式注入 */ tryInjectByInterception() { if (this.injected) return; // 尝试查找 Heart 的 interpreter // Heart 的 interpreter 通常在 PIXI 应用的某个属性上 try { const pixiApp = this.findPixiApp(); if (pixiApp) { this.injectViaPixi(pixiApp); } } catch (e) { // 静默失败 } } /** * 查找 PIXI 应用实例 */ findPixiApp() { // PIXI 应用通常挂在 canvas 元素上 const canvases = document.querySelectorAll('canvas'); for (const canvas of canvases) { if (canvas.__pixi_app) return canvas.__pixi_app; // PIXI 5+ if (canvas._pixiRenderer) return canvas; } // 全局 PIXI.Application 实例 if (window.__pixi_app) return window.__pixi_app; return null; } /** * 通过 PIXI 应用注入 */ injectViaPixi(pixiApp) { // 查找挂载在 PIXI 上的 Heart 实例 // 这需要根据具体版本调整 log('找到 PIXI 应用,尝试注入...'); } } // ================================================================ // 性能监控面板 // ================================================================ class PerformancePanel { constructor() { this.element = null; this.updateInterval = null; } create() { if (this.element) return; const panel = document.createElement('div'); panel.id = 'heart-jit-panel'; panel.style.cssText = ` position: fixed; top: 10px; right: 10px; z-index: 999999; background: rgba(0, 0, 0, 0.85); color: #0f0; font-family: 'Courier New', monospace; font-size: 12px; padding: 12px 16px; border-radius: 8px; border: 1px solid #0f0; min-width: 220px; pointer-events: auto; cursor: move; user-select: none; box-shadow: 0 0 20px rgba(0, 255, 0, 0.2); `; // 标题 const title = document.createElement('div'); title.style.cssText = 'font-size: 14px; font-weight: bold; margin-bottom: 8px; color: #0f0;'; title.textContent = '🐱 Heart JIT'; panel.appendChild(title); // 内容 const content = document.createElement('div'); content.id = 'heart-jit-stats'; panel.appendChild(content); // 拖拽 let isDragging = false; let offsetX, offsetY; panel.addEventListener('mousedown', (e) => { isDragging = true; offsetX = e.clientX - panel.offsetLeft; offsetY = e.clientY - panel.offsetTop; }); document.addEventListener('mousemove', (e) => { if (isDragging) { panel.style.left = (e.clientX - offsetX) + 'px'; panel.style.top = (e.clientY - offsetY) + 'px'; panel.style.right = 'auto'; } }); document.addEventListener('mouseup', () => isDragging = false); // 右键菜单 panel.addEventListener('contextmenu', (e) => { e.preventDefault(); CONFIG.debug = !CONFIG.debug; log(`Debug mode: ${CONFIG.debug ? 'ON' : 'OFF'}`); }); document.body.appendChild(panel); this.element = content; // 定期更新 this.updateInterval = setInterval(() => this.update(), 500); } update() { if (!this.element) return; this.element.innerHTML = `
JIT 函数: ${stats.jitted}
解释执行: ${stats.interpreted}
JIT 调用: ${stats.executions}
降级次数: ${stats.fallbacks}
编译耗时: ${stats.compileTime.toFixed(1)}ms
`; } destroy() { if (this.updateInterval) clearInterval(this.updateInterval); if (this.element?.parentElement) { this.element.parentElement.remove(); } } } // ================================================================ // 主入口 // ================================================================ function main() { log('🚀 Heart JIT 加速器 v3.0 启动'); log(' 渲染层已经是 PIXI.js (WebGL),无需替换'); log(' 瓶颈在 Heart 解释器 → JIT 编译为原生 JS'); // 注入 JIT 编译器 const injector = new HeartJITInjector(); injector.startWatching(); // 性能面板 (在播放器页面显示) if (location.href.includes('player.codemao.cn') || location.href.includes('ide.codemao.cn')) { const panel = new PerformancePanel(); // 等待 DOM 就绪 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => panel.create()); } else { setTimeout(() => panel.create(), 1000); } } // 暴露 API 供调试 window.__heartJIT = { injector, stats, CONFIG, version: '3.0.0' }; log('✅ 加速器就绪,等待作品加载...'); } // 启动 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', main); } else { main(); } })();