never-jscore 2.2.0__cp38-abi3-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- never_jscore/__init__.py +11 -0
- never_jscore/never_jscore.pyd +0 -0
- never_jscore/never_jscore.pyi +204 -0
- never_jscore/py.typed +2 -0
- never_jscore-2.2.0.dist-info/METADATA +1357 -0
- never_jscore-2.2.0.dist-info/RECORD +7 -0
- never_jscore-2.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,1357 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: never-jscore
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
14
|
+
Classifier: Programming Language :: Rust
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Classifier: Topic :: Software Development :: Interpreters
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Summary: High-performance Python JavaScript execution engine based on Deno Core (V8) with full Promise/async support - py_mini_racer style API
|
|
19
|
+
Keywords: javascript,execjs,deno,v8,js,python,rust,promise,async,py_mini_racer
|
|
20
|
+
License: MIT
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
23
|
+
Project-URL: Homepage, https://github.com/neverl805/never-jscore
|
|
24
|
+
Project-URL: Repository, https://github.com/neverl805/never-jscore
|
|
25
|
+
Project-URL: Documentation, https://github.com/neverl805/never-jscore#readme
|
|
26
|
+
Project-URL: Bug Tracker, https://github.com/neverl805/never-jscore/issues
|
|
27
|
+
|
|
28
|
+
# never_jscore 中文文档
|
|
29
|
+
|
|
30
|
+
基于 Deno Core (V8) 的高性能 Python JavaScript 执行引擎,**专为 JS 逆向工程和补环境优化**。
|
|
31
|
+
努力成为PyExecJS上位替代品
|
|
32
|
+
|
|
33
|
+
## 项目特点
|
|
34
|
+
|
|
35
|
+
- ⚡ **极致性能**:
|
|
36
|
+
- 使用 Rust + Deno Core (V8 引擎)
|
|
37
|
+
- 实例化 Context 复用优化
|
|
38
|
+
- **比 PyExecJS 快 100-300倍**
|
|
39
|
+
- **与 PyMiniRacer 性能相当,部分场景更快**
|
|
40
|
+
- 🔄 **Promise/async 支持**:
|
|
41
|
+
- 完整支持 Promise 和 async/await
|
|
42
|
+
- 自动等待异步结果
|
|
43
|
+
- **唯一高性能 Promise 方案**(PyMiniRacer 不支持)
|
|
44
|
+
- 🌐 **完整 Web API 扩展** (v2.2.0):
|
|
45
|
+
- ✅ **Node.js APIs**: require()、fs、path、fetch()
|
|
46
|
+
- ✅ **浏览器存储**: localStorage、sessionStorage
|
|
47
|
+
- ✅ **浏览器环境(简易实现)**: navigator、location、document、window、screen
|
|
48
|
+
- ✅ **URL 处理**: URL、URLSearchParams
|
|
49
|
+
- ✅ **表单数据**: FormData
|
|
50
|
+
- ✅ **事件系统**: Event、EventTarget
|
|
51
|
+
- ✅ **网络请求**: fetch()、XMLHttpRequest
|
|
52
|
+
- ✅ **Crypto APIs**: Base64、MD5、SHA1/256/512、HMAC、Hex
|
|
53
|
+
- ✅ **URL 编码**: encodeURIComponent、encodeURI 等
|
|
54
|
+
- ✅ **定时器**: setTimeout、setInterval(立即执行版本)
|
|
55
|
+
- ✅ **随机数**: crypto.randomUUID()、crypto.getRandomValues()
|
|
56
|
+
- 🎯 专为 JS 逆向和补环境设计,无需额外 polyfill
|
|
57
|
+
- 📦 **上下文隔离**: 每个 Context 独立的 V8 执行环境,互不干扰
|
|
58
|
+
- 🎯 **py_mini_racer 兼容**: API 设计类似 py_mini_racer,实例化使用
|
|
59
|
+
- 🧹 **自动内存管理**: 基于 Rust 的自动垃圾回收,无内存泄漏
|
|
60
|
+
- 🛡️ **类型安全**: 提供完整的类型提示(.pyi 文件)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## 特性对比表
|
|
64
|
+
|
|
65
|
+
| 特性 | never_jscore | PyExecJS | PyMiniRacer | js2py | dukpy |
|
|
66
|
+
|------|--------------|----------|-------------|-------|-------|
|
|
67
|
+
| 引擎 | V8 | Node/V8等 | V8 | 纯Python | Duktape |
|
|
68
|
+
| Promise | ✅ 完整 | ❌ | ⚠️ 有限 | ❌ | ❌ |
|
|
69
|
+
| async/await | ✅ | ❌ | ⚠️ | ❌ | ❌ |
|
|
70
|
+
| require() | ✅ 完整 | ✅ | ❌ | ❌ | ❌ |
|
|
71
|
+
| fetch() | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
72
|
+
| localStorage | ✅ | ❌ | ❌ | ❌ | ❌ |
|
|
73
|
+
| 浏览器环境 | ️ ⚠️大部分 | ❌ | ❌ | ⚠️ 部分 | ❌ |
|
|
74
|
+
| 性能 | ⚡⚡⚡⚡⚡ | ⚡⚡ | ⚡⚡⚡⚡⚡ | ⚡ | ⚡⚡⚡ |
|
|
75
|
+
| 安装难度 | 简单 | 需Node.js | 简单 | 简单 | 简单 |
|
|
76
|
+
| 上下文复用 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
77
|
+
| 类型转换 | 自动 | 自动 | 自动 | 自动 | 自动 |
|
|
78
|
+
| ES6+ | ✅ 完整 | ✅ | ✅ | ⚠️ 部分 | ⚠️ 部分 |
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 常见问题
|
|
83
|
+
|
|
84
|
+
### Q: 为什么 PyMiniRacer 在某些测试中更快?
|
|
85
|
+
A: PyMiniRacer 是 V8 的直接绑定,开销最小。never_jscore 使用 rust开发以及Deno Core,有轻量级包装层,但提供了更多功能(如 Promise 支持)。
|
|
86
|
+
|
|
87
|
+
### Q: 什么时候选择 never_jscore?
|
|
88
|
+
A: 当你需要:
|
|
89
|
+
- Promise/async 支持(现代 JS 库)
|
|
90
|
+
- 高性能 + Rust 稳定性
|
|
91
|
+
- 完整的 Node.js 环境(require、fs、path)
|
|
92
|
+
- 浏览器环境模拟(补环境)
|
|
93
|
+
- fetch() 网络请求
|
|
94
|
+
- localStorage/sessionStorage
|
|
95
|
+
- JS 逆向工程
|
|
96
|
+
|
|
97
|
+
### Q: PyExecJS 为什么这么慢?
|
|
98
|
+
A: PyExecJS 通过进程调用外部 JS 运行时,每次都有进程通信开销。
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
## 可用测试文件
|
|
103
|
+
- [benchmark.py](examples/benchmark.py) - 性能基准测试
|
|
104
|
+
- [test_async_simple.py](tests/test_async_simple.py) - 异步功能测试
|
|
105
|
+
- [test_extensions.py](tests/test_extensions.py) - 扩展 API 测试
|
|
106
|
+
- [test_new_apis.py](tests/test_new_apis.py) - 新 API 测试
|
|
107
|
+
- [test_all_features.py](tests/test_all_features.py) - 完整功能测试套件
|
|
108
|
+
- [test_browser_apis.py](tests/test_browser_apis.py) - 浏览器 API 测试
|
|
109
|
+
- [test_high_priority_apis.py](tests/test_high_priority_apis.py) - 高优先级 API 测试
|
|
110
|
+
- [test_wasm.py](tests/test_wasm.py) - WebAssembly 测试
|
|
111
|
+
- [use_polyfill.py](examples/use_polyfill.py) - Polyfill 使用示例
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
## 性能对比
|
|
117
|
+

