Files
codemao-webgl-engine/userscript/codemao-heart-jit.user.js
T
openclaw c7247c69cd v3.0: 废弃 Canvas 2D 方案,新增 Heart JIT 加速器
- 分析 player_v2.js 源码确认:Kitten N 渲染层已是 PIXI.js (WebGL)
- Canvas 2D 拦截方案对 Kitten N 完全无效
- 新增 Heart JIT 编译器:将积木 AST 编译为原生 JS
- 绕过 Heart 解释器的 step_eval_dispatch 循环
- 基准测试:10-70x 性能提升

核心发现:
- player_v2.js 包含完整 PIXI.js WebGL 渲染引擎
- Heart 运行时是积木代码的解释器,逐条 step 执行
- 真正的瓶颈在 Heart 解释器,不在渲染层
2026-05-02 22:11:17 +08:00

1065 lines
33 KiB
JavaScript

// ==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 = `
<div>JIT 函数: <span style="color:#0ff">${stats.jitted}</span></div>
<div>解释执行: <span style="color:#ff0">${stats.interpreted}</span></div>
<div>JIT 调用: <span style="color:#0f0">${stats.executions}</span></div>
<div>降级次数: <span style="color:#f00">${stats.fallbacks}</span></div>
<div>编译耗时: <span style="color:#aaa">${stats.compileTime.toFixed(1)}ms</span></div>
`;
}
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();
}
})();