chore: 项目已迁移至 Gitee/GitHub,仅保留迁移说明 (tombstone)
This commit is contained in:
1 parent
bcf4122991
commit
e83315b378
7 files changed
+1
-2439
No files matched your search
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026 shuiyu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
你好呀~这个仓库已经搬到更好用的地方啦。
|
||||
|
||||
为了让代码管理更清爽、多平台同步更省心,我们把原先分散的镜像统一收拢到
|
||||
**Gitee** 与 **GitHub** 主仓维护,这里就只保留这张"迁移小纸条"咯。
|
||||
为了让代码管理更清爽、多平台同步更省心,我们把原先分散的镜像统一收拢到主仓维护,这里就只保留这张"迁移小纸条"咯。
|
||||
|
||||
📦 新家在这里:
|
||||
- Gitee:https://gitee.com/shuiyu1123/codemao-webgl-engine
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
// ============================================================
|
||||
// 性能对比基准测试
|
||||
// 模拟 Heart 解释器 vs JIT 编译器的执行性能
|
||||
// ============================================================
|
||||
|
||||
const ITERATIONS = 100000;
|
||||
|
||||
// 模拟 Heart 解释器的执行方式
|
||||
function heartInterpreterSimulated(blocks, runtime) {
|
||||
let result = 0;
|
||||
let pc = 0; // program counter
|
||||
const stack = [];
|
||||
|
||||
while (pc < blocks.length) {
|
||||
const block = blocks[pc];
|
||||
|
||||
// 模拟 step_eval_dispatch 的开销
|
||||
const blockType = block.type;
|
||||
const params = block.params;
|
||||
|
||||
// 分发开销:查找 block type
|
||||
switch (blockType) {
|
||||
case 'variables_get':
|
||||
// 获取变量值
|
||||
result = runtime.getVariable(params.VAR);
|
||||
break;
|
||||
case 'variables_set':
|
||||
runtime.setVariable(params.VAR, result);
|
||||
break;
|
||||
case 'math_arithmetic':
|
||||
const a = runtime.getVariable(params.A);
|
||||
const b = runtime.getVariable(params.B);
|
||||
switch (params.OP) {
|
||||
case 'ADD': result = a + b; break;
|
||||
case 'MINUS': result = a - b; break;
|
||||
case 'MULTIPLY': result = a * b; break;
|
||||
case 'DIVIDE': result = a / b; break;
|
||||
}
|
||||
break;
|
||||
case 'logic_compare':
|
||||
const la = runtime.getVariable(params.A);
|
||||
const lb = runtime.getVariable(params.B);
|
||||
switch (params.OP) {
|
||||
case 'EQ': result = la == lb; break;
|
||||
case 'LT': result = la < lb; break;
|
||||
case 'GT': result = la > lb; break;
|
||||
}
|
||||
break;
|
||||
case 'repeat_n_times':
|
||||
// 解释器需要为每次迭代管理状态
|
||||
for (let i = 0; i < params.TIMES; i++) {
|
||||
// 模拟 yield 检查
|
||||
if (i % 1000 === 0) {
|
||||
// 解释器的 yield 开销
|
||||
stack.push({ pc, frame: i });
|
||||
stack.pop();
|
||||
}
|
||||
result += i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 模拟 next_block 查找开销
|
||||
pc++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// JIT 编译后的执行方式
|
||||
function jitCompiled(runtime) {
|
||||
let result = 0;
|
||||
|
||||
// 直接执行,无 dispatch 开销
|
||||
for (let i = 0; i < ITERATIONS; i++) {
|
||||
result = runtime.getVariable('x');
|
||||
result = result + runtime.getVariable('y');
|
||||
result = result * 2;
|
||||
runtime.setVariable('x', result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 运行基准测试
|
||||
function runBenchmark() {
|
||||
const runtime = {
|
||||
vars: { x: 1, y: 2, n: 1000 },
|
||||
getVariable(name) { return this.vars[name] || 0; },
|
||||
setVariable(name, val) { this.vars[name] = val; }
|
||||
};
|
||||
|
||||
// 构造积木序列
|
||||
const blocks = [];
|
||||
for (let i = 0; i < ITERATIONS; i++) {
|
||||
blocks.push(
|
||||
{ type: 'variables_get', params: { VAR: 'x' } },
|
||||
{ type: 'math_arithmetic', params: { A: 'x', B: 'y', OP: 'ADD' } },
|
||||
{ type: 'variables_set', params: { VAR: 'x' } }
|
||||
);
|
||||
}
|
||||
|
||||
// 测试解释器
|
||||
console.time('Heart Interpreter');
|
||||
const interpResult = heartInterpreterSimulated(blocks, { ...runtime });
|
||||
console.timeEnd('Heart Interpreter');
|
||||
|
||||
// 重置 runtime
|
||||
runtime.vars.x = 1;
|
||||
|
||||
// 测试 JIT
|
||||
console.time('JIT Compiled');
|
||||
const jitResult = jitCompiled({ ...runtime });
|
||||
console.timeEnd('JIT Compiled');
|
||||
|
||||
return { interpResult, jitResult };
|
||||
}
|
||||
|
||||
// 导出
|
||||
if (typeof module !== 'undefined') {
|
||||
module.exports = { runBenchmark };
|
||||
} else {
|
||||
window.runBenchmark = runBenchmark;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
# 反编译进度
|
||||
|
||||
新仓库: https://gitee.com/shuiyu1123/codemao-heart-source (私有)
|
||||
|
||||
## 已还原的核心模块
|
||||
|
||||
| 模块 | 文件 | 状态 |
|
||||
|------|------|------|
|
||||
| RegistryImpl | `src/heart/Registry.js` | ✅ 完成 |
|
||||
| BlockPredicatesImpl | `src/heart/BlockPredicates.js` | ✅ 完成 |
|
||||
| CompilerImpl | `src/heart/Compiler.js` | ✅ 完成 |
|
||||
| InterpreterImpl | `src/heart/Interpreter.js` | ✅ 完成 (完整) |
|
||||
| RuntimeManagerImpl | `src/heart/RuntimeManager.js` | ✅ 完成 |
|
||||
|
||||
## 完整文档
|
||||
|
||||
- `docs/player-source-analysis.md` - 1.8MB bundle 完整架构分析
|
||||
- `docs/module-map.md` - webpack 模块映射表
|
||||
- `docs/domain-functions.md` - 80+ 积木实现清单
|
||||
- `TASKS.md` - 反编译任务清单
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 还原 RuntimeDataImpl, BlockPoolImpl, EventBus
|
||||
2. 还原 Domain Functions (积木实际实现)
|
||||
3. 还原渲染层 (PIXI.js 集成)
|
||||
4. JIT 编译器与还原的 Interpreter 对接
|
||||
@@ -1,442 +0,0 @@
|
||||
# 编程猫 Player v2.js 源码架构分析
|
||||
|
||||
> 从 `https://kn-cdn.codemao.cn/kn/build/player_v2.2d180c468559fbd29528.js` 提取
|
||||
> 文件大小: 1.8MB, webpack 打包, 125 个模块
|
||||
|
||||
## 1. 整体架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ React UI 层 │
|
||||
│ Theatre (舞台), Ranking (排行榜), Scanner (扫码), │
|
||||
│ FaceDetect, VirtualKeyboard, Dialog │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Redux + Saga 状态管理 │
|
||||
│ theatre_states, run_states, cloud_states, │
|
||||
│ variable_states, userinfo_state, ai_states │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ Heart 运行时引擎 │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │Compiler │ │Interpreter│ │Registry │ │RuntimeMgr│ │
|
||||
│ │(编译积木) │ │(执行积木) │ │(注册积木) │ │(管理实体) │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ PIXI.js 渲染层 (WebGL) │
|
||||
│ Application, Container, Sprite, Graphics, │
|
||||
│ Text, Filter, Ticker, InteractionManager │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 底层服务 │
|
||||
│ Audio (Howler.js), Physics, MIDI, Cloud Variables, │
|
||||
│ Face Detection, QR Scanner, WebSocket │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 2. Heart 运行时核心类
|
||||
|
||||
### 2.1 `Heart` (入口)
|
||||
```javascript
|
||||
// e.Heart = _
|
||||
// 创建: new_heart(config)
|
||||
// 方法:
|
||||
// get_runtime_manager() → RuntimeManager
|
||||
// get_runtime_data() → RuntimeData
|
||||
// get_registry() → Registry
|
||||
// get_compiler() → Compiler
|
||||
// get_event_bus() → EventBus
|
||||
// get_util() → BlockUtil
|
||||
// lint(entities, linters) → { errors }
|
||||
```
|
||||
|
||||
### 2.2 `RegistryImpl` (积木注册表)
|
||||
```javascript
|
||||
class RegistryImpl {
|
||||
// 存储
|
||||
domain_function = {} // 积木实现函数
|
||||
domain_function_index = {} // 函数索引
|
||||
respond = {} // 响应器
|
||||
lifetime_respond = {} // 生命周期响应器
|
||||
action_types = {} // 动作类型
|
||||
meta_restart_when_finished = {}
|
||||
meta_finish_out_of_run_group = {}
|
||||
|
||||
// 方法
|
||||
register({ namespace, id, domain_function, respond, metadata })
|
||||
register_action_type({ namespace, id, statefulness })
|
||||
register_linter(linter)
|
||||
has_responder_type(type) → boolean
|
||||
has_lifetime_responder_type(type) → boolean
|
||||
get_action_type(namespaced_id) → ActionSpec
|
||||
get_domain_functions() → { [name]: Function }
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 `CompilerImpl` / `OptiCompilerImpl` (编译器)
|
||||
```javascript
|
||||
class CompilerImpl {
|
||||
// 将 workspace_json 编译为可执行的积木树
|
||||
compile(entity, procedures, is_clone) → compiled_block_map
|
||||
compile_from_workspace_json(blocksJSON, entity_id, is_clone) → {
|
||||
procedures: { [name]: block },
|
||||
compiled_block_map: { [id]: block }
|
||||
}
|
||||
}
|
||||
|
||||
class OptiCompilerImpl {
|
||||
// 优化编译器 - 生成更高效的 AST
|
||||
compile(entity, procedures, is_clone) → compiled_program
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 `InterpreterImpl` (解释器) ⭐ 性能瓶颈
|
||||
```javascript
|
||||
class InterpreterImpl {
|
||||
// 核心状态
|
||||
root_block // 根积木
|
||||
current_block // 当前执行的积木
|
||||
current_frame // 当前栈帧
|
||||
program_state // 程序状态
|
||||
n_warp_iterations // warp 迭代计数
|
||||
n_procedure_calls // 过程调用计数
|
||||
|
||||
// 核心方法
|
||||
step() → StepResult // 执行一步 ← 这是最热的路径
|
||||
do_step() → StepResult // 内部执行
|
||||
|
||||
// do_step 的执行流程:
|
||||
// 1. get_stack_frame() - 获取当前栈帧
|
||||
// 2. step_eval_dispatch() - 根据块类型分发
|
||||
// 3. 处理各种结果类型:
|
||||
// - Yield → 返回 yielding
|
||||
// - Finish → 返回 finished
|
||||
// - EvalArg → 求值参数
|
||||
// - EvalChild → 求值子块
|
||||
// - EvalTell → 求值 tell 块
|
||||
// - EvalWarp → 求值 warp 块
|
||||
// - ProcedureCall → 调用过程
|
||||
// - ProcedureReturn → 过程返回
|
||||
// 4. go_next_or_parent() - 导航到下一个块或父块
|
||||
// 5. must_yield() - 检查是否需要让出
|
||||
|
||||
// step_eval_dispatch 分发逻辑:
|
||||
step_eval_dispatch() {
|
||||
if (block.disabled) return skip
|
||||
if (is.responder_block(block)) return eval_child_or_done
|
||||
if (is.event_block(block)) return eval_event
|
||||
if (is.cond_block(block)) return eval_cond
|
||||
if (is.domain_block(block)) return eval_domain
|
||||
if (is.break(block)) return Break
|
||||
if (is.repeat_forever(block)) return eval_repeat_forever
|
||||
if (is.repeat_n_times(block)) return eval_repeat_n_times
|
||||
if (is.repeat_forever_until(block)) return eval_repeat_forever_until
|
||||
if (is.wait_until(block)) return eval_wait_until
|
||||
if (is.procedures_callreturn(block)) return eval_procedures_callreturn
|
||||
if (is.procedures_defnoreturn(block)) return eval_procedures_defnoreturn
|
||||
if (is.procedures_parameter(block)) return eval_procedures_parameter
|
||||
if (is.procedures_return_value(block)) return eval_procedures_return_value
|
||||
if (is.procedures_callnoreturn(block)) return eval_procedures_callnoreturn
|
||||
if (is.sync_tell(block)) return eval_sync_tell
|
||||
if (is.async_tell(block)) return eval_async_tell
|
||||
if (is.warp(block)) return eval_warp
|
||||
throw unknown_block_type
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 `RuntimeManagerImpl` (运行时管理器)
|
||||
```javascript
|
||||
class RuntimeManagerImpl {
|
||||
// 实体管理
|
||||
create_entity_instance(entity_id, typeclass_id)
|
||||
create_singleton_entity_instances()
|
||||
destruct_entity(entity_id)
|
||||
get_entity_state(entity_id) → EntityState
|
||||
|
||||
// 解释器管理
|
||||
spawn_interpreter(config)
|
||||
spawn_async_tell_interpreter(config)
|
||||
remove_interpreter(interpreter_id)
|
||||
add_interpreter(interpreter)
|
||||
get_interpreter_ids() → string[]
|
||||
get_n_interpreters() → number
|
||||
|
||||
// 执行
|
||||
run() // 开始执行
|
||||
stop() // 停止
|
||||
update() // 每帧更新 ← 调用所有 interpreter.step()
|
||||
|
||||
// 变量
|
||||
get_variable(name, entity_id, interpreter_id)
|
||||
set_variable(name, value, entity_id, interpreter_id)
|
||||
set_variable_specs(specs)
|
||||
|
||||
// 线程控制
|
||||
thread_wait(entity_id, interpreter_id, ms)
|
||||
get_thread_lock(entity_id, interpreter_id) → ThreadLock
|
||||
|
||||
// 广播
|
||||
broadcaster_sending_message(entity_id, message, is_blocking)
|
||||
|
||||
// 动作
|
||||
send_action({ id, namespace, parameters, value })
|
||||
}
|
||||
```
|
||||
|
||||
### 2.6 `RuntimeDataImpl` (运行时数据)
|
||||
```javascript
|
||||
class RuntimeDataImpl {
|
||||
// 变量和列表
|
||||
clone_id_2_original_id(clone_id) → original_id
|
||||
get_arbitrary_data(key) → any
|
||||
set_arbitrary_data(key, value)
|
||||
is_running() → boolean
|
||||
|
||||
// 事件总线
|
||||
variable_update → EventBus
|
||||
list_update → EventBus
|
||||
entity_variable_update → EventBus
|
||||
entity_list_update → EventBus
|
||||
}
|
||||
```
|
||||
|
||||
### 2.7 `BlockPredicatesImpl` (积木谓词)
|
||||
```javascript
|
||||
class BlockPredicatesImpl {
|
||||
// 判断积木类型
|
||||
hat_block_type(type) → boolean
|
||||
responder_block(block) → boolean
|
||||
responder_type(type) → boolean
|
||||
lifetime_responder_type(type) → boolean
|
||||
event_block(block) → boolean
|
||||
domain_block(block) → boolean
|
||||
loop_block(block) → boolean
|
||||
cond_block(block) → boolean
|
||||
proc_block(block) → boolean
|
||||
special_block(block) → boolean
|
||||
|
||||
// 具体积木判断
|
||||
controls_if(block) → boolean
|
||||
repeat_n_times(block) → boolean
|
||||
repeat_forever(block) → boolean
|
||||
repeat_forever_until(block) → boolean
|
||||
wait_until(block) → boolean
|
||||
break(block) → boolean
|
||||
procedures_defnoreturn(block) → boolean
|
||||
procedures_callreturn(block) → boolean
|
||||
async_tell(block) → boolean
|
||||
sync_tell(block) → boolean
|
||||
warp(block) → boolean
|
||||
mirror(block) → boolean
|
||||
start_on_click(block) → boolean
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Domain Functions (积木实现)
|
||||
|
||||
积木的实际功能通过 domain functions 注册到 Registry:
|
||||
|
||||
### 3.1 变量操作
|
||||
```javascript
|
||||
variables_get(t, e, n) // 获取变量
|
||||
variables_set(t, e, n) // 设置变量
|
||||
change_variable(t, e, n) // 改变变量
|
||||
```
|
||||
|
||||
### 3.2 列表操作
|
||||
```javascript
|
||||
lists_append(t, e, n) // 添加到列表
|
||||
lists_delete(t, e, n) // 删除列表项
|
||||
lists_insert_value(t, e, n) // 插入值
|
||||
lists_replace(t, e, n) // 替换列表项
|
||||
lists_get_value(t, e, n) // 获取列表值
|
||||
lists_length(t, e, n) // 列表长度
|
||||
lists_index_of(t, e, n) // 查找索引
|
||||
lists_copy(t, e, n) // 复制列表
|
||||
lists_is_exist(t, e, n) // 检查值是否存在
|
||||
```
|
||||
|
||||
### 3.3 运动
|
||||
```javascript
|
||||
self_move_to(t, e, n) // 移动到
|
||||
self_set_position(t, e, n) // 设置位置
|
||||
self_change_coordinate(t, e, n)// 改变坐标
|
||||
self_glide_to(t, e, n) // 滑行到
|
||||
self_glide_coordinate(t, e, n) // 滑行到坐标
|
||||
self_point_towards(t, e, n) // 面向
|
||||
self_face_to(t, e, n) // 面向角色
|
||||
self_bounce_off_edge(t, e, n) // 碰到边缘反弹
|
||||
```
|
||||
|
||||
### 3.4 外观
|
||||
```javascript
|
||||
show(t, e, n) // 显示
|
||||
hide(t, e, n) // 隐藏
|
||||
self_set_effect_2(t, e, n) // 设置特效
|
||||
self_change_effect_3(t, e, n) // 改变特效
|
||||
```
|
||||
|
||||
### 3.5 声音
|
||||
```javascript
|
||||
play_audio(t, e, n) // 播放声音
|
||||
play_audio_and_wait(t, e, n) // 播放并等待
|
||||
play_audio_2(t, e, n) // 播放声音 v2
|
||||
play_audio_and_wait_2(t, e, n)// 播放并等待 v2
|
||||
stop_audio_2(t, e, n) // 停止声音
|
||||
play_words_audio(t, e, n) // 朗读文字
|
||||
play_words_audio_wait(t, e, n)// 朗读并等待
|
||||
```
|
||||
|
||||
### 3.6 事件
|
||||
```javascript
|
||||
self_broadcast(t, e, n) // 广播消息
|
||||
self_broadcast_and_wait(t, e, n) // 广播并等待
|
||||
self_listen(t, e, n) // 当收到消息
|
||||
when(t, e, n) // 当...时
|
||||
start_on_click(t, e, n) // 当点击
|
||||
```
|
||||
|
||||
### 3.7 控制
|
||||
```javascript
|
||||
wait(t, e, n) // 等待
|
||||
repeat_n_times // 重复 N 次
|
||||
repeat_forever // 永久重复
|
||||
repeat_forever_until // 重复直到
|
||||
terminate(t, e, n) // 终止
|
||||
stop(t, e, n) // 停止
|
||||
restart(t, e, n) // 重新开始
|
||||
destruct(t, e, n) // 销毁
|
||||
```
|
||||
|
||||
### 3.8 逻辑和数学
|
||||
```javascript
|
||||
logic_compare(t, e, n) // 比较
|
||||
logic_operation(t, e, n) // 逻辑运算
|
||||
logic_negate(t, e, n) // 逻辑非
|
||||
logic_boolean(t, e, n) // 布尔值
|
||||
math_arithmetic(t, e, n) // 四则运算
|
||||
math_single(t, e, n) // 单目运算
|
||||
math_round(t, e, n) // 取整
|
||||
math_modulo(t, e, n) // 取余
|
||||
random(t, e, n) // 随机数
|
||||
calculate(t, e, n) // 计算表达式
|
||||
```
|
||||
|
||||
### 3.9 文本
|
||||
```javascript
|
||||
text_join(t, e, n) // 拼接
|
||||
text_length(t, e, n) // 长度
|
||||
text_indexof(t, e, n) // 查找
|
||||
text_contain(t, e, n) // 包含
|
||||
text_select(t, e, n) // 截取
|
||||
text_split(t, e, n) // 分割
|
||||
text_append(t, e, n) // 追加
|
||||
text_implicit(t, e, n) // 隐式转换
|
||||
```
|
||||
|
||||
### 3.10 询问/语音
|
||||
```javascript
|
||||
self_ask_listen(t, e, n) // 询问并等待回答
|
||||
voice_recognition(t, e, n) // 语音识别
|
||||
self_ask_record(t, e, n) // 询问并录音
|
||||
get_voice_answer(t, e, n) // 获取语音回答
|
||||
enable_voice_detection(t, e, n) // 启用声音侦测
|
||||
get_voice_volume(t, e, n) // 获取音量
|
||||
```
|
||||
|
||||
### 3.11 时间
|
||||
```javascript
|
||||
get_time(t, e, n) // 获取时间
|
||||
get_cur_frames(t, e, n) // 获取当前帧数
|
||||
reset_timer(t, e, n) // 重置计时器
|
||||
get_timer(t, e, n) // 获取计时器
|
||||
```
|
||||
|
||||
## 4. BCM 加载流程
|
||||
|
||||
```
|
||||
1. player.codemao.cn/t/{work_id} 加载
|
||||
2. fetch_bcm_request action 触发
|
||||
3. API: GET https://api.codemao.cn/api/work/info/{work_id}
|
||||
4. 获取 BCM 数据 (workspace_json)
|
||||
5. Heart.Compiler.compile(workspace_json) → compiled_block_map
|
||||
6. RuntimeManager.load(entities) → 加载所有实体
|
||||
7. RuntimeManager.run() → 开始执行
|
||||
8. requestAnimationFrame 循环 → RuntimeManager.update()
|
||||
→ 每个 Interpreter.step()
|
||||
```
|
||||
|
||||
## 5. 渲染层 (PIXI.js)
|
||||
|
||||
PIXI.js 版本: 6.0.4
|
||||
|
||||
```javascript
|
||||
// PIXI 核心组件
|
||||
PIXI.Application // 应用 (包含 ticker, stage, renderer)
|
||||
PIXI.Container // 容器 (display object 树)
|
||||
PIXI.Sprite // 精灵
|
||||
PIXI.Graphics // 矢量图形
|
||||
PIXI.Text // 文字
|
||||
PIXI.Filter // 滤镜 (blur, twist, pixelate, ASCII 等)
|
||||
PIXI.Ticker // 帧循环 (requestAnimationFrame)
|
||||
PIXI.InteractionManager // 交互管理 (点击, 拖拽)
|
||||
PIXI.Texture // 纹理
|
||||
PIXI.BaseTexture // 基础纹理
|
||||
PIXI.RenderTexture // 渲染纹理
|
||||
|
||||
// 着色器
|
||||
// 顶点着色器: 标准 2D 变换
|
||||
// 片段着色器: 纹理采样 + 颜色混合
|
||||
// 支持 WebGL 1.0 和 2.0
|
||||
```
|
||||
|
||||
## 6. 性能瓶颈分析
|
||||
|
||||
| 组件 | 耗时占比 | 瓶颈原因 |
|
||||
|------|---------|---------|
|
||||
| Interpreter.do_step() | ~60% | 逐条解释, dispatch 开销, 栈帧管理 |
|
||||
| Interpreter.step_eval_dispatch() | ~25% | 大量 if-else 类型判断 |
|
||||
| RuntimeManager.update() | ~10% | 遍历所有 interpreter |
|
||||
| PIXI.js 渲染 | ~5% | 已经是 WebGL, 高效 |
|
||||
|
||||
## 7. JIT 加速切入点
|
||||
|
||||
### 7.1 直接替换 Interpreter.step()
|
||||
```javascript
|
||||
// 找到 Interpreter 实例
|
||||
const rm = heart.get_runtime_manager();
|
||||
// rm 内部维护 interpreter 列表
|
||||
// 替换每个 interpreter 的 step 方法
|
||||
```
|
||||
|
||||
### 7.2 拦截 Compiler.compile()
|
||||
```javascript
|
||||
// 在编译阶段介入
|
||||
// 将 compiled_block_map 转为原生 JS
|
||||
// 绕过 Interpreter 的 step-by-step 执行
|
||||
```
|
||||
|
||||
### 7.3 优化 step_eval_dispatch()
|
||||
```javascript
|
||||
// 将 if-else 链改为 lookup table
|
||||
// 预编译 block type → handler 映射
|
||||
// 减少每次 dispatch 的开销
|
||||
```
|
||||
|
||||
## 8. 关键代码位置 (webpack 模块)
|
||||
|
||||
| 功能 | 模块特征 |
|
||||
|------|---------|
|
||||
| Heart 入口 | `new_heart`, `e.Heart` |
|
||||
| Registry | `e.RegistryImpl`, `has_responder_type` |
|
||||
| Compiler | `e.CompilerImpl`, `compile_from_workspace_json` |
|
||||
| OptiCompiler | `e.OptiCompilerImpl` |
|
||||
| Interpreter | `do_step`, `step_eval_dispatch` |
|
||||
| RuntimeManager | `e.RuntimeManagerImpl`, `get_runtime_manager` |
|
||||
| BlockPredicates | `e.BlockPredicatesImpl`, `repeat_forever` |
|
||||
| BlockProvider | `e.BasicBlockProviderFactoryImpl` |
|
||||
| Domain Functions | `get_domain_functions`, 各种 `self_*`, `lists_*` |
|
||||
| PIXI 渲染 | `PIXI.Application`, `lvVU` 模块 |
|
||||
| 音频 | `AudioPlayer`, `Howl`, MIDI |
|
||||
| 物理 | `physics2_*` |
|
||||
| Redux Store | `dqaR` 模块, `ALsR` |
|
||||
| Theatre | `ypP6` 模块, `ft` 组件 |
|
||||
| Cloud | `qVl3` 模块, BCM 加载 |
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,806 +0,0 @@
|
||||
// ==UserScript==
|
||||
// @name 编程猫 Heart JIT 加速器
|
||||
// @namespace https://gitee.com/shuiyu1123/codemao-webgl-engine
|
||||
// @version 3.1.0
|
||||
// @description 绕过 Heart 运行时解释器,将积木 AST 直接编译为原生 JS 执行
|
||||
// @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.1
|
||||
* ============================================================
|
||||
*
|
||||
* 从 player_v2.js 源码分析得到的注入点:
|
||||
*
|
||||
* 1. PIXI.Application 创建时挂在 canvas.__pixi_app
|
||||
* 2. Redux store 通过 T.a.get_store() 访问
|
||||
* 3. Heart 运行时通过 Object(E.b)().get_runtime_manager() 获取
|
||||
* 4. 游戏循环在 requestAnimationFrame 中运行
|
||||
* 5. BCM 数据加载后通过 LOAD_BCM action 触发
|
||||
*
|
||||
* 注入策略:
|
||||
* - 拦截 PIXI.Application 构造函数 → 拿到 pixi app 实例
|
||||
* - 通过 pixi app 的 ticker 回调找到 Heart 运行时
|
||||
* - 替换 Interpreter 的 step 方法为 JIT 编译版本
|
||||
*
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
(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,
|
||||
profileInterval: 5000, // 性能采样间隔
|
||||
};
|
||||
|
||||
// ================================================================
|
||||
// 统计
|
||||
// ================================================================
|
||||
const stats = {
|
||||
jitted: 0,
|
||||
interpreted: 0,
|
||||
fallbacks: 0,
|
||||
executions: 0,
|
||||
compileTime: 0,
|
||||
avgStepTime: 0,
|
||||
pixiApps: 0,
|
||||
heartFound: false,
|
||||
};
|
||||
|
||||
// ================================================================
|
||||
// JIT 编译器
|
||||
// ================================================================
|
||||
class JITCompiler {
|
||||
constructor() {
|
||||
this.cache = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将一个积木过程编译为原生 JS 生成器函数
|
||||
* 使用生成器是为了支持 Heart 的 yield 协程机制
|
||||
*/
|
||||
compile(blockChain) {
|
||||
if (!blockChain) return null;
|
||||
const id = blockChain.id || 'chain_' + Math.random();
|
||||
if (this.cache.has(id)) return this.cache.get(id);
|
||||
|
||||
const t0 = performance.now();
|
||||
try {
|
||||
const body = this.emitChain(blockChain, 0);
|
||||
// 生成器函数,支持 yield 协程
|
||||
const fn = new Function('ctx', `
|
||||
"use strict";
|
||||
const { runtime, data, eventBus, ohno, entity_id, interpreter_id } = ctx;
|
||||
let yield_count = 0;
|
||||
let _break_flag = false;
|
||||
${body}
|
||||
`);
|
||||
stats.compileTime += performance.now() - t0;
|
||||
stats.jitted++;
|
||||
this.cache.set(id, fn);
|
||||
if (CONFIG.debug) log(`JIT: compiled ${blockChain.type || 'chain'} (${id}) in ${(performance.now()-t0).toFixed(2)}ms`);
|
||||
return fn;
|
||||
} catch (e) {
|
||||
stats.fallbacks++;
|
||||
if (CONFIG.debug) warn('JIT compile failed:', e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
emitChain(block, depth) {
|
||||
if (!block || depth > 50) return '';
|
||||
let code = this.emitBlock(block, depth);
|
||||
if (block.next_block) {
|
||||
code += '\n' + this.emitChain(block.next_block, depth);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
emitBlock(block, depth) {
|
||||
const t = block.type;
|
||||
const p = block.params || {};
|
||||
const children = block.child_block || [];
|
||||
|
||||
switch (t) {
|
||||
// === 变量 ===
|
||||
case 'variables_set':
|
||||
return `runtime.set_variable(${JSON.stringify(p.VAR)}, ${this.val(p.VALUE, depth)}, entity_id, interpreter_id);`;
|
||||
case 'variables_get':
|
||||
return `runtime.get_variable(${JSON.stringify(p.VAR)}, entity_id, interpreter_id)`;
|
||||
case 'change_variable': {
|
||||
const m = p.method || 'increase';
|
||||
return `(function(){var c=runtime.get_variable(${JSON.stringify(p.valname)},entity_id,interpreter_id);var a=${this.val(p.n,depth)};runtime.set_variable(${JSON.stringify(p.valname)},${m==='increase'?'c+a':'c-a'},entity_id,interpreter_id);})()`;
|
||||
}
|
||||
|
||||
// === 逻辑 ===
|
||||
case 'logic_compare': {
|
||||
const op = {EQ:'==',NEQ:'!=',LT:'<',LTE:'<=',GT:'>',GTE:'>='}[p.OP] || '==';
|
||||
return `(${this.val(p.A,depth)} ${op} ${this.val(p.B,depth)})`;
|
||||
}
|
||||
case 'logic_operation':
|
||||
return p.OP === 'AND'
|
||||
? `(${this.val(p.A,depth)} && ${this.val(p.B,depth)})`
|
||||
: `(${this.val(p.A,depth)} || ${this.val(p.B,depth)})`;
|
||||
case 'logic_negate':
|
||||
return `(!${this.val(p.BOOL,depth)})`;
|
||||
case 'logic_boolean':
|
||||
return p.BOOL === 'TRUE' ? 'true' : 'false';
|
||||
|
||||
// === 数学 ===
|
||||
case 'math_arithmetic': {
|
||||
const ops = {ADD:'+',MINUS:'-',MULTIPLY:'*',DIVIDE:'/'}[p.OP];
|
||||
if (ops) return `(${this.val(p.A,depth)} ${ops} ${this.val(p.B,depth)})`;
|
||||
if (p.OP === 'POWER') return `Math.pow(${this.val(p.A,depth)},${this.val(p.B,depth)})`;
|
||||
return `(${this.val(p.A,depth)} + ${this.val(p.B,depth)})`;
|
||||
}
|
||||
case 'math_modulo':
|
||||
return `(${this.val(p.DIVIDEND,depth)} % ${this.val(p.DIVISOR,depth)})`;
|
||||
case 'random':
|
||||
return `Math.floor(${this.val(p.a,depth)} + Math.random() * (${this.val(p.b,depth)} - ${this.val(p.a,depth)} + 1))`;
|
||||
|
||||
// === 文本 ===
|
||||
case 'text_join': {
|
||||
const parts = [];
|
||||
for (let i = 0; p['ADD'+i] !== undefined; i++) parts.push(this.val(p['ADD'+i], depth));
|
||||
return parts.length ? `("" + ${parts.join(' + ')})` : '""';
|
||||
}
|
||||
case 'text_length':
|
||||
return `String(${this.val(p.VALUE,depth)}).length`;
|
||||
case 'text_contain':
|
||||
return `(String(${this.val(p.TEXT1,depth)}).indexOf(String(${this.val(p.TEXT2,depth)})) !== -1)`;
|
||||
case 'text_implicit':
|
||||
case 'default_value':
|
||||
case 'multiline_text':
|
||||
return JSON.stringify(p.TEXT || p.VALUE || '');
|
||||
|
||||
// === 控制流 ===
|
||||
case 'controls_if':
|
||||
case 'controls_if_no_else':
|
||||
case 'controls_if_dropdown': {
|
||||
const conds = p.conditions || p.if_dropdown_conditions || [];
|
||||
let code = '';
|
||||
for (let i = 0; i < conds.length; i++) {
|
||||
const cond = this.val(conds[i], depth);
|
||||
const body = children[i] ? this.emitChain(children[i], depth+1) : '';
|
||||
code += (i === 0 ? 'if' : ' else if') + `(${cond}){${body}}`;
|
||||
}
|
||||
if (children.length > conds.length) {
|
||||
code += ` else {${this.emitChain(children[children.length-1], depth+1)}}`;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
case 'repeat_n_times': {
|
||||
const times = this.val(p.TIMES, depth);
|
||||
const body = children[0] ? this.emitChain(children[0], depth+1) : '';
|
||||
return `for(var _i=0;_i<${times};_i++){${body}if(_break_flag){_break_flag=false;break;}}`;
|
||||
}
|
||||
case 'repeat_forever': {
|
||||
const body = children[0] ? this.emitChain(children[0], depth+1) : '';
|
||||
return `while(true){${body}yield_count++;if(yield_count>=500){yield_count=0;}if(_break_flag){_break_flag=false;break;}}`;
|
||||
}
|
||||
case 'repeat_forever_until': {
|
||||
const cond = this.val(p.condition, depth);
|
||||
const body = children[0] ? this.emitChain(children[0], depth+1) : '';
|
||||
return `while(!(${cond})){${body}yield_count++;if(yield_count>=500){yield_count=0;}if(_break_flag){_break_flag=false;break;}}`;
|
||||
}
|
||||
case 'wait':
|
||||
return `/* wait ${this.val(p.time,depth)}s */`;
|
||||
case 'break':
|
||||
return '_break_flag=true;';
|
||||
|
||||
// === 过程 ===
|
||||
case 'procedures_callnoreturn':
|
||||
case 'procedures_2_callnoreturn':
|
||||
case 'procedures_callreturn':
|
||||
case 'procedures_2_callreturn': {
|
||||
const name = p.NAME || block.procedure_name || '';
|
||||
const args = Object.entries(p).filter(([k]) => k !== 'NAME').map(([k,v]) => `${k}:${this.val(v,depth)}`).join(',');
|
||||
return `runtime.call_procedure(${JSON.stringify(name)},{${args}},entity_id,interpreter_id)`;
|
||||
}
|
||||
case 'procedures_return_value':
|
||||
case 'procedures_2_return_value':
|
||||
return `/* return ${this.val(p.VALUE,depth)} */`;
|
||||
|
||||
// === 广播 ===
|
||||
case 'self_broadcast':
|
||||
return `runtime.broadcaster_sending_message(entity_id,${JSON.stringify(p.message)},false);`;
|
||||
case 'self_broadcast_and_wait':
|
||||
return `runtime.broadcaster_sending_message(entity_id,${JSON.stringify(p.message)},true);`;
|
||||
|
||||
// === 时间 ===
|
||||
case 'get_time': {
|
||||
const m = {year:'getFullYear',month:'getMonth',date:'getDate',week:'getDay',hour:'getHours',minute:'getMinutes',second:'getSeconds'}[p.op];
|
||||
return m ? `(new Date).${m}()${p.op==='month'?'+1':''}` : '0';
|
||||
}
|
||||
case 'get_cur_frames':
|
||||
return 'runtime.get_elapsed_frames()';
|
||||
case 'get_timer':
|
||||
return 'runtime.get_timer_elapsed_s()';
|
||||
case 'reset_timer':
|
||||
return 'runtime.reset_timer();';
|
||||
case 'wait_seconds': {
|
||||
const secs = this.val(p.time, depth);
|
||||
return `/* wait ${secs}s */`;
|
||||
}
|
||||
|
||||
// === 列表 ===
|
||||
case 'lists_append':
|
||||
return `(function(){var l=runtime.get_variable(${JSON.stringify(p.VAR)},entity_id,interpreter_id);if(Array.isArray(l))l.push(${this.val(p.VALUE,depth)});})()`;
|
||||
case 'lists_get_value':
|
||||
return `(function(){var l=runtime.get_variable(${JSON.stringify(p.VAR)},entity_id,interpreter_id);if(!Array.isArray(l))return undefined;var i=${this.val(p.INDEX,depth)};${p.TYPE==='last'?'i=l.length;':''}i=i>0?i-1:i+l.length;return l[i];})()`;
|
||||
case 'lists_length':
|
||||
return `(function(){var l=runtime.get_variable(${JSON.stringify(p.VAR)},entity_id,interpreter_id);return Array.isArray(l)?l.length:0;})()`;
|
||||
case 'lists_delete':
|
||||
return `(function(){var l=runtime.get_variable(${JSON.stringify(p.VAR)},entity_id,interpreter_id);if(!Array.isArray(l))return;var i=${this.val(p.INDEX,depth)};${p.TYPE==='last'?'l.pop();':'i=i>0?i-1:i+l.length;l.splice(i,1);'}})()`;
|
||||
|
||||
// === 通用 domain function ===
|
||||
default:
|
||||
return this.emitDomainFunc(block, depth);
|
||||
}
|
||||
}
|
||||
|
||||
emitDomainFunc(block, depth) {
|
||||
const p = block.params || {};
|
||||
const args = Object.entries(p).map(([k,v]) => `${k}:${this.val(v,depth)}`).join(',');
|
||||
return `(function(){try{var df=runtime.get_domain_function(${JSON.stringify(block.type)});if(df)df({${args}},entity_id,interpreter_id,ctx);}catch(e){if(${CONFIG.debug})console.error(${JSON.stringify(block.type)},e);}})()`;
|
||||
}
|
||||
|
||||
val(v, depth) {
|
||||
if (v === undefined || v === null) return 'undefined';
|
||||
if (typeof v === 'number' || typeof v === 'boolean') return JSON.stringify(v);
|
||||
if (typeof v === 'string') return JSON.stringify(v);
|
||||
if (typeof v === 'object' && v.type) {
|
||||
const inner = this.emitBlock(v, depth + 1);
|
||||
if (inner.length < 100 && !inner.includes(';')) return inner;
|
||||
return `(function(){${inner}})()`;
|
||||
}
|
||||
return JSON.stringify(v);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 注入器 — 通过拦截 PIXI 和 RAF 找到 Heart 运行时
|
||||
// ================================================================
|
||||
class HeartJITInjector {
|
||||
constructor() {
|
||||
this.jit = new JITCompiler();
|
||||
this.heartRM = null; // runtime_manager
|
||||
this.injected = false;
|
||||
this.patchedInterpreters = new WeakSet();
|
||||
this.origRAF = window.requestAnimationFrame;
|
||||
this.frameCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装拦截器
|
||||
*/
|
||||
install() {
|
||||
this.interceptRAF();
|
||||
this.interceptPIXI();
|
||||
this.startProfiling();
|
||||
log('拦截器已安装,等待作品加载...');
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截 requestAnimationFrame 以检测游戏循环
|
||||
*/
|
||||
interceptRAF() {
|
||||
const self = this;
|
||||
const orig = this.origRAF;
|
||||
|
||||
window.requestAnimationFrame = function(cb) {
|
||||
return orig.call(window, function(time) {
|
||||
self.frameCount++;
|
||||
// 每 120 帧尝试一次注入
|
||||
if (self.frameCount % 120 === 0 && !self.injected) {
|
||||
self.tryInject();
|
||||
}
|
||||
cb(time);
|
||||
});
|
||||
};
|
||||
log('RAF 拦截已安装');
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截 PIXI.Application 构造函数
|
||||
*/
|
||||
interceptPIXI() {
|
||||
const self = this;
|
||||
// 等 PIXI 加载
|
||||
const check = setInterval(() => {
|
||||
if (window.PIXI && window.PIXI.Application) {
|
||||
clearInterval(check);
|
||||
self.hookPIXIApplication();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
hookPIXIApplication() {
|
||||
const self = this;
|
||||
const OrigApp = window.PIXI.Application;
|
||||
|
||||
// 保存原始构造函数
|
||||
const PatchedApp = function(...args) {
|
||||
const app = new OrigApp(...args);
|
||||
stats.pixiApps++;
|
||||
log(`PIXI.Application 创建 (#${stats.pixiApps})`);
|
||||
|
||||
// 标记 canvas
|
||||
if (app.view) {
|
||||
app.view.__pixi_app = app;
|
||||
app.view.__heart_jit_marked = true;
|
||||
}
|
||||
|
||||
// 尝试通过 ticker 找到 Heart
|
||||
self.tryInjectViaPIXI(app);
|
||||
|
||||
return app;
|
||||
};
|
||||
|
||||
// 复制原型
|
||||
PatchedApp.prototype = OrigApp.prototype;
|
||||
PatchedApp.prototype.constructor = PatchedApp;
|
||||
|
||||
// 替换
|
||||
window.PIXI.Application = PatchedApp;
|
||||
log('PIXI.Application 已 hook');
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过 PIXI app 尝试注入
|
||||
*/
|
||||
tryInjectViaPIXI(app) {
|
||||
// PIXI ticker 会调用 render,而 render 连接到 Heart 的更新循环
|
||||
// 我们在 ticker 回调中查找 Heart 实例
|
||||
const self = this;
|
||||
let checkCount = 0;
|
||||
|
||||
const checkTicker = () => {
|
||||
checkCount++;
|
||||
if (self.injected || checkCount > 600) return; // 10 秒超时
|
||||
|
||||
// 尝试从 React fiber 树中找到 Heart 实例
|
||||
const heart = self.findHeartFromDOM();
|
||||
if (heart) {
|
||||
self.setupJIT(heart);
|
||||
return;
|
||||
}
|
||||
|
||||
// 继续检查
|
||||
requestAnimationFrame(checkTicker);
|
||||
};
|
||||
|
||||
requestAnimationFrame(checkTicker);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 DOM 中查找 Heart 运行时实例
|
||||
* 通过 React fiber 树遍历
|
||||
*/
|
||||
findHeartFromDOM() {
|
||||
try {
|
||||
// 查找 canvas 元素
|
||||
const canvas = document.querySelector('canvas');
|
||||
if (!canvas) return null;
|
||||
|
||||
// React 16+ fiber 节点
|
||||
const fiberKey = Object.keys(canvas).find(k =>
|
||||
k.startsWith('__reactFiber$') || k.startsWith('__reactInternalInstance$')
|
||||
);
|
||||
if (!fiberKey) return null;
|
||||
|
||||
let fiber = canvas[fiberKey];
|
||||
// 向上遍历 fiber 树查找包含 heart 的组件
|
||||
for (let i = 0; i < 50 && fiber; i++) {
|
||||
const state = fiber.memoizedState;
|
||||
const props = fiber.memoizedProps;
|
||||
|
||||
// 检查 stateNode
|
||||
if (fiber.stateNode && fiber.stateNode.get_heart) {
|
||||
const heart = fiber.stateNode.get_heart();
|
||||
if (heart && heart.get_runtime_manager) {
|
||||
return heart;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查 props 中的 heart 引用
|
||||
if (props && props.get_heart) {
|
||||
const heart = props.get_heart();
|
||||
if (heart && heart.get_runtime_manager) {
|
||||
return heart;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查 ref
|
||||
if (fiber.ref && fiber.ref.current && fiber.ref.current.get_heart) {
|
||||
const heart = fiber.ref.current.get_heart();
|
||||
if (heart && heart.get_runtime_manager) {
|
||||
return heart;
|
||||
}
|
||||
}
|
||||
|
||||
fiber = fiber.return;
|
||||
}
|
||||
|
||||
// 备用:查找全局变量
|
||||
if (window.__heart) return window.__heart;
|
||||
if (window.__pixi_app) {
|
||||
const app = window.__pixi_app;
|
||||
if (app.__heart) return app.__heart;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
if (CONFIG.debug) warn('DOM 查找失败:', e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 主注入尝试
|
||||
*/
|
||||
tryInject() {
|
||||
if (this.injected) return true;
|
||||
|
||||
const heart = this.findHeartFromDOM();
|
||||
if (heart) {
|
||||
this.setupJIT(heart);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 备用方案:从 Redux store 查找
|
||||
const store = this.findReduxStore();
|
||||
if (store) {
|
||||
const state = store.getState();
|
||||
// Heart 运行时可能在某个 reducer 的 state 中
|
||||
if (state.cloud_states && state.cloud_states.loading_state === 'load_done') {
|
||||
log('检测到作品已加载,尝试从 store 获取 Heart...');
|
||||
this.tryInjectFromStore(store);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找 Redux store
|
||||
*/
|
||||
findReduxStore() {
|
||||
// 编程猫用 T.a.get_store() 暴露 store
|
||||
// 尝试从 window 上查找
|
||||
try {
|
||||
// 查找 store 的特征:dispatch + getState + subscribe
|
||||
if (window.__store) return window.__store;
|
||||
|
||||
// 扫描 window 上的对象
|
||||
for (const key of Object.keys(window)) {
|
||||
const obj = window[key];
|
||||
if (obj && typeof obj.dispatch === 'function' &&
|
||||
typeof obj.getState === 'function' &&
|
||||
typeof obj.subscribe === 'function') {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Redux store 尝试注入
|
||||
*/
|
||||
tryInjectFromStore(store) {
|
||||
// 监听 store 变化,当 Heart 初始化完成后注入
|
||||
const self = this;
|
||||
let lastState = store.getState();
|
||||
|
||||
store.subscribe(() => {
|
||||
const state = store.getState();
|
||||
if (state.run_states && state.run_states.run_state === 1 && !self.injected) {
|
||||
// 游戏正在运行
|
||||
self.tryInject();
|
||||
}
|
||||
lastState = state;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 JIT 加速
|
||||
*/
|
||||
setupJIT(heart) {
|
||||
if (this.injected) return;
|
||||
|
||||
try {
|
||||
const rm = heart.get_runtime_manager();
|
||||
if (!rm) {
|
||||
warn('Runtime manager 未找到');
|
||||
return;
|
||||
}
|
||||
|
||||
this.heartRM = rm;
|
||||
this.injected = true;
|
||||
stats.heartFound = true;
|
||||
|
||||
// 拦截 Interpreter 创建
|
||||
this.patchInterpreterFactory(rm);
|
||||
|
||||
log('✅ Heart JIT 加速器注入成功!');
|
||||
log(' Heart 运行时已找到,JIT 编译器就绪');
|
||||
|
||||
// 通知用户
|
||||
this.showNotification('Heart JIT 加速器已激活 ⚡');
|
||||
|
||||
} catch (e) {
|
||||
warn('注入失败:', e);
|
||||
stats.fallbacks++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch Interpreter 工厂方法
|
||||
* 当新的 Interpreter 实例创建时,自动替换其 step 方法
|
||||
*/
|
||||
patchInterpreterFactory(rm) {
|
||||
const self = this;
|
||||
const jit = this.jit;
|
||||
|
||||
// 拦截 load 方法(当积木加载时触发)
|
||||
const origLoad = rm.load?.bind(rm);
|
||||
if (origLoad) {
|
||||
rm.load = function(entities) {
|
||||
const result = origLoad(entities);
|
||||
// 加载后 patch 所有 Interpreter
|
||||
self.patchAllInterpreters(rm);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
// 拦截 run 方法
|
||||
const origRun = rm.run?.bind(rm);
|
||||
if (origRun) {
|
||||
rm.run = function() {
|
||||
const result = origRun();
|
||||
self.patchAllInterpreters(rm);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
// 立即 patch 已有的 interpreter
|
||||
this.patchAllInterpreters(rm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch 所有活跃的 Interpreter 实例
|
||||
*/
|
||||
patchAllInterpreters(rm) {
|
||||
// Heart 的 interpreter 通常存储在内部数组中
|
||||
// 通过遍历 rm 的属性查找
|
||||
try {
|
||||
for (const key of Object.keys(rm)) {
|
||||
const val = rm[key];
|
||||
if (Array.isArray(val)) {
|
||||
for (const item of val) {
|
||||
if (item && typeof item.step === 'function' && !this.patchedInterpreters.has(item)) {
|
||||
this.patchInterpreter(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (val && typeof val === 'object' && !Array.isArray(val)) {
|
||||
for (const subKey of Object.keys(val || {})) {
|
||||
const sub = val[subKey];
|
||||
if (sub && typeof sub.step === 'function' && !this.patchedInterpreters.has(sub)) {
|
||||
this.patchInterpreter(sub);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (CONFIG.debug) warn('patch interpreters 失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch 单个 Interpreter 实例
|
||||
*/
|
||||
patchInterpreter(interpreter) {
|
||||
if (this.patchedInterpreters.has(interpreter)) return;
|
||||
this.patchedInterpreters.add(interpreter);
|
||||
|
||||
const jit = this.jit;
|
||||
const origStep = interpreter.step?.bind(interpreter);
|
||||
|
||||
if (!origStep) return;
|
||||
|
||||
// 获取根积木
|
||||
const rootBlock = interpreter.root_block;
|
||||
if (!rootBlock) return;
|
||||
|
||||
// JIT 编译
|
||||
const jittedFn = jit.compile(rootBlock);
|
||||
if (!jittedFn) return;
|
||||
|
||||
// 替换 step 方法
|
||||
interpreter.step = function() {
|
||||
stats.executions++;
|
||||
try {
|
||||
// 尝试 JIT 执行
|
||||
const ctx = {
|
||||
runtime: this.run_mgr || this.runtime_manager || {},
|
||||
data: this.program_state || {},
|
||||
eventBus: {},
|
||||
ohno: {},
|
||||
entity_id: this.original_entities?.target_entity || '',
|
||||
interpreter_id: this.original_entities?.interpreter_id || '',
|
||||
};
|
||||
jittedFn(ctx);
|
||||
return { done: true };
|
||||
} catch (e) {
|
||||
// 降级到原始解释器
|
||||
stats.fallbacks++;
|
||||
return origStep();
|
||||
}
|
||||
};
|
||||
|
||||
stats.jitted++;
|
||||
if (CONFIG.debug) log(`Patched interpreter: ${rootBlock.type || 'unknown'}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示通知
|
||||
*/
|
||||
showNotification(msg) {
|
||||
try {
|
||||
const div = document.createElement('div');
|
||||
div.style.cssText = `
|
||||
position:fixed;top:20px;left:50%;transform:translateX(-50%);
|
||||
background:linear-gradient(135deg,#0f0,#0a0);color:#fff;
|
||||
padding:12px 24px;border-radius:8px;font-family:monospace;font-size:14px;
|
||||
z-index:999999;box-shadow:0 4px 20px rgba(0,255,0,0.3);
|
||||
animation:jit-fade 3s forwards;
|
||||
`;
|
||||
div.textContent = msg;
|
||||
document.body.appendChild(div);
|
||||
|
||||
// 添加动画
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
@keyframes jit-fade {
|
||||
0% { opacity: 0; transform: translateX(-50%) translateY(-20px); }
|
||||
20% { opacity: 1; transform: translateX(-50%) translateY(0); }
|
||||
80% { opacity: 1; }
|
||||
100% { opacity: 0; transform: translateX(-50%) translateY(-10px); }
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
setTimeout(() => div.remove(), 3000);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* 性能监控
|
||||
*/
|
||||
startProfiling() {
|
||||
setInterval(() => {
|
||||
if (this.injected && CONFIG.debug) {
|
||||
log(`Stats: jitted=${stats.jitted}, exec=${stats.executions}, fallback=${stats.fallbacks}, compile=${stats.compileTime.toFixed(1)}ms`);
|
||||
}
|
||||
}, CONFIG.profileInterval);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 性能面板
|
||||
// ================================================================
|
||||
class PerfPanel {
|
||||
constructor() {
|
||||
this.el = null;
|
||||
this.interval = null;
|
||||
}
|
||||
|
||||
show() {
|
||||
if (this.el) 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.9);color:#0f0;font-family:'Courier New',monospace;
|
||||
font-size:11px;padding:10px 14px;border-radius:6px;border:1px solid #0f0;
|
||||
min-width:200px;box-shadow:0 0 15px rgba(0,255,0,0.2);
|
||||
cursor:move;user-select:none;
|
||||
`;
|
||||
|
||||
const title = document.createElement('div');
|
||||
title.style.cssText = 'font-size:13px;font-weight:bold;margin-bottom:6px;color:#0f0;border-bottom:1px solid #030;padding-bottom:4px;';
|
||||
title.textContent = '🐱 Heart JIT v3.1';
|
||||
panel.appendChild(title);
|
||||
|
||||
const content = document.createElement('div');
|
||||
content.id = 'jit-stats';
|
||||
panel.appendChild(content);
|
||||
|
||||
// 拖拽
|
||||
let dragging = false, ox, oy;
|
||||
panel.addEventListener('mousedown', e => { dragging=true; ox=e.clientX-panel.offsetLeft; oy=e.clientY-panel.offsetTop; });
|
||||
document.addEventListener('mousemove', e => { if(dragging){panel.style.left=(e.clientX-ox)+'px';panel.style.top=(e.clientY-oy)+'px';panel.style.right='auto';} });
|
||||
document.addEventListener('mouseup', () => dragging=false);
|
||||
|
||||
// 右键切换 debug
|
||||
panel.addEventListener('contextmenu', e => { e.preventDefault(); CONFIG.debug=!CONFIG.debug; });
|
||||
|
||||
document.body.appendChild(panel);
|
||||
this.el = content;
|
||||
|
||||
this.interval = setInterval(() => this.update(), 500);
|
||||
}
|
||||
|
||||
update() {
|
||||
if (!this.el) return;
|
||||
const s = stats;
|
||||
const status = s.heartFound ? '✅ 已注入' : '⏳ 等待...';
|
||||
this.el.innerHTML = `
|
||||
<div>状态: <span style="color:${s.heartFound?'#0f0':'#ff0'}">${status}</span></div>
|
||||
<div>JIT 函数: <span style="color:#0ff">${s.jitted}</span></div>
|
||||
<div>执行次数: <span style="color:#0f0">${s.executions}</span></div>
|
||||
<div>降级: <span style="color:#f00">${s.fallbacks}</span></div>
|
||||
<div>编译耗时: <span style="color:#888">${s.compileTime.toFixed(1)}ms</span></div>
|
||||
<div style="margin-top:4px;font-size:9px;color:#555">右键切换debug</div>
|
||||
`;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.interval) clearInterval(this.interval);
|
||||
if (this.el?.parentElement) this.el.parentElement.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 主入口
|
||||
// ================================================================
|
||||
function main() {
|
||||
log('🚀 Heart JIT 加速器 v3.1 启动');
|
||||
log(' 渲染层: PIXI.js (WebGL) — 无需替换');
|
||||
log(' 目标: Heart 解释器 → JIT 编译为原生 JS');
|
||||
|
||||
const injector = new HeartJITInjector();
|
||||
injector.install();
|
||||
|
||||
// 性能面板
|
||||
if (location.href.includes('player.codemao.cn') || location.href.includes('ide.codemao.cn')) {
|
||||
const panel = new PerfPanel();
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', () => setTimeout(() => panel.show(), 500));
|
||||
} else {
|
||||
setTimeout(() => panel.show(), 500);
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露 API
|
||||
window.__heartJIT = { injector, stats, CONFIG, version: '3.1.0' };
|
||||
log('✅ 就绪,等待作品加载...');
|
||||
}
|
||||
|
||||
// 启动
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', main);
|
||||
} else {
|
||||
main();
|
||||
}
|
||||
|
||||
})();
|
||||
Reference in new issue
Block a user