Skip to content

Commit e58d9a8

Browse files
committed
fix(return): 修复函数返回为空时导致操作栈过提取问题.
1 parent bceb2e3 commit e58d9a8

6 files changed

Lines changed: 206 additions & 21 deletions

File tree

docs/api/system.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function println(output) {
5656
## `readline` 标准输入行获取
5757

5858
* 无形参
59-
* 返回值: 返回一行输入
59+
* 返回值: 返回一行输入 (EOF 时返回 null)
6060

6161
~~实现过长, 请查阅标准库源码~~
6262

lib/system.exf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ function readline {
2323
while {
2424
c = read();
2525
if (c == "") {
26+
if (line == "") {
27+
return null;
28+
}
2629
break;
2730
}
2831
if (c == newline) {

src/compiler/ast/vm_ir.rs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use smol_str::{SmolStr, ToSmolStr};
44
use std::fmt::Display;
55
use std::str::FromStr;
66

7-
use crate::compiler::ast::ssa_ir::{Code, LocalMap, OpCode, OpCodeTable, Operand};
7+
use crate::compiler::ast::ssa_ir::{Code, LocalAddr, LocalMap, OpCode, OpCodeTable, Operand};
88
use crate::compiler::ast::vm_ir::Types::{Bool, Float, Null, Number, Ref, String};
99

1010
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -27,6 +27,7 @@ pub enum ByteCode {
2727
JumpTrue(usize), // 栈顶条件跳转 (pc位置)
2828
JumpFalse(usize), // 栈顶反转条件跳转 (pc位置)
2929
Call, // 函数调用 (要求栈上最少有两个引用)
30+
CallConst(usize), // 函数调用 (常量表索引)
3031
Nol, // 空操作
3132
GetRef, // 拼接引用路径
3233
Return, // 退出当前栈帧 (并将栈顶元素压入父栈帧操作栈)
@@ -188,6 +189,13 @@ fn opcode_to_vmir(code: OpCode) -> ByteCode {
188189
}
189190
}
190191

192+
fn is_call_const_operand(operand: &Operand) -> bool {
193+
matches!(
194+
operand,
195+
Operand::Reference(_) | Operand::Library(_) | Operand::Call(_)
196+
)
197+
}
198+
191199
impl IrFunction {
192200
#[must_use]
193201
pub const fn new(
@@ -219,8 +227,21 @@ impl IrFunction {
219227
constant_table: &mut ConstantTable,
220228
) {
221229
let mut codes_builder: Vec<ByteCode> = Vec::new();
222-
for code in table.opcodes {
223-
match code.1 {
230+
let entries: Vec<(LocalAddr, OpCode)> = table.opcodes.into_iter().collect();
231+
let mut i = 0;
232+
while i < entries.len() {
233+
let (_addr, op) = &entries[i];
234+
if let OpCode::Push(_, imm) = op {
235+
if let Some((_, OpCode::Call(_, _))) = entries.get(i + 1)
236+
&& is_call_const_operand(imm)
237+
{
238+
let index = constant_table.add_operand(imm.clone(), code0);
239+
codes_builder.push(ByteCode::CallConst(index));
240+
i += 2;
241+
continue;
242+
}
243+
}
244+
match op.clone() {
224245
OpCode::Push(_, imm) => {
225246
if let Operand::Val(key) = imm {
226247
if let Some(index) = locals.get_index(key) {
@@ -305,6 +326,7 @@ impl IrFunction {
305326
codes_builder.push(opcode_to_vmir(c));
306327
}
307328
}
329+
i += 1;
308330
}
309331
self.codes = codes_builder;
310332
}
@@ -368,11 +390,23 @@ impl VMIRTable {
368390
locals: &LocalMap,
369391
const_table: &mut ConstantTable,
370392
) {
371-
let opcodes = table.opcodes.clone();
393+
let entries: Vec<(LocalAddr, OpCode)> = table.opcodes.clone().into_iter().collect();
372394
let mut codes_builder: Vec<ByteCode> = Vec::new();
373395
self.globals = locals.now_index;
374-
for code in opcodes {
375-
match code.1 {
396+
let mut i = 0;
397+
while i < entries.len() {
398+
let (_addr, op) = &entries[i];
399+
if let OpCode::Push(_, imm) = op {
400+
if let Some((_, OpCode::Call(_, _))) = entries.get(i + 1)
401+
&& is_call_const_operand(imm)
402+
{
403+
let index = const_table.add_operand(imm.clone(), code0);
404+
codes_builder.push(ByteCode::CallConst(index));
405+
i += 2;
406+
continue;
407+
}
408+
}
409+
match op.clone() {
376410
OpCode::Push(_, imm) => {
377411
if let Operand::Val(key) = imm {
378412
let Some(index) = locals.get_index(key) else {
@@ -419,6 +453,7 @@ impl VMIRTable {
419453
codes_builder.push(opcode_to_vmir(c));
420454
}
421455
}
456+
i += 1;
422457
}
423458
self.codes = codes_builder;
424459
}

src/compiler/semantic/expression.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,9 @@ fn lower_ref(
383383
|| matches!(left_tree, ASTExprTree::Var(_token))
384384
);
385385

386-
let table = lower_expr(semantic, left_tree, code, global_values, None)?.2;
387-
opcode_table.append_code(&table);
386+
let table = lower_expr(semantic, left_tree, code, global_values, None)?;
387+
388+
opcode_table.append_code(&table.2);
388389

389390
let code = match right_tree {
390391
ASTExprTree::Var(token) => Push(None, Operand::Reference(token.text().to_smolstr())),

src/runtime/executor.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,13 @@ fn run_code<'a>(
280280
Ok(state) => return Ok(state),
281281
Err(err) => return Err(err),
282282
},
283+
ByteCode::CallConst(const_index) => {
284+
match call_const(stack_frame, units, call_cache, sync_table, *const_index) {
285+
Ok(RunState::Continue) => continue,
286+
Ok(state) => return Ok(state),
287+
Err(err) => return Err(err),
288+
}
289+
}
283290
ByteCode::Return => return Ok(RunState::Return),
284291
ByteCode::Jump(pc) => jump(stack_frame, *pc),
285292
ByteCode::JumpTrue(pc) => jump_true(stack_frame, *pc),
@@ -506,19 +513,18 @@ pub fn call_function(
506513
if let Some((unit_index, func_index)) = frame.take_sync_lock() {
507514
sync_table.unlock(unit_index, func_index);
508515
}
509-
if let Some(ret_var) = frame.get_op_stack_top().cloned() {
510-
if let Some((unit_index, func_index, key)) = frame.take_memo() {
511-
call_cache.store_memo(unit_index, func_index, key, ret_var.clone());
512-
}
513-
if executor.call_stack.is_empty() {
514-
return ret_var;
515-
}
516-
executor
517-
.call_stack
518-
.last_mut()
519-
.unwrap()
520-
.push_op_stack(ret_var);
516+
let ret_var = frame.get_op_stack_top().cloned().unwrap_or(Value::Null);
517+
if let Some((unit_index, func_index, key)) = frame.take_memo() {
518+
call_cache.store_memo(unit_index, func_index, key, ret_var.clone());
519+
}
520+
if executor.call_stack.is_empty() {
521+
return ret_var;
521522
}
523+
executor
524+
.call_stack
525+
.last_mut()
526+
.unwrap()
527+
.push_op_stack(ret_var);
522528
}
523529
RunState::Continue => {}
524530
RunState::ThreadExit => {
@@ -537,6 +543,9 @@ pub fn call_function(
537543
sync_table.unlock(unit_index, func_index);
538544
}
539545
executor.frame_index -= 1;
546+
if let Some(parent) = executor.call_stack.last_mut() {
547+
parent.push_op_stack(Value::Null);
548+
}
540549
}
541550
},
542551
Err(state) => {

src/runtime/vm_table_opt.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ pub struct CallCache {
3232
map: HashMap<SmolStr, (usize, usize)>,
3333
memoizable: HashSet<(usize, usize)>,
3434
memo: HashMap<(usize, usize), HashMap<Vec<MemoKey>, Value>>,
35+
const_map: Vec<Vec<Option<(usize, usize)>>>,
3536
}
3637

3738
impl CallCache {
3839
pub fn new(units: &[MetadataUnit]) -> Self {
3940
let mut map = HashMap::new();
4041
let mut memoizable = HashSet::new();
4142
let mut memo = HashMap::new();
43+
let mut const_map: Vec<Vec<Option<(usize, usize)>>> = Vec::with_capacity(units.len());
4244
for (unit_index, unit) in units.iter().enumerate() {
4345
for (func_index, func) in unit.methods.iter().enumerate() {
4446
let self_path = format_smolstr!("{}/{}", unit.names, func.name);
@@ -50,17 +52,40 @@ impl CallCache {
5052
}
5153
}
5254
}
55+
for unit in units {
56+
let mut vec = vec![None; unit.constant_table.len()];
57+
for (idx, value) in unit.constant_table.iter().enumerate() {
58+
if let Value::Ref(path) = value
59+
&& let Some(target) = map.get(path)
60+
{
61+
vec[idx] = Some(*target);
62+
}
63+
}
64+
const_map.push(vec);
65+
}
5366
Self {
5467
map,
5568
memoizable,
5669
memo,
70+
const_map,
5771
}
5872
}
5973

6074
pub fn resolve(&self, path: &SmolStr) -> Option<(usize, usize)> {
6175
self.map.get(path).copied()
6276
}
6377

78+
pub fn resolve_const(
79+
&self,
80+
unit_index: usize,
81+
const_index: usize,
82+
) -> Option<(usize, usize)> {
83+
self.const_map
84+
.get(unit_index)
85+
.and_then(|vec| vec.get(const_index).copied())
86+
.flatten()
87+
}
88+
6489
pub fn is_memoizable(&self, unit_index: usize, func_index: usize) -> bool {
6590
self.memoizable.contains(&(unit_index, func_index))
6691
}
@@ -124,6 +149,12 @@ fn is_pure_self_recursive(
124149
_ => return false,
125150
}
126151
}
152+
ByteCode::CallConst(const_index) => {
153+
match unit.constant_table.get(*const_index) {
154+
Some(Value::Ref(path)) if path == self_path => {}
155+
_ => return false,
156+
}
157+
}
127158
_ => {}
128159
}
129160
}
@@ -259,6 +290,112 @@ pub fn call_func<'a>(
259290
Ok(RunState::CallRequest(frame))
260291
}
261292

293+
pub fn call_const<'a>(
294+
stack_frame: &mut StackFrame,
295+
units: &'a [MetadataUnit],
296+
call_cache: &CallCache,
297+
sync_table: &SyncTable,
298+
const_index: usize,
299+
) -> Result<RunState<'a>, RuntimeError> {
300+
let current_unit = stack_frame.get_unit_index();
301+
let mut path_ref: Option<SmolStr> = None;
302+
303+
let (unit_index, func_index) = if let Some(target) =
304+
call_cache.resolve_const(current_unit, const_index)
305+
{
306+
target
307+
} else {
308+
let Some(Value::Ref(path)) = stack_frame.get_const(const_index) else {
309+
return Err(RuntimeError::VMError);
310+
};
311+
path_ref = Some(path.clone());
312+
let Some(target) = call_cache.resolve(path) else {
313+
return Err(RuntimeError::NoSuchFunctionException(path.clone()));
314+
};
315+
target
316+
};
317+
318+
let unit = &units[unit_index];
319+
let func = &unit.methods[func_index];
320+
let codes = func.get_codes();
321+
let sync_locked = sync_table.lock_if_sync(unit_index, func_index);
322+
323+
if call_cache.is_memoizable(unit_index, func_index)
324+
&& let Some(args) = stack_frame.peek_args(func.args)
325+
&& let Some(key) = CallCache::make_key(args)
326+
{
327+
if let Some(value) = call_cache.get_memo(unit_index, func_index, &key) {
328+
for _ in 0..func.args {
329+
let _ = stack_frame.pop_op_stack();
330+
}
331+
stack_frame.push_op_stack(value);
332+
stack_frame.next_pc();
333+
if sync_locked {
334+
sync_table.unlock(unit_index, func_index);
335+
}
336+
return Ok(RunState::Continue);
337+
}
338+
let native = if func.is_native {
339+
let path = if let Some(path) = path_ref {
340+
path
341+
} else {
342+
let Some(Value::Ref(path)) = stack_frame.get_const(const_index) else {
343+
return Err(RuntimeError::VMError);
344+
};
345+
path.clone()
346+
};
347+
Some(path)
348+
} else {
349+
None
350+
};
351+
let mut frame = StackFrame::new(
352+
unit_index,
353+
func.locals,
354+
codes,
355+
unit.constant_table,
356+
func.name.as_str(),
357+
func.r_name.as_str(),
358+
native,
359+
func.args,
360+
);
361+
frame.set_memo((unit_index, func_index), key);
362+
if sync_locked {
363+
frame.set_sync_lock((unit_index, func_index));
364+
}
365+
stack_frame.next_pc();
366+
return Ok(RunState::CallRequest(frame));
367+
}
368+
369+
stack_frame.next_pc();
370+
let native = if func.is_native {
371+
let path = if let Some(path) = path_ref {
372+
path
373+
} else {
374+
let Some(Value::Ref(path)) = stack_frame.get_const(const_index) else {
375+
return Err(RuntimeError::VMError);
376+
};
377+
path.clone()
378+
};
379+
Some(path)
380+
} else {
381+
None
382+
};
383+
let mut frame = StackFrame::new(
384+
unit_index,
385+
func.locals,
386+
codes,
387+
unit.constant_table,
388+
func.name.as_str(),
389+
func.r_name.as_str(),
390+
native,
391+
func.args,
392+
);
393+
if sync_locked {
394+
frame.set_sync_lock((unit_index, func_index));
395+
}
396+
Ok(RunState::CallRequest(frame))
397+
}
398+
262399
pub fn load_array_local(stack_frame: &mut StackFrame, len: usize, index: usize) {
263400
let mut elements: Vec<Value> = Vec::new();
264401
for _ in 0..len {

0 commit comments

Comments
 (0)