|
|
118
|
+
|
|
119
|
+
| 测试项目 | never_jscore | PyMiniRacer | PyExecJS |
|
|
120
|
+
|------------------------------|----------------|------------|----------|
|
|
121
|
+
| 简单计算 | 0.007ms | 0.005ms | 2.3ms |
|
|
122
|
+
| 字符串操作 | **0.004ms** 🏆 | 0.008ms | 2.3ms |
|
|
123
|
+
| 数组操作 | **0.004ms** 🏆 | 0.006ms | 2.3ms |
|
|
124
|
+
| 复杂算法参数生成<br/>(1000次循环 )<br/>[benchmark.py](examples/benchmark.py) | **0.0111s** 🏆 | 0.0383s | 69.4735s |
|
|
125
|
+
| Promise | **✅ 0.003ms** | ❌ 不支持 | ❌ 不支持 |
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
## 安装
|
|
129
|
+
```bash
|
|
130
|
+
pip install never-jscore
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 从源码安装
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
pip install maturin
|
|
137
|
+
maturin develop --release
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## 快速开始
|
|
141
|
+
|
|
142
|
+
### 0. 创建 Context(启用扩展)
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
import never_jscore
|
|
146
|
+
|
|
147
|
+
# 启用 Web API 扩展(默认,推荐用于 JS 逆向)
|
|
148
|
+
ctx = never_jscore.Context(enable_extensions=True)
|
|
149
|
+
|
|
150
|
+
# 或禁用扩展(纯净 V8 环境)
|
|
151
|
+
ctx = never_jscore.Context(enable_extensions=False)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### 1. 基本用法(实例化 Context)
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
import never_jscore
|
|
158
|
+
|
|
159
|
+
# 创建 JavaScript 执行上下文
|
|
160
|
+
ctx = never_jscore.Context()
|
|
161
|
+
|
|
162
|
+
# 一次性求值
|
|
163
|
+
result = ctx.evaluate("1 + 2 + 3")
|
|
164
|
+
print(result) # 6
|
|
165
|
+
|
|
166
|
+
# 字符串操作
|
|
167
|
+
result = ctx.evaluate("'Hello'.toUpperCase()")
|
|
168
|
+
print(result) # HELLO
|
|
169
|
+
|
|
170
|
+
# 数组操作
|
|
171
|
+
result = ctx.evaluate("[1, 2, 3].map(x => x * 2)")
|
|
172
|
+
print(result) # [2, 4, 6]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 2. 编译和调用
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
import never_jscore
|
|
179
|
+
|
|
180
|
+
# 创建上下文
|
|
181
|
+
ctx = never_jscore.Context()
|
|
182
|
+
|
|
183
|
+
# 编译 JavaScript 代码
|
|
184
|
+
ctx.compile("""
|
|
185
|
+
function add(a, b) {
|
|
186
|
+
return a + b;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function greet(name) {
|
|
190
|
+
return "Hello, " + name + "!";
|
|
191
|
+
}
|
|
192
|
+
""")
|
|
193
|
+
|
|
194
|
+
# 调用函数
|
|
195
|
+
print(ctx.call("add", [5, 3])) # 8
|
|
196
|
+
print(ctx.call("greet", ["World"])) # Hello, World!
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 3. 异步支持(Promise/async/await)
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
import never_jscore
|
|
203
|
+
|
|
204
|
+
ctx = never_jscore.Context()
|
|
205
|
+
|
|
206
|
+
# async 函数
|
|
207
|
+
ctx.compile("""
|
|
208
|
+
async function fetchData(id) {
|
|
209
|
+
// 模拟异步操作
|
|
210
|
+
return await Promise.resolve({id: id, name: "User" + id});
|
|
211
|
+
}
|
|
212
|
+
""")
|
|
213
|
+
|
|
214
|
+
# Promise 自动等待
|
|
215
|
+
result = ctx.evaluate("Promise.resolve(42)")
|
|
216
|
+
print(result) # 42
|
|
217
|
+
|
|
218
|
+
result = ctx.call("fetchData", [123])
|
|
219
|
+
print(result) # {'id': 123, 'name': 'User123'}
|
|
220
|
+
|
|
221
|
+
# Promise 链
|
|
222
|
+
result = ctx.evaluate("""
|
|
223
|
+
Promise.resolve(10)
|
|
224
|
+
.then(x => x * 2)
|
|
225
|
+
.then(x => x + 5)
|
|
226
|
+
""")
|
|
227
|
+
print(result) # 25
|
|
228
|
+
|
|
229
|
+
# Promise.all 并发
|
|
230
|
+
result = ctx.evaluate("""
|
|
231
|
+
Promise.all([
|
|
232
|
+
Promise.resolve(1),
|
|
233
|
+
Promise.resolve(2),
|
|
234
|
+
Promise.resolve(3)
|
|
235
|
+
])
|
|
236
|
+
""")
|
|
237
|
+
print(result) # [1, 2, 3]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### 4. 上下文状态管理
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
import never_jscore
|
|
244
|
+
|
|
245
|
+
ctx = never_jscore.Context()
|
|
246
|
+
ctx.compile("""
|
|
247
|
+
var counter = 0;
|
|
248
|
+
|
|
249
|
+
function increment() {
|
|
250
|
+
return ++counter;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function getCounter() {
|
|
254
|
+
return counter;
|
|
255
|
+
}
|
|
256
|
+
""")
|
|
257
|
+
|
|
258
|
+
print(ctx.call("increment", [])) # 1
|
|
259
|
+
print(ctx.call("increment", [])) # 2
|
|
260
|
+
print(ctx.call("getCounter", [])) # 2
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### 5. 复杂对象处理
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
import never_jscore
|
|
267
|
+
|
|
268
|
+
ctx = never_jscore.Context()
|
|
269
|
+
ctx.compile("""
|
|
270
|
+
function processUser(name, age, tags) {
|
|
271
|
+
return {
|
|
272
|
+
name: name,
|
|
273
|
+
age: age,
|
|
274
|
+
isAdult: age >= 18,
|
|
275
|
+
tags: tags,
|
|
276
|
+
summary: name + ' (' + age + '岁)'
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
""")
|
|
280
|
+
|
|
281
|
+
result = ctx.call("processUser", ["张三", 25, ["Python", "JavaScript"]])
|
|
282
|
+
print(result)
|
|
283
|
+
# {
|
|
284
|
+
# 'name': '张三',
|
|
285
|
+
# 'age': 25,
|
|
286
|
+
# 'isAdult': True,
|
|
287
|
+
# 'tags': ['Python', 'JavaScript'],
|
|
288
|
+
# 'summary': '张三 (25岁)'
|
|
289
|
+
# }
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### 6. 性能监控
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
import never_jscore
|
|
296
|
+
|
|
297
|
+
ctx = never_jscore.Context()
|
|
298
|
+
ctx.compile("function compute(n) { return n * n; }")
|
|
299
|
+
|
|
300
|
+
# 执行多次
|
|
301
|
+
for i in range(100):
|
|
302
|
+
ctx.call("compute", [i])
|
|
303
|
+
|
|
304
|
+
# 查看统计
|
|
305
|
+
stats = ctx.get_stats()
|
|
306
|
+
print(f"执行次数: {stats[0]}") # 100
|
|
307
|
+
|
|
308
|
+
# 请求垃圾回收(可选)
|
|
309
|
+
ctx.gc()
|
|
310
|
+
|
|
311
|
+
# 重置统计
|
|
312
|
+
ctx.reset_stats()
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## API 参考
|
|
316
|
+
|
|
317
|
+
### Context 类
|
|
318
|
+
|
|
319
|
+
#### `never_jscore.Context(enable_extensions: bool = True)`
|
|
320
|
+
|
|
321
|
+
创建一个新的 JavaScript 执行上下文。
|
|
322
|
+
|
|
323
|
+
**参数**:
|
|
324
|
+
- `enable_extensions` (可选): 是否启用内置 Web API 扩展(默认 True)
|
|
325
|
+
- `True`: 启用 Crypto、URL 编码、setTimeout、Worker 等扩展
|
|
326
|
+
- `False`: 纯净 V8 环境,只有 ECMAScript 标准 API
|
|
327
|
+
|
|
328
|
+
**返回**: Context 对象
|
|
329
|
+
|
|
330
|
+
**示例**:
|
|
331
|
+
```python
|
|
332
|
+
# 启用扩展(默认,推荐用于 JS 逆向)
|
|
333
|
+
ctx = never_jscore.Context()
|
|
334
|
+
ctx = never_jscore.Context(enable_extensions=True)
|
|
335
|
+
|
|
336
|
+
# 禁用扩展(纯净 V8)
|
|
337
|
+
ctx = never_jscore.Context(enable_extensions=False)
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
#### `compile(code: str) -> None`
|
|
341
|
+
|
|
342
|
+
编译 JavaScript 代码并加入全局作用域。
|
|
343
|
+
|
|
344
|
+
**参数**:
|
|
345
|
+
- `code`: JavaScript 代码字符串
|
|
346
|
+
|
|
347
|
+
**示例**:
|
|
348
|
+
```python
|
|
349
|
+
ctx.compile('''
|
|
350
|
+
function add(a, b) { return a + b; }
|
|
351
|
+
''')
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
#### `eval(code: str, return_value: bool = False, auto_await: bool = True) -> Any`
|
|
355
|
+
|
|
356
|
+
执行代码并将其加入全局作用域。
|
|
357
|
+
|
|
358
|
+
**参数**:
|
|
359
|
+
- `code`: JavaScript 代码字符串
|
|
360
|
+
- `return_value`: 是否返回最后表达式的值(默认 False)
|
|
361
|
+
- `auto_await`: 是否自动等待 Promise(默认 True)
|
|
362
|
+
|
|
363
|
+
**返回**: 如果 return_value=True,返回执行结果;否则返回 None
|
|
364
|
+
|
|
365
|
+
**示例**:
|
|
366
|
+
```python
|
|
367
|
+
ctx.eval("var x = 10;") # 添加到全局作用域
|
|
368
|
+
result = ctx.eval("x * 2", return_value=True) # 返回 20
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
#### `evaluate(code: str, auto_await: bool = True) -> Any`
|
|
372
|
+
|
|
373
|
+
执行代码并返回结果(不影响全局作用域)。
|
|
374
|
+
|
|
375
|
+
**参数**:
|
|
376
|
+
- `code`: JavaScript 代码字符串
|
|
377
|
+
- `auto_await`: 是否自动等待 Promise(默认 True)
|
|
378
|
+
|
|
379
|
+
**返回**: 表达式的值
|
|
380
|
+
|
|
381
|
+
**示例**:
|
|
382
|
+
```python
|
|
383
|
+
result = ctx.evaluate("1 + 2 + 3") # 6
|
|
384
|
+
result = ctx.evaluate("Promise.resolve(42)") # 42
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
#### `call(name: str, args: list, auto_await: bool = True) -> Any`
|
|
388
|
+
|
|
389
|
+
调用 JavaScript 函数。
|
|
390
|
+
|
|
391
|
+
**参数**:
|
|
392
|
+
- `name`: 函数名称
|
|
393
|
+
- `args`: 参数列表
|
|
394
|
+
- `auto_await`: 是否自动等待 Promise(默认 True)
|
|
395
|
+
|
|
396
|
+
**返回**: 函数返回值
|
|
397
|
+
|
|
398
|
+
**示例**:
|
|
399
|
+
```python
|
|
400
|
+
result = ctx.call("add", [1, 2])
|
|
401
|
+
result = ctx.call("asyncFunc", [arg], auto_await=True)
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
#### `gc() -> None`
|
|
405
|
+
|
|
406
|
+
请求 V8 垃圾回收。
|
|
407
|
+
|
|
408
|
+
**注意**: 这只是提示,V8 会根据自己的策略决定是否执行。在大多数情况下无需手动调用。
|
|
409
|
+
|
|
410
|
+
#### `get_stats() -> tuple[int]`
|
|
411
|
+
|
|
412
|
+
获取执行统计信息。
|
|
413
|
+
|
|
414
|
+
**返回**: `(exec_count,)` 执行次数元组
|
|
415
|
+
|
|
416
|
+
#### `reset_stats() -> None`
|
|
417
|
+
|
|
418
|
+
重置统计信息。
|
|
419
|
+
|
|
420
|
+
## 类型转换
|
|
421
|
+
|
|
422
|
+
Python 和 JavaScript 之间的类型自动转换:
|
|
423
|
+
|
|
424
|
+
| Python 类型 | JavaScript 类型 | 说明 |
|
|
425
|
+
|------------|----------------|------|
|
|
426
|
+
| None | null | 空值 |
|
|
427
|
+
| bool | boolean | 布尔值 |
|
|
428
|
+
| int | number | 整数 |
|
|
429
|
+
| float | number | 浮点数 |
|
|
430
|
+
| str | string | 字符串 |
|
|
431
|
+
| list | Array | 数组 |
|
|
432
|
+
| dict | Object | 对象 |
|
|
433
|
+
|
|
434
|
+
**示例**:
|
|
435
|
+
```python
|
|
436
|
+
# Python -> JavaScript
|
|
437
|
+
ctx.call("func", [None, True, 42, 3.14, "hello", [1, 2], {"key": "value"}])
|
|
438
|
+
|
|
439
|
+
# JavaScript -> Python
|
|
440
|
+
result = ctx.evaluate("{name: 'John', age: 30}") # dict
|
|
441
|
+
result = ctx.evaluate("[1, 2, 3]") # list
|
|
442
|
+
result = ctx.evaluate("null") # None
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
## 使用限制
|
|
446
|
+
|
|
447
|
+
### ⚠️ 多 Context 限制(重要!)
|
|
448
|
+
|
|
449
|
+
由于 V8 引擎的 thread-local storage 限制和内存管理机制,**多个 Context 必须遵循特定的使用模式**。
|
|
450
|
+
|
|
451
|
+
#### 限制 1: 不能交叉使用
|
|
452
|
+
|
|
453
|
+
**一旦创建了第二个 Context,就不能再使用第一个 Context!**
|
|
454
|
+
|
|
455
|
+
```python
|
|
456
|
+
# ❌ 错误用法:交叉使用会崩溃
|
|
457
|
+
ctx1 = never_jscore.Context()
|
|
458
|
+
ctx1.compile("function f1() { return 1; }")
|
|
459
|
+
result1 = ctx1.call("f1", []) # OK
|
|
460
|
+
|
|
461
|
+
ctx2 = never_jscore.Context()
|
|
462
|
+
ctx2.compile("function f2() { return 2; }")
|
|
463
|
+
result2 = ctx2.call("f2", []) # OK
|
|
464
|
+
|
|
465
|
+
result1 = ctx1.call("f1", []) # ❌ 崩溃!不能回到 ctx1
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
#### 限制 2: LIFO 删除顺序
|
|
469
|
+
|
|
470
|
+
**多个 Context 必须按 LIFO 顺序删除(后创建先删除)**
|
|
471
|
+
|
|
472
|
+
```python
|
|
473
|
+
# ❌ 错误:按创建顺序删除会崩溃
|
|
474
|
+
ctx1 = never_jscore.Context()
|
|
475
|
+
ctx2 = never_jscore.Context()
|
|
476
|
+
ctx3 = never_jscore.Context()
|
|
477
|
+
|
|
478
|
+
del ctx1 # ❌ 崩溃!
|
|
479
|
+
del ctx2
|
|
480
|
+
del ctx3
|
|
481
|
+
|
|
482
|
+
# ✅ 正确:LIFO 顺序(后进先出)
|
|
483
|
+
ctx1 = never_jscore.Context()
|
|
484
|
+
ctx2 = never_jscore.Context()
|
|
485
|
+
ctx3 = never_jscore.Context()
|
|
486
|
+
|
|
487
|
+
# 使用最后创建的 Context
|
|
488
|
+
result = ctx3.call("func", [])
|
|
489
|
+
|
|
490
|
+
# 按逆序删除
|
|
491
|
+
del ctx3 # ✅ 最后创建,最先删除
|
|
492
|
+
del ctx2 # ✅
|
|
493
|
+
del ctx1 # ✅
|
|
494
|
+
|
|
495
|
+
# ❌ 错误:让 Python 自动清理(顺序不确定)
|
|
496
|
+
# 程序结束时会崩溃
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
#### 推荐的使用模式
|
|
500
|
+
|
|
501
|
+
**模式 1: 单 Context 模式(最推荐)**
|
|
502
|
+
|
|
503
|
+
将所有函数定义在一个 Context 中:
|
|
504
|
+
|
|
505
|
+
```python
|
|
506
|
+
import never_jscore
|
|
507
|
+
|
|
508
|
+
ctx = never_jscore.Context()
|
|
509
|
+
ctx.compile("""
|
|
510
|
+
function f1() { return 1; }
|
|
511
|
+
function f2() { return 2; }
|
|
512
|
+
function f3() { return 3; }
|
|
513
|
+
""")
|
|
514
|
+
|
|
515
|
+
# 可以任意调用
|
|
516
|
+
ctx.call("f1", []) # OK
|
|
517
|
+
ctx.call("f2", []) # OK
|
|
518
|
+
ctx.call("f1", []) # OK - 可以重复调用
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
**模式 2: 顺序使用(用完即删)**
|
|
522
|
+
|
|
523
|
+
```python
|
|
524
|
+
import never_jscore
|
|
525
|
+
|
|
526
|
+
# 使用第一个 Context
|
|
527
|
+
ctx1 = never_jscore.Context()
|
|
528
|
+
ctx1.compile("function f1() { return 1; }")
|
|
529
|
+
result = ctx1.call("f1", [])
|
|
530
|
+
del ctx1 # 用完立即删除
|
|
531
|
+
|
|
532
|
+
# 使用第二个 Context
|
|
533
|
+
ctx2 = never_jscore.Context()
|
|
534
|
+
ctx2.compile("function f2() { return 2; }")
|
|
535
|
+
result = ctx2.call("f2", [])
|
|
536
|
+
del ctx2 # 用完立即删除
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
**模式 3: LIFO 批量清理(高级用法)**
|
|
540
|
+
|
|
541
|
+
```python
|
|
542
|
+
import never_jscore
|
|
543
|
+
|
|
544
|
+
# 创建多个 Context(注意:创建第二个后不能再用第一个)
|
|
545
|
+
ctx1 = never_jscore.Context()
|
|
546
|
+
ctx1.compile("function f1() { return 1; }")
|
|
547
|
+
r1 = ctx1.call("f1", []) # 在创建 ctx2 之前完成所有操作
|
|
548
|
+
|
|
549
|
+
ctx2 = never_jscore.Context()
|
|
550
|
+
ctx2.compile("function f2() { return 2; }")
|
|
551
|
+
r2 = ctx2.call("f2", []) # ctx1 已不可用
|
|
552
|
+
|
|
553
|
+
ctx3 = never_jscore.Context()
|
|
554
|
+
ctx3.compile("function f3() { return 3; }")
|
|
555
|
+
r3 = ctx3.call("f3", []) # ctx1, ctx2 已不可用
|
|
556
|
+
|
|
557
|
+
# LIFO 顺序删除(后进先出)
|
|
558
|
+
del ctx3
|
|
559
|
+
del ctx2
|
|
560
|
+
del ctx1
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
## v2.2.0 新功能:Node.js 和浏览器 API
|
|
564
|
+
|
|
565
|
+
### 1. require() - CommonJS 模块系统
|
|
566
|
+
|
|
567
|
+
完整的 Node.js 模块解析实现,支持相对路径、node_modules、package.json:
|
|
568
|
+
|
|
569
|
+
```python
|
|
570
|
+
import never_jscore
|
|
571
|
+
|
|
572
|
+
ctx = never_jscore.Context()
|
|
573
|
+
|
|
574
|
+
# 创建测试模块
|
|
575
|
+
with open('my_module.js', 'w') as f:
|
|
576
|
+
f.write('''
|
|
577
|
+
module.exports = {
|
|
578
|
+
add: function(a, b) { return a + b; },
|
|
579
|
+
version: '1.0.0'
|
|
580
|
+
};
|
|
581
|
+
''')
|
|
582
|
+
|
|
583
|
+
# 使用 require() 加载模块
|
|
584
|
+
result = ctx.evaluate("""
|
|
585
|
+
const myModule = require('./my_module.js');
|
|
586
|
+
myModule.add(10, 20)
|
|
587
|
+
""")
|
|
588
|
+
print(result) # 30
|
|
589
|
+
|
|
590
|
+
# 使用内置模块 fs 和 path
|
|
591
|
+
ctx.compile("""
|
|
592
|
+
const fs = require('fs');
|
|
593
|
+
const path = require('path');
|
|
594
|
+
|
|
595
|
+
function readConfig() {
|
|
596
|
+
const filePath = path.join('.', 'config.json');
|
|
597
|
+
if (fs.existsSync(filePath)) {
|
|
598
|
+
return fs.readFileSync(filePath);
|
|
599
|
+
}
|
|
600
|
+
return '{}';
|
|
601
|
+
}
|
|
602
|
+
""")
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
**支持的功能**:
|
|
606
|
+
- ✅ 相对路径:`./module.js`, `../lib/utils.js`
|
|
607
|
+
- ✅ 绝对路径:`/path/to/module.js`
|
|
608
|
+
- ✅ node_modules 查找(递归向上)
|
|
609
|
+
- ✅ package.json 主入口解析
|
|
610
|
+
- ✅ 自动扩展名(`.js`, `.json`)
|
|
611
|
+
- ✅ 目录入口(`index.js`)
|
|
612
|
+
- ✅ 模块缓存(`require.cache`)
|
|
613
|
+
|
|
614
|
+
**内置模块**:
|
|
615
|
+
- `fs` - 文件系统操作
|
|
616
|
+
- `path` - 路径处理
|
|
617
|
+
|
|
618
|
+
### 2. fetch() - HTTP 网络请求
|
|
619
|
+
|
|
620
|
+
现代 HTTP API,兼容浏览器标准:
|
|
621
|
+
|
|
622
|
+
```python
|
|
623
|
+
import never_jscore
|
|
624
|
+
|
|
625
|
+
ctx = never_jscore.Context()
|
|
626
|
+
|
|
627
|
+
# GET 请求
|
|
628
|
+
result = ctx.evaluate("""
|
|
629
|
+
(async () => {
|
|
630
|
+
const response = await fetch('https://api.github.com/users/github');
|
|
631
|
+
const data = await response.json();
|
|
632
|
+
return {
|
|
633
|
+
status: response.status,
|
|
634
|
+
username: data.login,
|
|
635
|
+
followers: data.followers
|
|
636
|
+
};
|
|
637
|
+
})()
|
|
638
|
+
""")
|
|
639
|
+
print(result)
|
|
640
|
+
|
|
641
|
+
# POST 请求
|
|
642
|
+
result = ctx.evaluate("""
|
|
643
|
+
(async () => {
|
|
644
|
+
const response = await fetch('https://httpbin.org/post', {
|
|
645
|
+
method: 'POST',
|
|
646
|
+
headers: {
|
|
647
|
+
'Content-Type': 'application/json',
|
|
648
|
+
'Authorization': 'Bearer token123'
|
|
649
|
+
},
|
|
650
|
+
body: JSON.stringify({
|
|
651
|
+
username: 'test',
|
|
652
|
+
password: 'pass'
|
|
653
|
+
}),
|
|
654
|
+
timeout: 30000
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
const data = await response.json();
|
|
658
|
+
return data;
|
|
659
|
+
})()
|
|
660
|
+
""")
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
**支持的功能**:
|
|
664
|
+
- ✅ GET/POST/PUT/DELETE/PATCH 等所有 HTTP 方法
|
|
665
|
+
- ✅ 自定义请求头
|
|
666
|
+
- ✅ JSON 自动序列化/反序列化
|
|
667
|
+
- ✅ Response 对象(`text()`, `json()`, `blob()`, `arrayBuffer()`)
|
|
668
|
+
- ✅ 状态码和状态文本
|
|
669
|
+
- ✅ 超时控制
|
|
670
|
+
|
|
671
|
+
### 3. localStorage / sessionStorage
|
|
672
|
+
|
|
673
|
+
浏览器存储 API,用于补环境:
|
|
674
|
+
|
|
675
|
+
```python
|
|
676
|
+
import never_jscore
|
|
677
|
+
|
|
678
|
+
ctx = never_jscore.Context()
|
|
679
|
+
|
|
680
|
+
# localStorage - 持久化存储
|
|
681
|
+
ctx.eval("""
|
|
682
|
+
localStorage.setItem('token', 'abc123');
|
|
683
|
+
localStorage.setItem('user', JSON.stringify({name: 'John', id: 123}));
|
|
684
|
+
""")
|
|
685
|
+
|
|
686
|
+
# 获取值
|
|
687
|
+
token = ctx.evaluate("localStorage.getItem('token')")
|
|
688
|
+
print(token) # 'abc123'
|
|
689
|
+
|
|
690
|
+
# sessionStorage - 会话存储
|
|
691
|
+
ctx.eval("""
|
|
692
|
+
sessionStorage.setItem('sessionId', '999');
|
|
693
|
+
""")
|
|
694
|
+
|
|
695
|
+
# 完整示例
|
|
696
|
+
result = ctx.evaluate("""
|
|
697
|
+
// 存储用户偏好
|
|
698
|
+
localStorage.setItem('theme', 'dark');
|
|
699
|
+
localStorage.setItem('language', 'zh-CN');
|
|
700
|
+
|
|
701
|
+
// 读取偏好
|
|
702
|
+
const preferences = {
|
|
703
|
+
theme: localStorage.getItem('theme'),
|
|
704
|
+
language: localStorage.getItem('language'),
|
|
705
|
+
itemCount: localStorage.length
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
JSON.stringify(preferences);
|
|
709
|
+
""")
|
|
710
|
+
print(result) # {"theme":"dark","language":"zh-CN","itemCount":2}
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
**API 方法**:
|
|
714
|
+
- `setItem(key, value)` - 设置值
|
|
715
|
+
- `getItem(key)` - 获取值(不存在返回 null)
|
|
716
|
+
- `removeItem(key)` - 删除键
|
|
717
|
+
- `clear()` - 清空所有
|
|
718
|
+
- `key(index)` - 按索引获取键名
|
|
719
|
+
- `length` - 获取键数量
|
|
720
|
+
|
|
721
|
+
### 4. 浏览器环境对象
|
|
722
|
+
|
|
723
|
+
模拟完整的浏览器环境(补环境必备):
|
|
724
|
+
|
|
725
|
+
```python
|
|
726
|
+
import never_jscore
|
|
727
|
+
|
|
728
|
+
ctx = never_jscore.Context()
|
|
729
|
+
|
|
730
|
+
# navigator - 浏览器信息
|
|
731
|
+
result = ctx.evaluate("""
|
|
732
|
+
JSON.stringify({
|
|
733
|
+
userAgent: navigator.userAgent,
|
|
734
|
+
platform: navigator.platform,
|
|
735
|
+
language: navigator.language,
|
|
736
|
+
onLine: navigator.onLine,
|
|
737
|
+
cookieEnabled: navigator.cookieEnabled,
|
|
738
|
+
hardwareConcurrency: navigator.hardwareConcurrency
|
|
739
|
+
})
|
|
740
|
+
""")
|
|
741
|
+
print(result)
|
|
742
|
+
# {"userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","platform":"Win32",...}
|
|
743
|
+
|
|
744
|
+
# location - URL 信息
|
|
745
|
+
result = ctx.evaluate("""
|
|
746
|
+
JSON.stringify({
|
|
747
|
+
href: location.href,
|
|
748
|
+
protocol: location.protocol,
|
|
749
|
+
hostname: location.hostname,
|
|
750
|
+
pathname: location.pathname
|
|
751
|
+
})
|
|
752
|
+
""")
|
|
753
|
+
|
|
754
|
+
# document - DOM 文档对象
|
|
755
|
+
result = ctx.evaluate("""
|
|
756
|
+
JSON.stringify({
|
|
757
|
+
readyState: document.readyState,
|
|
758
|
+
title: document.title,
|
|
759
|
+
URL: document.URL,
|
|
760
|
+
domain: document.domain,
|
|
761
|
+
characterSet: document.characterSet
|
|
762
|
+
})
|
|
763
|
+
""")
|
|
764
|
+
|
|
765
|
+
# window - 窗口对象
|
|
766
|
+
result = ctx.evaluate("""
|
|
767
|
+
JSON.stringify({
|
|
768
|
+
innerWidth: window.innerWidth,
|
|
769
|
+
innerHeight: window.innerHeight,
|
|
770
|
+
devicePixelRatio: window.devicePixelRatio
|
|
771
|
+
})
|
|
772
|
+
""")
|
|
773
|
+
|
|
774
|
+
# screen - 屏幕信息
|
|
775
|
+
result = ctx.evaluate("""
|
|
776
|
+
JSON.stringify({
|
|
777
|
+
width: screen.width,
|
|
778
|
+
height: screen.height,
|
|
779
|
+
colorDepth: screen.colorDepth
|
|
780
|
+
})
|
|
781
|
+
""")
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
**可用对象**:
|
|
785
|
+
- `navigator` - 浏览器用户代理信息
|
|
786
|
+
- `location` - 页面 URL 信息
|
|
787
|
+
- `document` - DOM 文档对象(方法返回 null/空数组)
|
|
788
|
+
- `window` - 窗口对象
|
|
789
|
+
- `screen` - 屏幕信息
|
|
790
|
+
|
|
791
|
+
### 5. URL / URLSearchParams
|
|
792
|
+
|
|
793
|
+
完整的 URL 处理 API:
|
|
794
|
+
|
|
795
|
+
```python
|
|
796
|
+
import never_jscore
|
|
797
|
+
|
|
798
|
+
ctx = never_jscore.Context()
|
|
799
|
+
|
|
800
|
+
# URL 解析
|
|
801
|
+
result = ctx.evaluate("""
|
|
802
|
+
const url = new URL('https://api.example.com:8080/search?q=test&page=1#results');
|
|
803
|
+
|
|
804
|
+
JSON.stringify({
|
|
805
|
+
href: url.href,
|
|
806
|
+
protocol: url.protocol,
|
|
807
|
+
hostname: url.hostname,
|
|
808
|
+
port: url.port,
|
|
809
|
+
pathname: url.pathname,
|
|
810
|
+
search: url.search,
|
|
811
|
+
hash: url.hash,
|
|
812
|
+
origin: url.origin
|
|
813
|
+
})
|
|
814
|
+
""")
|
|
815
|
+
|
|
816
|
+
# URLSearchParams - 查询字符串操作
|
|
817
|
+
result = ctx.evaluate("""
|
|
818
|
+
const params = new URLSearchParams('name=John&age=30');
|
|
819
|
+
|
|
820
|
+
// 获取参数
|
|
821
|
+
const name = params.get('name'); // "John"
|
|
822
|
+
|
|
823
|
+
// 设置参数
|
|
824
|
+
params.set('age', '31');
|
|
825
|
+
|
|
826
|
+
// 追加参数
|
|
827
|
+
params.append('tag', 'developer');
|
|
828
|
+
params.append('tag', 'python');
|
|
829
|
+
|
|
830
|
+
// 获取所有同名参数
|
|
831
|
+
const tags = params.getAll('tag'); // ["developer", "python"]
|
|
832
|
+
|
|
833
|
+
// 转回查询字符串
|
|
834
|
+
const queryString = params.toString(); // "name=John&age=31&tag=developer&tag=python"
|
|
835
|
+
|
|
836
|
+
JSON.stringify({name, tags, queryString});
|
|
837
|
+
""")
|
|
838
|
+
|
|
839
|
+
# URL + URLSearchParams 组合
|
|
840
|
+
result = ctx.evaluate("""
|
|
841
|
+
const url = new URL('https://api.example.com/search');
|
|
842
|
+
url.searchParams.append('q', 'javascript');
|
|
843
|
+
url.searchParams.append('limit', '10');
|
|
844
|
+
|
|
845
|
+
url.href // "https://api.example.com/search?q=javascript&limit=10"
|
|
846
|
+
""")
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
### 6. FormData
|
|
850
|
+
|
|
851
|
+
表单数据处理 API:
|
|
852
|
+
|
|
853
|
+
```python
|
|
854
|
+
import never_jscore
|
|
855
|
+
|
|
856
|
+
ctx = never_jscore.Context()
|
|
857
|
+
|
|
858
|
+
result = ctx.evaluate("""
|
|
859
|
+
const formData = new FormData();
|
|
860
|
+
|
|
861
|
+
// 添加字段
|
|
862
|
+
formData.append('username', 'john_doe');
|
|
863
|
+
formData.append('email', 'john@example.com');
|
|
864
|
+
formData.append('tags', 'js');
|
|
865
|
+
formData.append('tags', 'python');
|
|
866
|
+
|
|
867
|
+
// 获取单个值
|
|
868
|
+
const username = formData.get('username'); // "john_doe"
|
|
869
|
+
|
|
870
|
+
// 获取所有同名值
|
|
871
|
+
const tags = formData.getAll('tags'); // ["js", "python"]
|
|
872
|
+
|
|
873
|
+
// 设置(覆盖)
|
|
874
|
+
formData.set('email', 'new@example.com');
|
|
875
|
+
|
|
876
|
+
// 检查是否存在
|
|
877
|
+
const hasUser = formData.has('username'); // true
|
|
878
|
+
|
|
879
|
+
// 删除
|
|
880
|
+
formData.delete('tags');
|
|
881
|
+
|
|
882
|
+
JSON.stringify({username, tags, hasUser});
|
|
883
|
+
""")
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
### 7. Event / EventTarget
|
|
887
|
+
|
|
888
|
+
完整的事件系统:
|
|
889
|
+
|
|
890
|
+
```python
|
|
891
|
+
import never_jscore
|
|
892
|
+
|
|
893
|
+
ctx = never_jscore.Context()
|
|
894
|
+
|
|
895
|
+
result = ctx.evaluate("""
|
|
896
|
+
// 创建事件目标
|
|
897
|
+
const target = new EventTarget();
|
|
898
|
+
|
|
899
|
+
let eventData = [];
|
|
900
|
+
|
|
901
|
+
// 添加事件监听器
|
|
902
|
+
target.addEventListener('custom', (event) => {
|
|
903
|
+
eventData.push({
|
|
904
|
+
type: event.type,
|
|
905
|
+
bubbles: event.bubbles,
|
|
906
|
+
cancelable: event.cancelable
|
|
907
|
+
});
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
// 创建并分发事件
|
|
911
|
+
const event = new Event('custom', {
|
|
912
|
+
bubbles: true,
|
|
913
|
+
cancelable: true
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
target.dispatchEvent(event);
|
|
917
|
+
|
|
918
|
+
// 支持 once 选项
|
|
919
|
+
target.addEventListener('once-event', () => {
|
|
920
|
+
eventData.push('fired once');
|
|
921
|
+
}, { once: true });
|
|
922
|
+
|
|
923
|
+
const onceEvent = new Event('once-event');
|
|
924
|
+
target.dispatchEvent(onceEvent);
|
|
925
|
+
target.dispatchEvent(onceEvent); // 第二次不会触发
|
|
926
|
+
|
|
927
|
+
JSON.stringify(eventData);
|
|
928
|
+
""")
|
|
929
|
+
```
|
|
930
|
+
|
|
931
|
+
### 8. XMLHttpRequest
|
|
932
|
+
|
|
933
|
+
传统 AJAX API(基于 fetch 实现):
|
|
934
|
+
|
|
935
|
+
```python
|
|
936
|
+
import never_jscore
|
|
937
|
+
|
|
938
|
+
ctx = never_jscore.Context()
|
|
939
|
+
|
|
940
|
+
result = ctx.evaluate("""
|
|
941
|
+
(async () => {
|
|
942
|
+
return new Promise((resolve, reject) => {
|
|
943
|
+
const xhr = new XMLHttpRequest();
|
|
944
|
+
|
|
945
|
+
xhr.onload = function() {
|
|
946
|
+
if (xhr.status === 200) {
|
|
947
|
+
resolve({
|
|
948
|
+
status: xhr.status,
|
|
949
|
+
statusText: xhr.statusText,
|
|
950
|
+
responseText: xhr.responseText.substring(0, 100),
|
|
951
|
+
readyState: xhr.readyState
|
|
952
|
+
});
|
|
953
|
+
} else {
|
|
954
|
+
reject('Error: ' + xhr.status);
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
xhr.onerror = function() {
|
|
959
|
+
reject('Network error');
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
// 发送请求
|
|
963
|
+
xhr.open('GET', 'https://httpbin.org/get');
|
|
964
|
+
xhr.setRequestHeader('X-Custom-Header', 'value');
|
|
965
|
+
xhr.send();
|
|
966
|
+
});
|
|
967
|
+
})()
|
|
968
|
+
""")
|
|
969
|
+
|
|
970
|
+
print(result)
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
**支持的功能**:
|
|
974
|
+
- ✅ open() / send() / abort()
|
|
975
|
+
- ✅ setRequestHeader() / getResponseHeader()
|
|
976
|
+
- ✅ onload / onerror / onreadystatechange 事件
|
|
977
|
+
- ✅ readyState / status / statusText
|
|
978
|
+
- ✅ responseText / response
|
|
979
|
+
- ✅ addEventListener / removeEventListener
|
|
980
|
+
|
|
981
|
+
## 内置 Web API 扩展(v2.0+)
|
|
982
|
+
|
|
983
|
+
never_jscore 内置了常用的 Web API,**无需额外 polyfill**,开箱即用!
|
|
984
|
+
|
|
985
|
+
### Crypto APIs(加密相关)
|
|
986
|
+
|
|
987
|
+
```python
|
|
988
|
+
import never_jscore
|
|
989
|
+
|
|
990
|
+
ctx = never_jscore.Context(enable_extensions=True)
|
|
991
|
+
|
|
992
|
+
# Base64 编解码
|
|
993
|
+
result = ctx.evaluate("btoa('Hello World')") # "SGVsbG8gV29ybGQ="
|
|
994
|
+
result = ctx.evaluate("atob('SGVsbG8gV29ybGQ=')") # "Hello World"
|
|
995
|
+
|
|
996
|
+
# 哈希函数
|
|
997
|
+
result = ctx.evaluate("md5('test')") # "098f6bcd4621d373cade4e832627b4f6"
|
|
998
|
+
result = ctx.evaluate("sha256('test')") # "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
|
|
999
|
+
|
|
1000
|
+
# HMAC
|
|
1001
|
+
result = ctx.evaluate("CryptoUtils.hmacSha256('key', 'message')")
|
|
1002
|
+
|
|
1003
|
+
# Hex 编解码
|
|
1004
|
+
result = ctx.evaluate("CryptoUtils.hexEncode('test')") # "74657374"
|
|
1005
|
+
result = ctx.evaluate("CryptoUtils.hexDecode('74657374')") # "test"
|
|
1006
|
+
|
|
1007
|
+
# 链式 API(类 Node.js crypto)
|
|
1008
|
+
result = ctx.evaluate("""
|
|
1009
|
+
CryptoUtils.createHash('sha256')
|
|
1010
|
+
.update('hello')
|
|
1011
|
+
.update(' world')
|
|
1012
|
+
.digest('hex')
|
|
1013
|
+
""")
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
### URL 编码
|
|
1017
|
+
|
|
1018
|
+
```python
|
|
1019
|
+
# URL 编码(兼容浏览器 API)
|
|
1020
|
+
result = ctx.evaluate("encodeURIComponent('hello world')") # "hello%20world"
|
|
1021
|
+
result = ctx.evaluate("decodeURIComponent('hello%20world')") # "hello world"
|
|
1022
|
+
```
|
|
1023
|
+
|
|
1024
|
+
### 随机数生成
|
|
1025
|
+
|
|
1026
|
+
```python
|
|
1027
|
+
# UUID 生成
|
|
1028
|
+
uuid = ctx.evaluate("crypto.randomUUID()") # "a3111236-1431-4d0d-807e-6c7b388d4433"
|
|
1029
|
+
|
|
1030
|
+
# 随机数组
|
|
1031
|
+
result = ctx.evaluate("""
|
|
1032
|
+
const arr = new Uint8Array(16);
|
|
1033
|
+
crypto.getRandomValues(arr);
|
|
1034
|
+
Array.from(arr)
|
|
1035
|
+
""") # [123, 45, 67, ...]
|
|
1036
|
+
|
|
1037
|
+
# 随机浮点数
|
|
1038
|
+
result = ctx.evaluate("Deno.core.ops.op_crypto_random()") # 0.123456789
|
|
1039
|
+
```
|
|
1040
|
+
|
|
1041
|
+
### 定时器(立即执行版本)
|
|
1042
|
+
|
|
1043
|
+
```python
|
|
1044
|
+
# setTimeout/setInterval(用于 API 检测,立即执行)
|
|
1045
|
+
result = ctx.evaluate("""
|
|
1046
|
+
let value = 0;
|
|
1047
|
+
setTimeout(() => { value = 42; }, 100); // 立即执行
|
|
1048
|
+
value // 需要等待 Promise
|
|
1049
|
+
""")
|
|
1050
|
+
```
|
|
1051
|
+
|
|
1052
|
+
⚠️ **注意**:setTimeout/setInterval 是**假的异步**(立即执行),仅用于通过 JS 代码中的 API 存在性检测。
|
|
1053
|
+
|
|
1054
|
+
### Worker API(单线程模拟)
|
|
1055
|
+
|
|
1056
|
+
```python
|
|
1057
|
+
# Worker API(用于兼容检测,单线程模拟)
|
|
1058
|
+
result = ctx.evaluate("""
|
|
1059
|
+
typeof Worker === 'function' &&
|
|
1060
|
+
typeof Worker.prototype.postMessage === 'function'
|
|
1061
|
+
""") # True
|
|
1062
|
+
```
|
|
1063
|
+
|
|
1064
|
+
⚠️ **注意**:Worker 是**单线程模拟**,不会真正创建多线程,仅用于通过代码检测。
|
|
1065
|
+
|
|
1066
|
+
### 禁用扩展(纯净 V8)
|
|
1067
|
+
|
|
1068
|
+
如果不需要这些 API,可以禁用扩展:
|
|
1069
|
+
|
|
1070
|
+
```python
|
|
1071
|
+
ctx = never_jscore.Context(enable_extensions=False)
|
|
1072
|
+
|
|
1073
|
+
# 此时只有 ECMAScript 标准 API
|
|
1074
|
+
result = ctx.evaluate("typeof btoa") # "undefined"
|
|
1075
|
+
result = ctx.evaluate("JSON.stringify({a: 1})") # '{"a":1}' - 标准 API 可用
|
|
1076
|
+
```
|
|
1077
|
+
|
|
1078
|
+
## 适用场景
|
|
1079
|
+
|
|
1080
|
+
### JavaScript 逆向分析(推荐)
|
|
1081
|
+
|
|
1082
|
+
```python
|
|
1083
|
+
import never_jscore
|
|
1084
|
+
|
|
1085
|
+
# 加载目标网站的加密 JS
|
|
1086
|
+
with open("target_crypto.js") as f:
|
|
1087
|
+
js_code = f.read()
|
|
1088
|
+
|
|
1089
|
+
ctx = never_jscore.Context(enable_extensions=True) # 启用内置 Web API
|
|
1090
|
+
ctx.compile(js_code)
|
|
1091
|
+
|
|
1092
|
+
# 直接调用加密函数(无需额外 polyfill)
|
|
1093
|
+
encrypted = ctx.call("encrypt", [plain_text, key])
|
|
1094
|
+
|
|
1095
|
+
# 如果 JS 代码使用了 btoa、md5、sha256 等,都可以直接使用
|
|
1096
|
+
result = ctx.evaluate("btoa(md5('test'))")
|
|
1097
|
+
```
|
|
1098
|
+
|
|
1099
|
+
### 真实场景:API 签名生成
|
|
1100
|
+
|
|
1101
|
+
```python
|
|
1102
|
+
import never_jscore
|
|
1103
|
+
|
|
1104
|
+
ctx = never_jscore.Context(enable_extensions=True)
|
|
1105
|
+
|
|
1106
|
+
# 典型的签名算法(无需 polyfill,直接运行)
|
|
1107
|
+
ctx.compile("""
|
|
1108
|
+
function generateSignature(params, secret) {
|
|
1109
|
+
// 1. 参数排序
|
|
1110
|
+
const keys = Object.keys(params).sort();
|
|
1111
|
+
|
|
1112
|
+
// 2. 拼接查询字符串
|
|
1113
|
+
const query = keys.map(k =>
|
|
1114
|
+
encodeURIComponent(k) + '=' + encodeURIComponent(params[k])
|
|
1115
|
+
).join('&');
|
|
1116
|
+
|
|
1117
|
+
// 3. 添加密钥并计算 HMAC
|
|
1118
|
+
const message = query + '&key=' + secret;
|
|
1119
|
+
const signature = CryptoUtils.hmacSha256(secret, message);
|
|
1120
|
+
|
|
1121
|
+
// 4. Base64 编码
|
|
1122
|
+
return btoa(signature);
|
|
1123
|
+
}
|
|
1124
|
+
""")
|
|
1125
|
+
|
|
1126
|
+
# 调用签名函数
|
|
1127
|
+
signature = ctx.call("generateSignature", [
|
|
1128
|
+
{"user": "test", "timestamp": "1234567890"},
|
|
1129
|
+
"my-secret-key"
|
|
1130
|
+
])
|
|
1131
|
+
print(f"Signature: {signature}")
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
### 异步数据处理
|
|
1135
|
+
|
|
1136
|
+
```python
|
|
1137
|
+
import never_jscore
|
|
1138
|
+
|
|
1139
|
+
ctx = never_jscore.Context()
|
|
1140
|
+
ctx.compile("""
|
|
1141
|
+
async function processBatch(items) {
|
|
1142
|
+
const results = await Promise.all(
|
|
1143
|
+
items.map(async item => {
|
|
1144
|
+
// 模拟异步处理
|
|
1145
|
+
return await Promise.resolve(item * 2);
|
|
1146
|
+
})
|
|
1147
|
+
);
|
|
1148
|
+
return results;
|
|
1149
|
+
}
|
|
1150
|
+
""")
|
|
1151
|
+
|
|
1152
|
+
result = ctx.call("processBatch", [[1, 2, 3, 4, 5]])
|
|
1153
|
+
print(result) # [2, 4, 6, 8, 10]
|
|
1154
|
+
```
|
|
1155
|
+
|
|
1156
|
+
## 性能优化建议
|
|
1157
|
+
|
|
1158
|
+
1. **重用 Context**: 对于需要多次调用的代码,创建一个 Context 并复用
|
|
1159
|
+
2. **批量处理**: 在 JavaScript 端批量处理数据,减少 Python-JS 调用次数
|
|
1160
|
+
3. **单 Context 模式**: 尽可能将所有函数定义在一个 Context 中
|
|
1161
|
+
4. **合理使用 auto_await**: 对于不需要等待的同步代码,设置 `auto_await=False` 可略微提升性能
|
|
1162
|
+
5. **避免频繁创建 Context**: 创建 Context 有开销,应尽量复用
|
|
1163
|
+
|
|
1164
|
+
## 模块化架构
|
|
1165
|
+
|
|
1166
|
+
项目采用清晰的模块化设计:
|
|
1167
|
+
|
|
1168
|
+
```
|
|
1169
|
+
src/
|
|
1170
|
+
├── lib.rs # 模块入口,仅导出 Context 类
|
|
1171
|
+
├── context.rs # Context 实现(V8 isolate 封装)
|
|
1172
|
+
├── runtime.rs # V8/Tokio runtime 管理
|
|
1173
|
+
├── convert.rs # Python ↔ JavaScript 类型转换
|
|
1174
|
+
├── storage.rs # 结果存储
|
|
1175
|
+
├── fs_ops.rs # 文件系统操作(11 个操作)
|
|
1176
|
+
├── fetch_ops.rs # HTTP 请求(基于 reqwest)
|
|
1177
|
+
├── crypto_ops.rs # 加密操作扩展(Base64、Hash、HMAC、Random)
|
|
1178
|
+
├── encoding_ops.rs # URL 编码扩展
|
|
1179
|
+
├── timer_ops.rs # 定时器扩展(setTimeout/setInterval)
|
|
1180
|
+
├── worker_ops.rs # Worker API 扩展
|
|
1181
|
+
├── ops/ # 新增 ops 模块
|
|
1182
|
+
│ ├── mod.rs # 模块导出
|
|
1183
|
+
│ ├── web_storage.rs # localStorage/sessionStorage(12 个操作)
|
|
1184
|
+
│ └── browser_env.rs # 浏览器环境对象(9 个操作)
|
|
1185
|
+
└── dddd_js/
|
|
1186
|
+
└── js_polyfill.js # JavaScript polyfill 层(自动注入,1660+ 行)
|
|
1187
|
+
|
|
1188
|
+
tests/
|
|
1189
|
+
├── test_all_features.py # 完整功能测试套件
|
|
1190
|
+
├── test_browser_apis.py # 浏览器 API 测试
|
|
1191
|
+
├── test_high_priority_apis.py # 高优先级 API 测试
|
|
1192
|
+
├── test_wasm.py # WebAssembly 测试
|
|
1193
|
+
├── test_async_simple.py # 异步功能测试
|
|
1194
|
+
├── test_extensions.py # 扩展 API 测试
|
|
1195
|
+
└── test_new_apis.py # 新 API 测试
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
### 扩展系统架构
|
|
1199
|
+
|
|
1200
|
+
- **Rust 层**: 使用 Deno Core 的 `#[op2]` 宏定义底层操作
|
|
1201
|
+
- **JavaScript 层**: 在 `js_polyfill.js` 中封装为标准 Web API
|
|
1202
|
+
- **自动注入**: `enable_extensions=True` 时自动加载所有扩展
|
|
1203
|
+
|
|
1204
|
+
## 测试
|
|
1205
|
+
|
|
1206
|
+
```bash
|
|
1207
|
+
# 基础功能测试
|
|
1208
|
+
python test_async_simple.py
|
|
1209
|
+
|
|
1210
|
+
# Web API 扩展测试
|
|
1211
|
+
python test_extensions.py
|
|
1212
|
+
python test_new_apis.py
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
## 技术细节
|
|
1216
|
+
|
|
1217
|
+
- **V8 引擎**: 使用 Deno Core 提供的 V8 bindings
|
|
1218
|
+
- **Tokio Runtime**: 全局单线程 runtime,支持异步操作
|
|
1219
|
+
- **类型转换**: Python ↔ JSON ↔ JavaScript 三层转换
|
|
1220
|
+
- **内存管理**: 使用 `std::mem::forget()` 避免 HandleScope 错误,每 100 次执行提示 GC
|
|
1221
|
+
- **扩展系统**: 基于 Deno Core extension 机制,模块化设计
|
|
1222
|
+
- **依赖库**:
|
|
1223
|
+
- `deno_core 0.367.0`: V8 运行时
|
|
1224
|
+
- `pyo3 0.27.1`: Python 绑定(abi3-py38)
|
|
1225
|
+
- `tokio 1.48`: 异步运行时
|
|
1226
|
+
- `reqwest 0.12`: HTTP 客户端(支持 JSON 和 blocking)
|
|
1227
|
+
- `lazy_static 1.4`: 全局状态管理(localStorage/sessionStorage)
|
|
1228
|
+
- `rand 0.8`: 随机数生成
|
|
1229
|
+
- `base64`, `md-5`, `sha1`, `sha2`, `hmac`: 加密库
|
|
1230
|
+
- `urlencoding`, `percent-encoding`: URL 编解码
|
|
1231
|
+
|
|
1232
|
+
## 许可证
|
|
1233
|
+
|
|
1234
|
+
MIT License
|
|
1235
|
+
|
|
1236
|
+
## 贡献
|
|
1237
|
+
|
|
1238
|
+
欢迎提交 Issue 和 Pull Request!
|
|
1239
|
+
|
|
1240
|
+
## 相关项目
|
|
1241
|
+
|
|
1242
|
+
- [py_mini_racer](https://github.com/sqreen/PyMiniRacer) - Python MiniRacer 实现
|
|
1243
|
+
- [PyExecJS](https://github.com/doloopwhile/PyExecJS) - 原 Python ExecJS 实现
|
|
1244
|
+
- [Deno](https://github.com/denoland/deno) - 现代 JavaScript/TypeScript 运行时
|
|
1245
|
+
- [PyO3](https://github.com/PyO3/pyo3) - Rust Python bindings
|
|
1246
|
+
|
|
1247
|
+
## 更新日志
|
|
1248
|
+
|
|
1249
|
+
### v2.2.0 (2025-11-11) - 重大功能扩展
|
|
1250
|
+
|
|
1251
|
+
#### Node.js 环境 API
|
|
1252
|
+
- ✨ **require()**: 完整的 CommonJS 模块系统
|
|
1253
|
+
- 相对/绝对路径模块加载
|
|
1254
|
+
- node_modules 递归查找
|
|
1255
|
+
- package.json 主入口解析
|
|
1256
|
+
- 模块缓存机制
|
|
1257
|
+
- ✨ **fs 模块**: 文件系统操作(readFileSync, writeFileSync, existsSync 等)
|
|
1258
|
+
- ✨ **path 模块**: 路径处理(resolve, join, dirname, basename 等)
|
|
1259
|
+
- ✨ **fetch()**: 现代 HTTP API
|
|
1260
|
+
- 支持所有 HTTP 方法(GET/POST/PUT/DELETE 等)
|
|
1261
|
+
- 自定义请求头
|
|
1262
|
+
- JSON 自动序列化/反序列化
|
|
1263
|
+
- Response/Headers 对象
|
|
1264
|
+
- 超时控制
|
|
1265
|
+
|
|
1266
|
+
#### 浏览器环境 API(补环境)
|
|
1267
|
+
- ✨ **localStorage/sessionStorage**: 浏览器存储 API
|
|
1268
|
+
- setItem/getItem/removeItem/clear
|
|
1269
|
+
- key() 和 length 属性
|
|
1270
|
+
- 线程安全全局存储
|
|
1271
|
+
- ✨ **浏览器环境对象**:
|
|
1272
|
+
- `navigator`: 用户代理、平台、语言等信息
|
|
1273
|
+
- `location`: URL 解析(href, protocol, hostname 等)
|
|
1274
|
+
- `document`: DOM 文档对象(readyState, title, URL 等)
|
|
1275
|
+
- `window`: 窗口属性(innerWidth, innerHeight 等)
|
|
1276
|
+
- `screen`: 屏幕信息(width, height, colorDepth 等)
|
|
1277
|
+
- ✨ **URL/URLSearchParams**: 完整的 URL 处理
|
|
1278
|
+
- URL 解析和构造
|
|
1279
|
+
- 查询参数操作(get/set/append/delete)
|
|
1280
|
+
- 迭代器支持
|
|
1281
|
+
- ✨ **FormData**: 表单数据处理
|
|
1282
|
+
- append/get/set/delete 方法
|
|
1283
|
+
- getAll() 获取所有同名字段
|
|
1284
|
+
- 迭代器支持
|
|
1285
|
+
- ✨ **Event/EventTarget**: 完整的事件系统
|
|
1286
|
+
- Event 类(preventDefault, stopPropagation)
|
|
1287
|
+
- EventTarget 类(addEventListener, removeEventListener, dispatchEvent)
|
|
1288
|
+
- 事件阶段常量和选项(once, capture)
|
|
1289
|
+
- ✨ **XMLHttpRequest**: 传统 AJAX API
|
|
1290
|
+
- 基于 fetch() 实现
|
|
1291
|
+
- 完整的状态管理(UNSENT/OPENED/HEADERS_RECEIVED/LOADING/DONE)
|
|
1292
|
+
- 事件处理器(onload, onerror, onreadystatechange)
|
|
1293
|
+
- 请求头操作
|
|
1294
|
+
|
|
1295
|
+
#### 新增依赖
|
|
1296
|
+
- `reqwest 0.12`: HTTP 客户端库(blocking 模式)
|
|
1297
|
+
- `lazy_static 1.4`: 全局状态管理
|
|
1298
|
+
|
|
1299
|
+
#### 代码增量
|
|
1300
|
+
- **Rust 代码**: ~800 行新增代码
|
|
1301
|
+
- fs_ops.rs (11 个操作)
|
|
1302
|
+
- fetch_ops.rs (3 个操作)
|
|
1303
|
+
- web_storage.rs (12 个操作)
|
|
1304
|
+
- browser_env.rs (9 个操作)
|
|
1305
|
+
- **JavaScript 代码**: ~890 行新增代码
|
|
1306
|
+
- require() 实现(~200 行)
|
|
1307
|
+
- fetch/Response/Headers(~60 行)
|
|
1308
|
+
- localStorage/sessionStorage(~50 行)
|
|
1309
|
+
- URL/URLSearchParams(~160 行)
|
|
1310
|
+
- FormData(~80 行)
|
|
1311
|
+
- Event/EventTarget(~130 行)
|
|
1312
|
+
- XMLHttpRequest(~180 行)
|
|
1313
|
+
- 浏览器环境对象(~40 行)
|
|
1314
|
+
|
|
1315
|
+
#### 测试覆盖
|
|
1316
|
+
- ✅ test_all_features.py: 7 个综合测试(100% 通过)
|
|
1317
|
+
- ✅ test_browser_apis.py: 浏览器 API 完整测试
|
|
1318
|
+
- ✅ test_high_priority_apis.py: 高优先级 API 测试
|
|
1319
|
+
- ✅ test_wasm.py: WebAssembly 验证测试
|
|
1320
|
+
|
|
1321
|
+
### v2.0.0 (2025-11-05)
|
|
1322
|
+
|
|
1323
|
+
#### 架构重构
|
|
1324
|
+
- 🔄 **架构重构**: 改为 py_mini_racer 风格的实例化 API
|
|
1325
|
+
- ✅ 修复 HandleScope 错误:使用 `std::mem::forget()` 管理 v8::Global
|
|
1326
|
+
- ✅ 明确 LIFO 清理顺序要求
|
|
1327
|
+
- ✅ 完善多 Context 使用限制说明
|
|
1328
|
+
- ✅ 新增 `compile()` 便捷方法
|
|
1329
|
+
- ✅ 新增 `evaluate()` 独立求值方法
|
|
1330
|
+
|
|
1331
|
+
#### Web API 扩展(全新)
|
|
1332
|
+
- ✨ **Crypto APIs**:
|
|
1333
|
+
- Base64: `btoa()`, `atob()`
|
|
1334
|
+
- 哈希: `md5()`, `sha1()`, `sha256()`, `sha512()`
|
|
1335
|
+
- HMAC: `hmacMd5()`, `hmacSha1()`, `hmacSha256()`
|
|
1336
|
+
- Hex: `hexEncode()`, `hexDecode()`
|
|
1337
|
+
- 链式 API: `CryptoUtils.createHash()`, `CryptoUtils.createHmac()`
|
|
1338
|
+
- ✨ **URL 编码**: `encodeURIComponent()`, `decodeURIComponent()`, `encodeURI()`, `decodeURI()`
|
|
1339
|
+
- ✨ **定时器**: `setTimeout()`, `setInterval()`, `clearTimeout()`, `clearInterval()` (立即执行版本)
|
|
1340
|
+
- ✨ **Worker API**: `Worker` 类(单线程模拟版本)
|
|
1341
|
+
- ✨ **随机数**: `crypto.randomUUID()`, `crypto.getRandomValues()`, 随机数 ops
|
|
1342
|
+
- 🎯 **扩展控制**: `Context(enable_extensions=True/False)` 可选启用/禁用
|
|
1343
|
+
- 📦 **自动注入**: 无需手动加载 polyfill,开箱即用
|
|
1344
|
+
|
|
1345
|
+
#### 性能优化
|
|
1346
|
+
- ⚡ 扩展模块采用 Rust 实现,性能接近原生
|
|
1347
|
+
- ⚡ JavaScript polyfill 层仅在必要时使用
|
|
1348
|
+
|
|
1349
|
+
### v0.1.0 (2025-11-01)
|
|
1350
|
+
|
|
1351
|
+
- ✅ 基于 Deno Core 的 JavaScript 执行
|
|
1352
|
+
- ✅ Promise/async/await 完整支持
|
|
1353
|
+
- ✅ Python ↔ JavaScript 类型自动转换
|
|
1354
|
+
- ✅ 模块化代码架构
|
|
1355
|
+
- ✅ 完整的类型提示支持
|
|
1356
|
+
- ✅ 性能监控和统计
|
|
1357
|
+
|