Compare commits
5 Commits
7b9bded5ee
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| ced4a9d060 | |||
| abe0844816 | |||
| 61ce3466a9 | |||
| bb0cf8c558 | |||
| 1cfa31abcc |
@@ -1,6 +1,6 @@
|
|||||||
# Aster
|
# Aster
|
||||||
|
|
||||||
Aster 是一门使用 Rust 编写的动态类型脚本语言解释器,基于树遍历(tree-walker)实现。无外部依赖,纯标准库。
|
Aster 是一门使用 Rust 编写的动态类型脚本语言解释器,支持两种执行模式:**树遍历(tree-walker)** 和 **字节码 VM**。无外部依赖,纯标准库。
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
|
|
||||||
@@ -11,9 +11,12 @@ cargo build
|
|||||||
# 启动 REPL(交互式)
|
# 启动 REPL(交互式)
|
||||||
cargo run
|
cargo run
|
||||||
|
|
||||||
# 执行脚本文件
|
# 执行脚本文件(树遍历模式)
|
||||||
cargo run -- examples/script.ast
|
cargo run -- examples/script.ast
|
||||||
|
|
||||||
|
# 执行脚本文件(字节码 VM 模式,性能更优)
|
||||||
|
cargo run -- --vm examples/script.ast
|
||||||
|
|
||||||
# 运行测试
|
# 运行测试
|
||||||
cargo test
|
cargo test
|
||||||
```
|
```
|
||||||
@@ -84,6 +87,21 @@ while (true) {
|
|||||||
if (condition) { break; }
|
if (condition) { break; }
|
||||||
if (skip) { continue; }
|
if (skip) { continue; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for-in 遍历(数组 / 字符串)
|
||||||
|
for (let item in [1, 2, 3]) {
|
||||||
|
print(item);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 模块系统
|
||||||
|
|
||||||
|
```js
|
||||||
|
// 加载并执行外部脚本,返回模块导出的对象
|
||||||
|
let math = require("math.ast");
|
||||||
|
print(math.add(3, 4));
|
||||||
|
|
||||||
|
// 第二次 require 同一文件会返回缓存的对象(支持循环引用)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 函数与闭包
|
### 函数与闭包
|
||||||
@@ -144,23 +162,26 @@ let t = os.clock(); // 模块化调用
|
|||||||
## 架构
|
## 架构
|
||||||
|
|
||||||
```
|
```
|
||||||
源码文本 → Lexer → Tokens → Parser → AST → Interpreter → 输出
|
源码文本 → Lexer → Tokens → Parser → AST ──→ Interpreter(树遍历)→ 输出
|
||||||
|
└─→ Compiler → Bytecode → VM → 输出
|
||||||
```
|
```
|
||||||
|
|
||||||
| 模块 | 职责 |
|
| 模块 | 职责 |
|
||||||
|------|------|
|
|------|------|
|
||||||
| `lexer/` | 词法分析。`Lexer::tokenize()` 将源码转为 `(Vec<Token>, Vec<Error>)`,Token 记录行列号用于错误报告,支持 `//` 单行注释 |
|
| `lexer/` | 词法分析。`Lexer::tokenize()` 将源码转为 `(Vec<Token>, Vec<Error>)`,Token 记录行列号用于错误报告,支持 `//` 单行注释和 `/* */` 块注释 |
|
||||||
| `parser/` | 递归下降 + Pratt 解析器。`Parser::parse()` 返回 `(Vec<Stmt>, Vec<Error>)`,遇到语法错误通过 `synchronize()` 跳过至下一条语句边界继续解析 |
|
| `parser/` | 递归下降 + Pratt 解析器。`Parser::parse()` 返回 `(Vec<Stmt>, Vec<Error>)`,遇到语法错误通过 `synchronize()` 跳过至下一条语句边界继续解析 |
|
||||||
| `ast/` | AST 节点定义。`Expr`(表达式)涵盖字面量、变量、赋值、属性/索引访问、一元/二元/逻辑运算、函数调用、lambda 和对象/数组字面量。`Stmt`(语句)涵盖 let、表达式语句、块、if/while/for、函数、return/break/continue |
|
| `ast/` | AST 节点定义。`Expr`(表达式)涵盖字面量、变量、赋值、属性/索引访问、一元/二元/逻辑运算、函数调用、lambda 和对象/数组字面量。`Stmt`(语句)涵盖 let、表达式语句、块、if/while/for、函数、return/break/continue |
|
||||||
| `interpreter/` | 树遍历求值器。`Env` 是基于 `Rc<RefCell<>>` 的链式作用域。`Signal` 枚举通过调用栈传播 `Return`/`Break`/`Continue`。`builtins/` 按 `io` 和 `os` 模块组织原生函数,同时注册为全局函数以方便使用 |
|
| `interpreter/` | 树遍历求值器。`Env` 是基于 `Rc<RefCell<>>` 的链式作用域。`Signal` 枚举通过调用栈传播 `Return`/`Break`/`Continue`。`builtins/` 按 `io` 和 `os` 模块组织原生函数,同时注册为全局函数以方便使用 |
|
||||||
|
| `vm/` | 字节码 VM。`Compiler` 将 AST 编译为基于栈的字节码,`Vm` 执行字节码指令。支持闭包 upvalue 捕获、3 层嵌套闭包、`require()` 模块加载。通过 `--vm` 标志启用 |
|
||||||
| `error/` | 三种错误:`LexError`(行列号)、`ParseError`(Token)、`RuntimeError`(可选 Token) |
|
| `error/` | 三种错误:`LexError`(行列号)、`ParseError`(Token)、`RuntimeError`(可选 Token) |
|
||||||
|
|
||||||
### 关键设计决策
|
### 关键设计决策
|
||||||
|
|
||||||
- **零外部依赖** — 全部基于 Rust 标准库构建
|
- **零外部依赖** — 全部基于 Rust 标准库构建
|
||||||
|
- **双执行模式** — 树遍历模式(默认,适合交互和调试)和字节码 VM 模式(`--vm`,约 40% 性能提升),共享同一 AST 和运行时语义
|
||||||
- **错误容忍解析** — 词法分析器和解析器均将错误收集到 `Vec` 中,单次运行可报告多个诊断信息,而非在第一个错误处就中止
|
- **错误容忍解析** — 词法分析器和解析器均将错误收集到 `Vec` 中,单次运行可报告多个诊断信息,而非在第一个错误处就中止
|
||||||
- **`Rc<RefCell<>>` 共享所有权** — 用于 `Env` 链、`Value::Object`、`Value::Array` 和 `Value::Function`,提供动态可变语义
|
- **`Rc<RefCell<>>` 共享所有权** — 用于 `Env` 链、`Value::Object`、`Value::Array` 和 `Value::Function`,提供动态可变语义
|
||||||
- **词法作用域闭包** — `Function` 在定义时捕获 `Env`,lambda 表达式同理
|
- **词法作用域闭包** — `Function` 在定义时捕获 `Env`,lambda 表达式同理;VM 通过 upvalue 机制支持最多 3 层传递式捕获
|
||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ struct Local {
|
|||||||
name: String,
|
name: String,
|
||||||
depth: u8, // scope depth where declared; 0 = uninitialized
|
depth: u8, // scope depth where declared; 0 = uninitialized
|
||||||
is_captured: bool,
|
is_captured: bool,
|
||||||
#[allow(dead_code)]
|
|
||||||
is_const: bool,
|
is_const: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +84,7 @@ struct Local {
|
|||||||
struct Upvalue {
|
struct Upvalue {
|
||||||
index: u8,
|
index: u8,
|
||||||
is_local: bool, // true = captured from enclosing fn's local; false = from upvalue
|
is_local: bool, // true = captured from enclosing fn's local; false = from upvalue
|
||||||
|
is_const: bool, // true = the source variable was declared `const`
|
||||||
name: String, // variable name (for transitive upvalue resolution)
|
name: String, // variable name (for transitive upvalue resolution)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +203,7 @@ impl Compiler {
|
|||||||
if self.scope_depth == 0 {
|
if self.scope_depth == 0 {
|
||||||
let name_idx = self.add_string_constant(name);
|
let name_idx = self.add_string_constant(name);
|
||||||
emit_u16(&mut self.function.code, OpCode::DefineGlobal, name_idx);
|
emit_u16(&mut self.function.code, OpCode::DefineGlobal, name_idx);
|
||||||
|
self.function.code.push(if mutable { 1 } else { 0 });
|
||||||
// DefineGlobal pushes value back; pop it for statement-level let
|
// DefineGlobal pushes value back; pop it for statement-level let
|
||||||
self.emit_op(OpCode::Pop);
|
self.emit_op(OpCode::Pop);
|
||||||
} else {
|
} else {
|
||||||
@@ -395,6 +396,7 @@ impl Compiler {
|
|||||||
if self.scope_depth == 0 {
|
if self.scope_depth == 0 {
|
||||||
let name_idx = self.add_string_constant(name);
|
let name_idx = self.add_string_constant(name);
|
||||||
emit_u16(&mut self.function.code, OpCode::DefineGlobal, name_idx);
|
emit_u16(&mut self.function.code, OpCode::DefineGlobal, name_idx);
|
||||||
|
self.function.code.push(1); // mutable=true for fn declarations
|
||||||
} else {
|
} else {
|
||||||
let slot = self.locals.len() as u8;
|
let slot = self.locals.len() as u8;
|
||||||
self.locals.push(Local {
|
self.locals.push(Local {
|
||||||
@@ -493,8 +495,23 @@ impl Compiler {
|
|||||||
// Simple assignment
|
// Simple assignment
|
||||||
self.compile_expr(value)?;
|
self.compile_expr(value)?;
|
||||||
if let Some(slot) = self.resolve_local(name) {
|
if let Some(slot) = self.resolve_local(name) {
|
||||||
|
// Check const for local
|
||||||
|
if self.locals[slot as usize].is_const {
|
||||||
|
return Err(RuntimeError::RuntimeError {
|
||||||
|
message: format!("Cannot reassign constant '{}'", name),
|
||||||
|
token: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
emit_u8(&mut self.function.code, OpCode::StoreLocal, slot);
|
emit_u8(&mut self.function.code, OpCode::StoreLocal, slot);
|
||||||
} else if let Some(uv_idx) = self.resolve_upvalue(name) {
|
} else if let Some(uv_idx) = self.resolve_upvalue(name) {
|
||||||
|
// Check const for upvalue
|
||||||
|
let upvalues = self.upvalues.borrow();
|
||||||
|
if upvalues[uv_idx as usize].is_const {
|
||||||
|
return Err(RuntimeError::RuntimeError {
|
||||||
|
message: format!("Cannot reassign constant '{}'", name),
|
||||||
|
token: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
emit_u8(&mut self.function.code, OpCode::StoreUpvalue, uv_idx);
|
emit_u8(&mut self.function.code, OpCode::StoreUpvalue, uv_idx);
|
||||||
} else {
|
} else {
|
||||||
let name_idx = self.add_string_constant(name);
|
let name_idx = self.add_string_constant(name);
|
||||||
@@ -514,14 +531,9 @@ impl Compiler {
|
|||||||
if let Some(slot) = self.resolve_local(name) {
|
if let Some(slot) = self.resolve_local(name) {
|
||||||
emit_u8(&mut self.function.code, OpCode::CompoundAssignLocal, slot);
|
emit_u8(&mut self.function.code, OpCode::CompoundAssignLocal, slot);
|
||||||
self.function.code.push(compound_op as u8);
|
self.function.code.push(compound_op as u8);
|
||||||
} else if self.resolve_upvalue(name).is_some() {
|
} else if let Some(uv_idx) = self.resolve_upvalue(name) {
|
||||||
// Compound assign on upvalue: load upvalue, compound op, store
|
emit_u8(&mut self.function.code, OpCode::CompoundAssignUpvalue, uv_idx);
|
||||||
// For simplicity, use a workaround: re-emit this as a separate step
|
self.function.code.push(compound_op as u8);
|
||||||
// TODO: add CompoundAssignUpvalue opcode
|
|
||||||
return Err(RuntimeError::RuntimeError {
|
|
||||||
message: "Compound assignment on upvalue not yet supported".into(),
|
|
||||||
token: None,
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
let name_idx = self.add_string_constant(name);
|
let name_idx = self.add_string_constant(name);
|
||||||
emit_u16(&mut self.function.code, OpCode::CompoundAssignProp, name_idx);
|
emit_u16(&mut self.function.code, OpCode::CompoundAssignProp, name_idx);
|
||||||
@@ -793,7 +805,9 @@ impl Compiler {
|
|||||||
// Found in direct parent's locals → capture as upvalue
|
// Found in direct parent's locals → capture as upvalue
|
||||||
let idx = self.upvalues.borrow().len() as u8;
|
let idx = self.upvalues.borrow().len() as u8;
|
||||||
self.upvalues.borrow_mut().push(Upvalue {
|
self.upvalues.borrow_mut().push(Upvalue {
|
||||||
index: i as u8, is_local: true, name: name.to_string()
|
index: i as u8, is_local: true,
|
||||||
|
is_const: local.is_const,
|
||||||
|
name: name.to_string()
|
||||||
});
|
});
|
||||||
return Some(idx);
|
return Some(idx);
|
||||||
} else {
|
} else {
|
||||||
@@ -832,15 +846,18 @@ impl Compiler {
|
|||||||
gp_upvalues.borrow_mut().push(Upvalue {
|
gp_upvalues.borrow_mut().push(Upvalue {
|
||||||
index: (i - self.parent_locals_count) as u8,
|
index: (i - self.parent_locals_count) as u8,
|
||||||
is_local: true,
|
is_local: true,
|
||||||
|
is_const: false, // can't easily resolve const-ness at this depth
|
||||||
name: name.to_string()
|
name: name.to_string()
|
||||||
});
|
});
|
||||||
idx
|
idx
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Parent's upvalue is transitive through grandparent
|
// Parent's upvalue is transitive through grandparent
|
||||||
|
let gp_uv_is_const = gp_upvalues.borrow()[gp_idx as usize].is_const;
|
||||||
enc_upvalues.borrow_mut().push(Upvalue {
|
enc_upvalues.borrow_mut().push(Upvalue {
|
||||||
index: gp_idx,
|
index: gp_idx,
|
||||||
is_local: false,
|
is_local: false,
|
||||||
|
is_const: gp_uv_is_const,
|
||||||
name: name.to_string()
|
name: name.to_string()
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -848,6 +865,7 @@ impl Compiler {
|
|||||||
enc_upvalues.borrow_mut().push(Upvalue {
|
enc_upvalues.borrow_mut().push(Upvalue {
|
||||||
index: (i - self.parent_locals_count) as u8,
|
index: (i - self.parent_locals_count) as u8,
|
||||||
is_local: true,
|
is_local: true,
|
||||||
|
is_const: local.is_const,
|
||||||
name: name.to_string()
|
name: name.to_string()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -855,9 +873,12 @@ impl Compiler {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Add transitive upvalue in self pointing to parent's
|
// Add transitive upvalue in self pointing to parent's
|
||||||
|
let parent_uv_is_const = enc_upvalues.borrow()[parent_idx as usize].is_const;
|
||||||
let idx = self.upvalues.borrow().len() as u8;
|
let idx = self.upvalues.borrow().len() as u8;
|
||||||
self.upvalues.borrow_mut().push(Upvalue {
|
self.upvalues.borrow_mut().push(Upvalue {
|
||||||
index: parent_idx, is_local: false, name: name.to_string()
|
index: parent_idx, is_local: false,
|
||||||
|
is_const: parent_uv_is_const,
|
||||||
|
name: name.to_string()
|
||||||
});
|
});
|
||||||
return Some(idx);
|
return Some(idx);
|
||||||
}
|
}
|
||||||
@@ -871,7 +892,9 @@ impl Compiler {
|
|||||||
if uv.name == name {
|
if uv.name == name {
|
||||||
let idx = self.upvalues.borrow().len() as u8;
|
let idx = self.upvalues.borrow().len() as u8;
|
||||||
self.upvalues.borrow_mut().push(Upvalue {
|
self.upvalues.borrow_mut().push(Upvalue {
|
||||||
index: uv.index, is_local: false, name: name.to_string()
|
index: uv.index, is_local: false,
|
||||||
|
is_const: uv.is_const,
|
||||||
|
name: name.to_string()
|
||||||
});
|
});
|
||||||
return Some(idx);
|
return Some(idx);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,12 +78,13 @@ pub enum OpCode {
|
|||||||
CompoundAssignLocal = 39, // operand: u8 slot + u8 compound-op tag
|
CompoundAssignLocal = 39, // operand: u8 slot + u8 compound-op tag
|
||||||
CompoundAssignProp = 40, // operand: u16 name idx + u8 compound-op tag
|
CompoundAssignProp = 40, // operand: u16 name idx + u8 compound-op tag
|
||||||
CompoundAssignIndex = 41, // operand: u8 compound-op tag
|
CompoundAssignIndex = 41, // operand: u8 compound-op tag
|
||||||
|
CompoundAssignUpvalue = 44, // operand: u8 upvalue idx + u8 compound-op tag
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpCode {
|
impl OpCode {
|
||||||
pub fn from_u8(byte: u8) -> Option<Self> {
|
pub fn from_u8(byte: u8) -> Option<Self> {
|
||||||
match byte {
|
match byte {
|
||||||
0..=41 => Some(unsafe { std::mem::transmute::<u8, OpCode>(byte) }),
|
0..=41 | 44 => Some(unsafe { std::mem::transmute::<u8, OpCode>(byte) }),
|
||||||
42 => Some(OpCode::LoadUpvalue),
|
42 => Some(OpCode::LoadUpvalue),
|
||||||
43 => Some(OpCode::StoreUpvalue),
|
43 => Some(OpCode::StoreUpvalue),
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -91,7 +92,7 @@ impl OpCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Total number of distinct opcodes
|
/// Total number of distinct opcodes
|
||||||
pub const COUNT: usize = 44;
|
pub const COUNT: usize = 45;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compound assignment operator tags (used as operand byte after
|
/// Compound assignment operator tags (used as operand byte after
|
||||||
|
|||||||
+45
-7
@@ -48,8 +48,8 @@ pub struct Vm {
|
|||||||
/// Call frames
|
/// Call frames
|
||||||
frames: Vec<CallFrame>,
|
frames: Vec<CallFrame>,
|
||||||
|
|
||||||
/// Script-level globals (top-level let bindings)
|
/// Script-level globals (top-level let/const bindings). Value is (value, is_mutable).
|
||||||
pub globals: Rc<RefCell<HashMap<String, Value>>>,
|
pub globals: Rc<RefCell<HashMap<String, (Value, bool)>>>,
|
||||||
|
|
||||||
/// Builtins (shared across modules)
|
/// Builtins (shared across modules)
|
||||||
pub builtins: Rc<RefCell<HashMap<String, Value>>>,
|
pub builtins: Rc<RefCell<HashMap<String, Value>>>,
|
||||||
@@ -246,7 +246,8 @@ impl Vm {
|
|||||||
OpCode::LoadGlobal => {
|
OpCode::LoadGlobal => {
|
||||||
let name_idx = read_u16(code, ip) as usize;
|
let name_idx = read_u16(code, ip) as usize;
|
||||||
let name = proto_string(&proto, name_idx)?;
|
let name = proto_string(&proto, name_idx)?;
|
||||||
let val = self.globals.borrow().get(&name).cloned()
|
let val = self.globals.borrow().get(&name)
|
||||||
|
.map(|(v, _)| v.clone())
|
||||||
.or_else(|| self.builtins.borrow().get(&name).cloned())
|
.or_else(|| self.builtins.borrow().get(&name).cloned())
|
||||||
.ok_or_else(|| RuntimeError::RuntimeError {
|
.ok_or_else(|| RuntimeError::RuntimeError {
|
||||||
message: format!("Undefined variable '{}'", name),
|
message: format!("Undefined variable '{}'", name),
|
||||||
@@ -259,16 +260,31 @@ impl Vm {
|
|||||||
let name_idx = read_u16(code, ip) as usize;
|
let name_idx = read_u16(code, ip) as usize;
|
||||||
let name = proto_string(&proto,name_idx)?;
|
let name = proto_string(&proto,name_idx)?;
|
||||||
let val = self.stack.last().unwrap().clone();
|
let val = self.stack.last().unwrap().clone();
|
||||||
self.globals.borrow_mut().insert(name, val);
|
{
|
||||||
|
let mut globals = self.globals.borrow_mut();
|
||||||
|
match globals.get(&name) {
|
||||||
|
Some((_, false)) => {
|
||||||
|
return Err(RuntimeError::RuntimeError {
|
||||||
|
message: format!("Cannot reassign constant '{}'", name),
|
||||||
|
token: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Exists and mutable, or not yet defined — store as mutable
|
||||||
|
globals.insert(name, (val, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
self.advance_ip(SIZE_U16);
|
self.advance_ip(SIZE_U16);
|
||||||
}
|
}
|
||||||
OpCode::DefineGlobal => {
|
OpCode::DefineGlobal => {
|
||||||
let name_idx = read_u16(code, ip) as usize;
|
let name_idx = read_u16(code, ip) as usize;
|
||||||
let name = proto_string(&proto,name_idx)?;
|
let name = proto_string(&proto,name_idx)?;
|
||||||
|
let mutable = code[ip + 3] != 0; // 0 = const, 1 = mutable
|
||||||
let val = self.stack.pop().unwrap();
|
let val = self.stack.pop().unwrap();
|
||||||
self.globals.borrow_mut().insert(name, val.clone());
|
self.globals.borrow_mut().insert(name, (val.clone(), mutable));
|
||||||
self.stack.push(val);
|
self.stack.push(val);
|
||||||
self.advance_ip(SIZE_U16);
|
self.advance_ip(SIZE_U16 + 1); // 4 bytes: opcode + u16 + u8
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Properties ---
|
// --- Properties ---
|
||||||
@@ -594,6 +610,28 @@ impl Vm {
|
|||||||
self.stack.push(result);
|
self.stack.push(result);
|
||||||
self.advance_ip(SIZE_OP + 1);
|
self.advance_ip(SIZE_OP + 1);
|
||||||
}
|
}
|
||||||
|
OpCode::CompoundAssignUpvalue => {
|
||||||
|
let uv_idx = read_u8(code, ip) as usize;
|
||||||
|
let op_tag = code[ip + 2];
|
||||||
|
let compound_op = CompoundOp::from_u8(op_tag).unwrap();
|
||||||
|
let rhs = self.stack.pop().unwrap();
|
||||||
|
let uv = Rc::clone(&self.frames.last().unwrap().closure.upvalues[uv_idx]);
|
||||||
|
let mut uv_ref = uv.borrow_mut();
|
||||||
|
let lhs = if let Some(ref closed) = uv_ref.closed {
|
||||||
|
closed.clone()
|
||||||
|
} else {
|
||||||
|
self.stack[uv_ref.location].clone()
|
||||||
|
};
|
||||||
|
let result = self.apply_compound_op(lhs, rhs, compound_op)?;
|
||||||
|
if let Some(ref mut closed) = uv_ref.closed {
|
||||||
|
*closed = result.clone();
|
||||||
|
} else {
|
||||||
|
self.stack[uv_ref.location] = result.clone();
|
||||||
|
}
|
||||||
|
drop(uv_ref);
|
||||||
|
self.stack.push(result);
|
||||||
|
self.advance_ip(SIZE_U8 + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1037,7 +1075,7 @@ impl Runtime for Vm {
|
|||||||
// 10. Collect exports from module globals
|
// 10. Collect exports from module globals
|
||||||
if let Value::Object(exports_map) = &exports_obj {
|
if let Value::Object(exports_map) = &exports_obj {
|
||||||
let mut map = exports_map.borrow_mut();
|
let mut map = exports_map.borrow_mut();
|
||||||
for (name, val) in module_vm.globals.borrow().iter() {
|
for (name, (val, _)) in module_vm.globals.borrow().iter() {
|
||||||
map.insert(name.clone(), val.clone());
|
map.insert(name.clone(), val.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user