never-jscore 1.0.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.
Potentially problematic release.
This version of never-jscore might be problematic. Click here for more details.
- never_jscore/__init__.py +5 -0
- never_jscore/__init__.pyi +223 -0
- never_jscore/never_jscore.pyd +0 -0
- never_jscore/py.typed +0 -0
- never_jscore-1.0.0.dist-info/METADATA +472 -0
- never_jscore-1.0.0.dist-info/RECORD +9 -0
- never_jscore-1.0.0.dist-info/WHEEL +4 -0
- never_jscore.pyi +223 -0
- py.typed +2 -0
never_jscore/__init__.py
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyExecJS-RS - 基于 Deno Core 的 JavaScript 运行时
|
|
3
|
+
|
|
4
|
+
完整支持 Promise 和 async/await,适合 JS 逆向分析。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, List, Union
|
|
8
|
+
|
|
9
|
+
class Context:
|
|
10
|
+
"""
|
|
11
|
+
JavaScript 执行上下文(支持异步)
|
|
12
|
+
|
|
13
|
+
每个 Context 包含一个独立的 V8 isolate 和 JavaScript 运行时环境。
|
|
14
|
+
默认自动等待 Promise,可以无缝调用异步 JavaScript 函数。
|
|
15
|
+
|
|
16
|
+
注意:由于 V8 限制,多个 Context 不能交叉使用。
|
|
17
|
+
建议在一个 Context 中定义所有需要的函数。
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
>>> # 同步函数
|
|
21
|
+
>>> ctx = compile("function add(a, b) { return a + b; }")
|
|
22
|
+
>>> result = ctx.call("add", [1, 2])
|
|
23
|
+
>>> print(result)
|
|
24
|
+
3
|
|
25
|
+
|
|
26
|
+
>>> # 异步函数(自动等待)
|
|
27
|
+
>>> ctx = compile("async function asyncAdd(a, b) { return a + b; }")
|
|
28
|
+
>>> result = ctx.call("asyncAdd", [5, 3])
|
|
29
|
+
>>> print(result)
|
|
30
|
+
8
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def call(self, name: str, args: List[Any], auto_await: bool = True) -> Any:
|
|
34
|
+
"""
|
|
35
|
+
调用 JavaScript 函数(支持 Promise)
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
name: 函数名称
|
|
39
|
+
args: 参数列表
|
|
40
|
+
auto_await: 是否自动等待 Promise(默认 True)
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
函数返回值,自动转换为 Python 对象
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
Exception: 当函数调用失败时
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
>>> ctx = compile("async function decrypt(data) { return data.split('').reverse().join(''); }")
|
|
50
|
+
>>> result = ctx.call("decrypt", ["olleh"])
|
|
51
|
+
>>> print(result)
|
|
52
|
+
hello
|
|
53
|
+
"""
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
def eval(self, code: str, auto_await: bool = True) -> Any:
|
|
57
|
+
"""
|
|
58
|
+
在当前上下文中执行 JavaScript 代码(支持 Promise)
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
code: JavaScript 代码字符串
|
|
62
|
+
auto_await: 是否自动等待 Promise(默认 True)
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
执行结果,自动转换为 Python 对象
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
Exception: 当代码执行失败时
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
>>> # 同步执行
|
|
72
|
+
>>> ctx = compile("var x = 10;")
|
|
73
|
+
>>> result = ctx.eval("x * 2")
|
|
74
|
+
>>> print(result)
|
|
75
|
+
20
|
|
76
|
+
|
|
77
|
+
>>> # Promise(自动等待)
|
|
78
|
+
>>> result = ctx.eval("Promise.resolve(42)")
|
|
79
|
+
>>> print(result)
|
|
80
|
+
42
|
|
81
|
+
"""
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
def gc(self) -> None:
|
|
85
|
+
"""
|
|
86
|
+
请求 V8 垃圾回收
|
|
87
|
+
|
|
88
|
+
注意:这只是向 V8 发送 GC 请求,V8 会根据自己的策略决定是否执行。
|
|
89
|
+
在大多数情况下,V8 的自动 GC 已经足够好,无需手动调用。
|
|
90
|
+
"""
|
|
91
|
+
...
|
|
92
|
+
|
|
93
|
+
def get_stats(self) -> tuple[int]:
|
|
94
|
+
"""
|
|
95
|
+
获取执行统计信息
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
包含执行次数的元组 (exec_count,)
|
|
99
|
+
"""
|
|
100
|
+
...
|
|
101
|
+
|
|
102
|
+
def reset_stats(self) -> None:
|
|
103
|
+
"""
|
|
104
|
+
重置统计信息
|
|
105
|
+
"""
|
|
106
|
+
...
|
|
107
|
+
|
|
108
|
+
def compile(code: str) -> Context:
|
|
109
|
+
"""
|
|
110
|
+
编译 JavaScript 代码并返回执行上下文
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
code: JavaScript 代码字符串
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Context 对象,可用于调用函数和执行代码
|
|
117
|
+
|
|
118
|
+
Raises:
|
|
119
|
+
Exception: 当代码编译失败时
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
>>> ctx = compile('''
|
|
123
|
+
... function greet(name) {
|
|
124
|
+
... return "Hello, " + name + "!";
|
|
125
|
+
... }
|
|
126
|
+
... ''')
|
|
127
|
+
>>> result = ctx.call("greet", ["World"])
|
|
128
|
+
>>> print(result)
|
|
129
|
+
Hello, World!
|
|
130
|
+
"""
|
|
131
|
+
...
|
|
132
|
+
|
|
133
|
+
def eval(code: str, auto_await: bool = True) -> Any:
|
|
134
|
+
"""
|
|
135
|
+
直接执行 JavaScript 代码并返回结果(支持 Promise)
|
|
136
|
+
|
|
137
|
+
默认自动等待 Promise。适合执行简单的一次性代码。
|
|
138
|
+
对于需要多次调用的场景,建议使用 compile() + Context.call()。
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
code: JavaScript 代码字符串
|
|
142
|
+
auto_await: 是否自动等待 Promise(默认 True)
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
执行结果,自动转换为 Python 对象
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
Exception: 当代码执行失败时
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
>>> # 同步代码
|
|
152
|
+
>>> result = eval("1 + 2 + 3")
|
|
153
|
+
>>> print(result)
|
|
154
|
+
6
|
|
155
|
+
|
|
156
|
+
>>> # Promise(自动等待)
|
|
157
|
+
>>> result = eval("Promise.resolve(42)")
|
|
158
|
+
>>> print(result)
|
|
159
|
+
42
|
|
160
|
+
|
|
161
|
+
>>> # async 函数
|
|
162
|
+
>>> result = eval("(async () => { return await Promise.resolve(100); })()")
|
|
163
|
+
>>> print(result)
|
|
164
|
+
100
|
|
165
|
+
"""
|
|
166
|
+
...
|
|
167
|
+
|
|
168
|
+
def compile_file(path: str) -> Context:
|
|
169
|
+
"""
|
|
170
|
+
从文件读取并编译 JavaScript 代码
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
path: JavaScript 文件路径
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Context 对象
|
|
177
|
+
|
|
178
|
+
Raises:
|
|
179
|
+
Exception: 当文件读取或编译失败时
|
|
180
|
+
|
|
181
|
+
Example:
|
|
182
|
+
>>> # 假设 script.js 包含: function add(a, b) { return a + b; }
|
|
183
|
+
>>> ctx = compile_file("script.js")
|
|
184
|
+
>>> result = ctx.call("add", [5, 3])
|
|
185
|
+
>>> print(result)
|
|
186
|
+
8
|
|
187
|
+
"""
|
|
188
|
+
...
|
|
189
|
+
|
|
190
|
+
def eval_file(path: str, auto_await: bool = True) -> Any:
|
|
191
|
+
"""
|
|
192
|
+
从文件读取并执行 JavaScript 代码(支持 Promise)
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
path: JavaScript 文件路径
|
|
196
|
+
auto_await: 是否自动等待 Promise(默认 True)
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
执行结果
|
|
200
|
+
|
|
201
|
+
Raises:
|
|
202
|
+
Exception: 当文件读取或执行失败时
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
>>> result = eval_file("script.js")
|
|
206
|
+
>>> print(result)
|
|
207
|
+
"""
|
|
208
|
+
...
|
|
209
|
+
|
|
210
|
+
# 类型别名
|
|
211
|
+
JSValue = Union[None, bool, int, float, str, List[Any], dict[str, Any]]
|
|
212
|
+
"""JavaScript 值的 Python 类型表示"""
|
|
213
|
+
|
|
214
|
+
__version__: str
|
|
215
|
+
"""模块版本号"""
|
|
216
|
+
|
|
217
|
+
__all__ = [
|
|
218
|
+
"Context",
|
|
219
|
+
"compile",
|
|
220
|
+
"eval",
|
|
221
|
+
"compile_file",
|
|
222
|
+
"eval_file",
|
|
223
|
+
]
|
|
Binary file
|
never_jscore/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: never-jscore
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Rust
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Summary: Fast JavaScript execution in Python using Deno Core (PyExecJS replacement)
|
|
16
|
+
Keywords: javascript,execjs,deno,v8,js,python,rust
|
|
17
|
+
License: MIT
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
20
|
+
Project-URL: Homepage, https://github.com/neverl805/never-jscore
|
|
21
|
+
Project-URL: Repository, https://github.com/neverl805/never-jscore
|
|
22
|
+
|
|
23
|
+
# never_jscore 中文文档
|
|
24
|
+
|
|
25
|
+
基于 Deno Core (V8) 的高性能 Python JavaScript 执行引擎。
|
|
26
|
+
|
|
27
|
+
## 项目特点
|
|
28
|
+
|
|
29
|
+
- ⚡ **极致性能**:
|
|
30
|
+
- 使用 Rust + Deno Core (V8 引擎)
|
|
31
|
+
- thread_local Context 缓存优化
|
|
32
|
+
- **比 PyExecJS 快 100-300倍**
|
|
33
|
+
- **与 PyMiniRacer 性能相当,部分场景更快**
|
|
34
|
+
- 🔄 **Promise/async 支持**:
|
|
35
|
+
- 完整支持 Promise 和 async/await
|
|
36
|
+
- 自动等待异步结果
|
|
37
|
+
- **唯一高性能 Promise 方案**(PyMiniRacer 不支持)
|
|
38
|
+
- 📦 **上下文隔离**: 每个 Context 独立的 V8 执行环境,互不干扰
|
|
39
|
+
- 🎯 **PyExecJS 兼容**: API 设计兼容 PyExecJS,可直接替换使用
|
|
40
|
+
- 🧹 **自动内存管理**: 基于 Rust 的自动垃圾回收,无内存泄漏
|
|
41
|
+
- 🛡️ **类型安全**: 提供完整的类型提示(.pyi 文件)
|
|
42
|
+
- 🎮 **JS逆向首选**: 专为 JS 逆向工程优化,支持批量函数调用
|
|
43
|
+
|
|
44
|
+
## 性能对比
|
|
45
|
+
|
|
46
|
+
| 测试项目 | never_jscore | PyMiniRacer | PyExecJS |
|
|
47
|
+
|---------|-------------|-------------|----------|
|
|
48
|
+
| 简单计算 | 0.012ms | 0.005ms | 2.3ms |
|
|
49
|
+
| 字符串操作 | **0.004ms** 🏆 | 0.008ms | 2.3ms |
|
|
50
|
+
| 数组操作 | **0.004ms** 🏆 | 0.006ms | 2.3ms |
|
|
51
|
+
| Promise | **✅ 0.003ms** | ❌ 不支持 | ❌ 不支持 |
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## 安装
|
|
55
|
+
|
|
56
|
+
### 从源码安装
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install maturin
|
|
60
|
+
maturin develop --release
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 快速开始
|
|
64
|
+
|
|
65
|
+
### 1. 基本用法
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import never_jscore as execjs
|
|
69
|
+
|
|
70
|
+
# 一次性执行
|
|
71
|
+
result = execjs.eval("1 + 2 + 3")
|
|
72
|
+
print(result) # 6
|
|
73
|
+
|
|
74
|
+
# 字符串操作
|
|
75
|
+
result = execjs.eval("'Hello'.toUpperCase()")
|
|
76
|
+
print(result) # HELLO
|
|
77
|
+
|
|
78
|
+
# 数组操作
|
|
79
|
+
result = execjs.eval("[1, 2, 3].map(x => x * 2)")
|
|
80
|
+
print(result) # [2, 4, 6]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2. 编译和调用
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# 编译 JavaScript 代码
|
|
87
|
+
ctx = execjs.compile("""
|
|
88
|
+
function add(a, b) {
|
|
89
|
+
return a + b;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function greet(name) {
|
|
93
|
+
return "Hello, " + name + "!";
|
|
94
|
+
}
|
|
95
|
+
""")
|
|
96
|
+
|
|
97
|
+
# 调用函数
|
|
98
|
+
print(ctx.call("add", [5, 3])) # 8
|
|
99
|
+
print(ctx.call("greet", ["World"])) # Hello, World!
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 3. 异步支持(Promise/async/await)
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
# Promise 自动等待
|
|
106
|
+
result = execjs.eval("Promise.resolve(42)")
|
|
107
|
+
print(result) # 42
|
|
108
|
+
|
|
109
|
+
# async 函数
|
|
110
|
+
ctx = execjs.compile("""
|
|
111
|
+
async function fetchData(id) {
|
|
112
|
+
// 模拟异步操作
|
|
113
|
+
return await Promise.resolve({id: id, name: "User" + id});
|
|
114
|
+
}
|
|
115
|
+
""")
|
|
116
|
+
|
|
117
|
+
result = ctx.call("fetchData", [123])
|
|
118
|
+
print(result) # {'id': 123, 'name': 'User123'}
|
|
119
|
+
|
|
120
|
+
# Promise 链
|
|
121
|
+
result = execjs.eval("""
|
|
122
|
+
Promise.resolve(10)
|
|
123
|
+
.then(x => x * 2)
|
|
124
|
+
.then(x => x + 5)
|
|
125
|
+
""")
|
|
126
|
+
print(result) # 25
|
|
127
|
+
|
|
128
|
+
# Promise.all 并发
|
|
129
|
+
result = execjs.eval("""
|
|
130
|
+
Promise.all([
|
|
131
|
+
Promise.resolve(1),
|
|
132
|
+
Promise.resolve(2),
|
|
133
|
+
Promise.resolve(3)
|
|
134
|
+
])
|
|
135
|
+
""")
|
|
136
|
+
print(result) # [1, 2, 3]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 4. 上下文状态管理
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
ctx = execjs.compile("""
|
|
143
|
+
var counter = 0;
|
|
144
|
+
|
|
145
|
+
function increment() {
|
|
146
|
+
return ++counter;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getCounter() {
|
|
150
|
+
return counter;
|
|
151
|
+
}
|
|
152
|
+
""")
|
|
153
|
+
|
|
154
|
+
print(ctx.call("increment", [])) # 1
|
|
155
|
+
print(ctx.call("increment", [])) # 2
|
|
156
|
+
print(ctx.call("getCounter", [])) # 2
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 5. 复杂对象处理
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
ctx = execjs.compile("""
|
|
163
|
+
function processUser(name, age, tags) {
|
|
164
|
+
return {
|
|
165
|
+
name: name,
|
|
166
|
+
age: age,
|
|
167
|
+
isAdult: age >= 18,
|
|
168
|
+
tags: tags,
|
|
169
|
+
summary: name + ' (' + age + '岁)'
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
""")
|
|
173
|
+
|
|
174
|
+
result = ctx.call("processUser", ["张三", 25, ["Python", "JavaScript"]])
|
|
175
|
+
print(result)
|
|
176
|
+
# {
|
|
177
|
+
# 'name': '张三',
|
|
178
|
+
# 'age': 25,
|
|
179
|
+
# 'isAdult': True,
|
|
180
|
+
# 'tags': ['Python', 'JavaScript'],
|
|
181
|
+
# 'summary': '张三 (25岁)'
|
|
182
|
+
# }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 6. 从文件加载
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
# 从文件编译
|
|
189
|
+
ctx = execjs.compile_file("script.js")
|
|
190
|
+
result = ctx.call("myFunction", [arg1, arg2])
|
|
191
|
+
|
|
192
|
+
# 从文件执行
|
|
193
|
+
result = execjs.eval_file("script.js")
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 7. 性能监控
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
ctx = execjs.compile("function compute(n) { return n * n; }")
|
|
200
|
+
|
|
201
|
+
# 执行多次
|
|
202
|
+
for i in range(100):
|
|
203
|
+
ctx.call("compute", [i])
|
|
204
|
+
|
|
205
|
+
# 查看统计
|
|
206
|
+
stats = ctx.get_stats()
|
|
207
|
+
print(f"执行次数: {stats[0]}") # 100
|
|
208
|
+
|
|
209
|
+
# 请求垃圾回收(可选)
|
|
210
|
+
ctx.gc()
|
|
211
|
+
|
|
212
|
+
# 重置统计
|
|
213
|
+
ctx.reset_stats()
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## API 参考
|
|
217
|
+
|
|
218
|
+
### 模块函数
|
|
219
|
+
|
|
220
|
+
#### `compile(code: str) -> Context`
|
|
221
|
+
|
|
222
|
+
编译 JavaScript 代码并返回执行上下文。
|
|
223
|
+
|
|
224
|
+
**参数**:
|
|
225
|
+
- `code`: JavaScript 代码字符串
|
|
226
|
+
|
|
227
|
+
**返回**: Context 对象
|
|
228
|
+
|
|
229
|
+
**示例**:
|
|
230
|
+
```python
|
|
231
|
+
ctx = execjs.compile('''
|
|
232
|
+
function add(a, b) { return a + b; }
|
|
233
|
+
''')
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### `eval(code: str, auto_await: bool = True) -> Any`
|
|
237
|
+
|
|
238
|
+
一次性执行 JavaScript 代码。
|
|
239
|
+
|
|
240
|
+
**参数**:
|
|
241
|
+
- `code`: JavaScript 代码字符串
|
|
242
|
+
- `auto_await`: 是否自动等待 Promise(默认 True)
|
|
243
|
+
|
|
244
|
+
**返回**: 执行结果
|
|
245
|
+
|
|
246
|
+
**示例**:
|
|
247
|
+
```python
|
|
248
|
+
result = execjs.eval("1 + 2 + 3") # 6
|
|
249
|
+
result = execjs.eval("Promise.resolve(42)") # 42
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
#### `compile_file(path: str) -> Context`
|
|
253
|
+
|
|
254
|
+
从文件读取并编译 JavaScript 代码。
|
|
255
|
+
|
|
256
|
+
#### `eval_file(path: str, auto_await: bool = True) -> Any`
|
|
257
|
+
|
|
258
|
+
从文件读取并执行 JavaScript 代码。
|
|
259
|
+
|
|
260
|
+
### Context 类
|
|
261
|
+
|
|
262
|
+
#### `call(name: str, args: list, auto_await: bool = True) -> Any`
|
|
263
|
+
|
|
264
|
+
调用 JavaScript 函数。
|
|
265
|
+
|
|
266
|
+
**参数**:
|
|
267
|
+
- `name`: 函数名称
|
|
268
|
+
- `args`: 参数列表
|
|
269
|
+
- `auto_await`: 是否自动等待 Promise(默认 True)
|
|
270
|
+
|
|
271
|
+
**返回**: 函数返回值
|
|
272
|
+
|
|
273
|
+
**示例**:
|
|
274
|
+
```python
|
|
275
|
+
result = ctx.call("add", [1, 2])
|
|
276
|
+
result = ctx.call("asyncFunc", [arg], auto_await=True)
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
#### `eval(code: str, auto_await: bool = True) -> Any`
|
|
280
|
+
|
|
281
|
+
在当前上下文执行代码。
|
|
282
|
+
|
|
283
|
+
**参数**:
|
|
284
|
+
- `code`: JavaScript 代码字符串
|
|
285
|
+
- `auto_await`: 是否自动等待 Promise(默认 True)
|
|
286
|
+
|
|
287
|
+
**返回**: 执行结果
|
|
288
|
+
|
|
289
|
+
#### `gc() -> None`
|
|
290
|
+
|
|
291
|
+
请求 V8 垃圾回收。
|
|
292
|
+
|
|
293
|
+
**注意**: 这只是提示,V8 会根据自己的策略决定是否执行。在大多数情况下无需手动调用。
|
|
294
|
+
|
|
295
|
+
#### `get_stats() -> tuple[int]`
|
|
296
|
+
|
|
297
|
+
获取执行统计信息。
|
|
298
|
+
|
|
299
|
+
**返回**: `(exec_count,)` 执行次数元组
|
|
300
|
+
|
|
301
|
+
#### `reset_stats() -> None`
|
|
302
|
+
|
|
303
|
+
重置统计信息。
|
|
304
|
+
|
|
305
|
+
## 类型转换
|
|
306
|
+
|
|
307
|
+
Python 和 JavaScript 之间的类型自动转换:
|
|
308
|
+
|
|
309
|
+
| Python 类型 | JavaScript 类型 | 说明 |
|
|
310
|
+
|------------|----------------|------|
|
|
311
|
+
| None | null | 空值 |
|
|
312
|
+
| bool | boolean | 布尔值 |
|
|
313
|
+
| int | number | 整数 |
|
|
314
|
+
| float | number | 浮点数 |
|
|
315
|
+
| str | string | 字符串 |
|
|
316
|
+
| list | Array | 数组 |
|
|
317
|
+
| dict | Object | 对象 |
|
|
318
|
+
|
|
319
|
+
**示例**:
|
|
320
|
+
```python
|
|
321
|
+
# Python -> JavaScript
|
|
322
|
+
ctx.call("func", [None, True, 42, 3.14, "hello", [1, 2], {"key": "value"}])
|
|
323
|
+
|
|
324
|
+
# JavaScript -> Python
|
|
325
|
+
result = execjs.eval("{name: 'John', age: 30}") # dict
|
|
326
|
+
result = execjs.eval("[1, 2, 3]") # list
|
|
327
|
+
result = execjs.eval("null") # None
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## 使用限制
|
|
331
|
+
|
|
332
|
+
### 多 Context 限制
|
|
333
|
+
|
|
334
|
+
⚠️ **重要**: 由于 V8 引擎限制,**不能交叉使用多个 Context 实例**。
|
|
335
|
+
|
|
336
|
+
```python
|
|
337
|
+
# ❌ 错误用法:交叉使用会崩溃
|
|
338
|
+
ctx1 = execjs.compile("function f1() { return 1; }")
|
|
339
|
+
ctx2 = execjs.compile("function f2() { return 2; }")
|
|
340
|
+
ctx1.call("f1", []) # OK
|
|
341
|
+
ctx2.call("f2", []) # OK
|
|
342
|
+
ctx1.call("f1", []) # ❌ 崩溃!
|
|
343
|
+
|
|
344
|
+
# ✅ 正确用法:单个 Context 包含所有函数
|
|
345
|
+
ctx = execjs.compile("""
|
|
346
|
+
function f1() { return 1; }
|
|
347
|
+
function f2() { return 2; }
|
|
348
|
+
""")
|
|
349
|
+
ctx.call("f1", []) # OK
|
|
350
|
+
ctx.call("f2", []) # OK
|
|
351
|
+
ctx.call("f1", []) # OK
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
**推荐做法**:
|
|
355
|
+
1. **单 Context 模式**(最推荐): 将所有函数定义在一个 Context 中
|
|
356
|
+
2. **顺序使用**: 用完一个 Context 再创建下一个
|
|
357
|
+
3. **eval() 模式**: 使用 `execjs.eval()` 进行一次性执行
|
|
358
|
+
|
|
359
|
+
详见项目文档 `MULTI_CONTEXT_LIMITATION.md`。
|
|
360
|
+
|
|
361
|
+
## 适用场景
|
|
362
|
+
|
|
363
|
+
### JavaScript 逆向分析
|
|
364
|
+
|
|
365
|
+
```python
|
|
366
|
+
# 加载目标网站的加密 JS
|
|
367
|
+
with open("target_crypto.js") as f:
|
|
368
|
+
js_code = f.read()
|
|
369
|
+
|
|
370
|
+
ctx = execjs.compile(js_code)
|
|
371
|
+
|
|
372
|
+
# 调用加密函数
|
|
373
|
+
encrypted = ctx.call("encrypt", [plain_text, key])
|
|
374
|
+
|
|
375
|
+
# 调用解密函数
|
|
376
|
+
decrypted = ctx.call("decrypt", [cipher_text, key])
|
|
377
|
+
|
|
378
|
+
# 解析响应
|
|
379
|
+
parsed = ctx.call("parseResponse", [response_data])
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### 前端工具集成
|
|
383
|
+
|
|
384
|
+
```python
|
|
385
|
+
# 使用 JavaScript 工具库
|
|
386
|
+
ctx = execjs.compile_file("lodash.min.js")
|
|
387
|
+
result = ctx.call("_.uniq", [[1, 2, 2, 3, 3, 3]])
|
|
388
|
+
print(result) # [1, 2, 3]
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### 异步数据处理
|
|
392
|
+
|
|
393
|
+
```python
|
|
394
|
+
ctx = execjs.compile("""
|
|
395
|
+
async function processBatch(items) {
|
|
396
|
+
const results = await Promise.all(
|
|
397
|
+
items.map(async item => {
|
|
398
|
+
// 模拟异步处理
|
|
399
|
+
return await Promise.resolve(item * 2);
|
|
400
|
+
})
|
|
401
|
+
);
|
|
402
|
+
return results;
|
|
403
|
+
}
|
|
404
|
+
""")
|
|
405
|
+
|
|
406
|
+
result = ctx.call("processBatch", [[1, 2, 3, 4, 5]])
|
|
407
|
+
print(result) # [2, 4, 6, 8, 10]
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
## 性能优化建议
|
|
411
|
+
|
|
412
|
+
1. **重复执行使用 compile()**: 对于需要多次调用的代码,使用 `compile()` 而不是 `eval()`
|
|
413
|
+
2. **批量处理**: 在 JavaScript 端批量处理数据,减少 Python-JS 调用次数
|
|
414
|
+
3. **避免频繁创建 Context**: 尽可能复用同一个 Context
|
|
415
|
+
4. **合理使用 auto_await**: 对于不需要等待的同步代码,设置 `auto_await=False` 可略微提升性能
|
|
416
|
+
|
|
417
|
+
## 模块化架构
|
|
418
|
+
|
|
419
|
+
项目采用清晰的模块化设计:
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
src/
|
|
423
|
+
├── lib.rs # 模块入口,导出 Python API
|
|
424
|
+
├── context.rs # Context 实现
|
|
425
|
+
├── runtime.rs # V8/Tokio runtime 管理
|
|
426
|
+
├── ops.rs # Deno Core ops 定义
|
|
427
|
+
├── convert.rs # Python ↔ JavaScript 类型转换
|
|
428
|
+
└── storage.rs # 结果存储
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## 测试
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
# 基本功能测试
|
|
435
|
+
python test_single_ctx.py
|
|
436
|
+
|
|
437
|
+
# 模块化测试
|
|
438
|
+
python test_modular.py
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
## 技术细节
|
|
442
|
+
|
|
443
|
+
- **V8 引擎**: 使用 Deno Core 提供的 V8 bindings
|
|
444
|
+
- **Tokio Runtime**: 全局单线程 runtime,支持异步操作
|
|
445
|
+
- **类型转换**: Python ↔ JSON ↔ JavaScript 三层转换
|
|
446
|
+
- **内存管理**: Rust 自动管理,每 100 次执行提示 GC
|
|
447
|
+
|
|
448
|
+
## 许可证
|
|
449
|
+
|
|
450
|
+
MIT License
|
|
451
|
+
|
|
452
|
+
## 贡献
|
|
453
|
+
|
|
454
|
+
欢迎提交 Issue 和 Pull Request!
|
|
455
|
+
|
|
456
|
+
## 相关项目
|
|
457
|
+
|
|
458
|
+
- [PyExecJS](https://github.com/doloopwhile/PyExecJS) - 原 Python ExecJS 实现
|
|
459
|
+
- [Deno](https://github.com/denoland/deno) - 现代 JavaScript/TypeScript 运行时
|
|
460
|
+
- [PyO3](https://github.com/PyO3/pyo3) - Rust Python bindings
|
|
461
|
+
|
|
462
|
+
## 更新日志
|
|
463
|
+
|
|
464
|
+
### v0.1.0
|
|
465
|
+
|
|
466
|
+
- ✅ 基于 Deno Core 的 JavaScript 执行
|
|
467
|
+
- ✅ Promise/async/await 完整支持
|
|
468
|
+
- ✅ Python ↔ JavaScript 类型自动转换
|
|
469
|
+
- ✅ 模块化代码架构
|
|
470
|
+
- ✅ 完整的类型提示支持
|
|
471
|
+
- ✅ 性能监控和统计
|
|
472
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
never_jscore-1.0.0.dist-info/METADATA,sha256=WubZM1NBLnlhK5EiJLMQ6etFkurSEkAqYdH036ayIMg,11709
|
|
2
|
+
never_jscore-1.0.0.dist-info/WHEEL,sha256=CG8OzNtm0LMpJ2zhrjswlO8N-965OeMLklsQAG-nMvQ,94
|
|
3
|
+
never_jscore.pyi,sha256=yq9KEl5FtPyUGEpTN4CMX80-mK3RdUhTlV9jZGYySiw,5885
|
|
4
|
+
never_jscore/__init__.py,sha256=840GIz4OE9B6lYUQRN7o0IV9cFEr6-siQBjigt002Ic,131
|
|
5
|
+
never_jscore/__init__.pyi,sha256=yq9KEl5FtPyUGEpTN4CMX80-mK3RdUhTlV9jZGYySiw,5885
|
|
6
|
+
never_jscore/never_jscore.pyd,sha256=mim8VxQXg3SK1SMNPE9j-bNCbwGSb3HCLHDue5rmPgQ,43345920
|
|
7
|
+
never_jscore/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
py.typed,sha256=a5K6xx9qkO2ovz5hHopVYtLfXXtb1or--wJahADDHGU,87
|
|
9
|
+
never_jscore-1.0.0.dist-info/RECORD,,
|
never_jscore.pyi
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PyExecJS-RS - 基于 Deno Core 的 JavaScript 运行时
|
|
3
|
+
|
|
4
|
+
完整支持 Promise 和 async/await,适合 JS 逆向分析。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any, List, Union
|
|
8
|
+
|
|
9
|
+
class Context:
|
|
10
|
+
"""
|
|
11
|
+
JavaScript 执行上下文(支持异步)
|
|
12
|
+
|
|
13
|
+
每个 Context 包含一个独立的 V8 isolate 和 JavaScript 运行时环境。
|
|
14
|
+
默认自动等待 Promise,可以无缝调用异步 JavaScript 函数。
|
|
15
|
+
|
|
16
|
+
注意:由于 V8 限制,多个 Context 不能交叉使用。
|
|
17
|
+
建议在一个 Context 中定义所有需要的函数。
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
>>> # 同步函数
|
|
21
|
+
>>> ctx = compile("function add(a, b) { return a + b; }")
|
|
22
|
+
>>> result = ctx.call("add", [1, 2])
|
|
23
|
+
>>> print(result)
|
|
24
|
+
3
|
|
25
|
+
|
|
26
|
+
>>> # 异步函数(自动等待)
|
|
27
|
+
>>> ctx = compile("async function asyncAdd(a, b) { return a + b; }")
|
|
28
|
+
>>> result = ctx.call("asyncAdd", [5, 3])
|
|
29
|
+
>>> print(result)
|
|
30
|
+
8
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def call(self, name: str, args: List[Any], auto_await: bool = True) -> Any:
|
|
34
|
+
"""
|
|
35
|
+
调用 JavaScript 函数(支持 Promise)
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
name: 函数名称
|
|
39
|
+
args: 参数列表
|
|
40
|
+
auto_await: 是否自动等待 Promise(默认 True)
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
函数返回值,自动转换为 Python 对象
|
|
44
|
+
|
|
45
|
+
Raises:
|
|
46
|
+
Exception: 当函数调用失败时
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
>>> ctx = compile("async function decrypt(data) { return data.split('').reverse().join(''); }")
|
|
50
|
+
>>> result = ctx.call("decrypt", ["olleh"])
|
|
51
|
+
>>> print(result)
|
|
52
|
+
hello
|
|
53
|
+
"""
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
def eval(self, code: str, auto_await: bool = True) -> Any:
|
|
57
|
+
"""
|
|
58
|
+
在当前上下文中执行 JavaScript 代码(支持 Promise)
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
code: JavaScript 代码字符串
|
|
62
|
+
auto_await: 是否自动等待 Promise(默认 True)
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
执行结果,自动转换为 Python 对象
|
|
66
|
+
|
|
67
|
+
Raises:
|
|
68
|
+
Exception: 当代码执行失败时
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
>>> # 同步执行
|
|
72
|
+
>>> ctx = compile("var x = 10;")
|
|
73
|
+
>>> result = ctx.eval("x * 2")
|
|
74
|
+
>>> print(result)
|
|
75
|
+
20
|
|
76
|
+
|
|
77
|
+
>>> # Promise(自动等待)
|
|
78
|
+
>>> result = ctx.eval("Promise.resolve(42)")
|
|
79
|
+
>>> print(result)
|
|
80
|
+
42
|
|
81
|
+
"""
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
def gc(self) -> None:
|
|
85
|
+
"""
|
|
86
|
+
请求 V8 垃圾回收
|
|
87
|
+
|
|
88
|
+
注意:这只是向 V8 发送 GC 请求,V8 会根据自己的策略决定是否执行。
|
|
89
|
+
在大多数情况下,V8 的自动 GC 已经足够好,无需手动调用。
|
|
90
|
+
"""
|
|
91
|
+
...
|
|
92
|
+
|
|
93
|
+
def get_stats(self) -> tuple[int]:
|
|
94
|
+
"""
|
|
95
|
+
获取执行统计信息
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
包含执行次数的元组 (exec_count,)
|
|
99
|
+
"""
|
|
100
|
+
...
|
|
101
|
+
|
|
102
|
+
def reset_stats(self) -> None:
|
|
103
|
+
"""
|
|
104
|
+
重置统计信息
|
|
105
|
+
"""
|
|
106
|
+
...
|
|
107
|
+
|
|
108
|
+
def compile(code: str) -> Context:
|
|
109
|
+
"""
|
|
110
|
+
编译 JavaScript 代码并返回执行上下文
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
code: JavaScript 代码字符串
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Context 对象,可用于调用函数和执行代码
|
|
117
|
+
|
|
118
|
+
Raises:
|
|
119
|
+
Exception: 当代码编译失败时
|
|
120
|
+
|
|
121
|
+
Example:
|
|
122
|
+
>>> ctx = compile('''
|
|
123
|
+
... function greet(name) {
|
|
124
|
+
... return "Hello, " + name + "!";
|
|
125
|
+
... }
|
|
126
|
+
... ''')
|
|
127
|
+
>>> result = ctx.call("greet", ["World"])
|
|
128
|
+
>>> print(result)
|
|
129
|
+
Hello, World!
|
|
130
|
+
"""
|
|
131
|
+
...
|
|
132
|
+
|
|
133
|
+
def eval(code: str, auto_await: bool = True) -> Any:
|
|
134
|
+
"""
|
|
135
|
+
直接执行 JavaScript 代码并返回结果(支持 Promise)
|
|
136
|
+
|
|
137
|
+
默认自动等待 Promise。适合执行简单的一次性代码。
|
|
138
|
+
对于需要多次调用的场景,建议使用 compile() + Context.call()。
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
code: JavaScript 代码字符串
|
|
142
|
+
auto_await: 是否自动等待 Promise(默认 True)
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
执行结果,自动转换为 Python 对象
|
|
146
|
+
|
|
147
|
+
Raises:
|
|
148
|
+
Exception: 当代码执行失败时
|
|
149
|
+
|
|
150
|
+
Example:
|
|
151
|
+
>>> # 同步代码
|
|
152
|
+
>>> result = eval("1 + 2 + 3")
|
|
153
|
+
>>> print(result)
|
|
154
|
+
6
|
|
155
|
+
|
|
156
|
+
>>> # Promise(自动等待)
|
|
157
|
+
>>> result = eval("Promise.resolve(42)")
|
|
158
|
+
>>> print(result)
|
|
159
|
+
42
|
|
160
|
+
|
|
161
|
+
>>> # async 函数
|
|
162
|
+
>>> result = eval("(async () => { return await Promise.resolve(100); })()")
|
|
163
|
+
>>> print(result)
|
|
164
|
+
100
|
|
165
|
+
"""
|
|
166
|
+
...
|
|
167
|
+
|
|
168
|
+
def compile_file(path: str) -> Context:
|
|
169
|
+
"""
|
|
170
|
+
从文件读取并编译 JavaScript 代码
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
path: JavaScript 文件路径
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Context 对象
|
|
177
|
+
|
|
178
|
+
Raises:
|
|
179
|
+
Exception: 当文件读取或编译失败时
|
|
180
|
+
|
|
181
|
+
Example:
|
|
182
|
+
>>> # 假设 script.js 包含: function add(a, b) { return a + b; }
|
|
183
|
+
>>> ctx = compile_file("script.js")
|
|
184
|
+
>>> result = ctx.call("add", [5, 3])
|
|
185
|
+
>>> print(result)
|
|
186
|
+
8
|
|
187
|
+
"""
|
|
188
|
+
...
|
|
189
|
+
|
|
190
|
+
def eval_file(path: str, auto_await: bool = True) -> Any:
|
|
191
|
+
"""
|
|
192
|
+
从文件读取并执行 JavaScript 代码(支持 Promise)
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
path: JavaScript 文件路径
|
|
196
|
+
auto_await: 是否自动等待 Promise(默认 True)
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
执行结果
|
|
200
|
+
|
|
201
|
+
Raises:
|
|
202
|
+
Exception: 当文件读取或执行失败时
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
>>> result = eval_file("script.js")
|
|
206
|
+
>>> print(result)
|
|
207
|
+
"""
|
|
208
|
+
...
|
|
209
|
+
|
|
210
|
+
# 类型别名
|
|
211
|
+
JSValue = Union[None, bool, int, float, str, List[Any], dict[str, Any]]
|
|
212
|
+
"""JavaScript 值的 Python 类型表示"""
|
|
213
|
+
|
|
214
|
+
__version__: str
|
|
215
|
+
"""模块版本号"""
|
|
216
|
+
|
|
217
|
+
__all__ = [
|
|
218
|
+
"Context",
|
|
219
|
+
"compile",
|
|
220
|
+
"eval",
|
|
221
|
+
"compile_file",
|
|
222
|
+
"eval_file",
|
|
223
|
+
]
|