///|
/// Bytecode VM (stack machine), inspired by MicroPython's `py/vm.c`.
///
/// This VM reuses MoonPython's existing runtime helpers (`Value`, builtins,
/// attribute lookup, call protocol, etc.) for semantics.
///|
priv enum VmBlockKind {
Except(Int32)
ExceptHandler
Finally(Int32)
FinallyHandler
}
///|
priv struct VmBlock {
kind : VmBlockKind
stack_height : Int32
// Whether this block pushed an active exception onto `active_exception_stack`.
pushed_exc : Bool
// `except*` state (stored on the active ExceptHandler block).
star_base_err : RuntimeError?
star_remaining_group : Value?
star_active_match : Bool
}
///|
///|
fn vm_trim_runtime_error_for_active_handler(err : RuntimeError) -> RuntimeError {
// See `runtime_block.mbt::trim_runtime_error_for_active_handler`.
let active_len = active_frame_stack.val.length()
if active_len <= 1 {
return err
}
// Only trim when the traceback appears to be a full snapshot of the active
// frame stack. With incremental traceback growth (caller-frame injection),
// trimming here would incorrectly drop legitimate outer frames.
if err.traceback.length() != active_len {
return err
}
let drop = active_len - 1
if err.traceback.length() <= drop {
return err
}
let trimmed_tb : Array[TracebackFrame] = []
for i = drop; i < err.traceback.length(); i = i + 1 {
trimmed_tb.push(err.traceback[i])
}
let trimmed_envs : Array[TracebackEnv] = []
if err.traceback_envs.length() == err.traceback.length() {
for i = drop; i < err.traceback_envs.length(); i = i + 1 {
trimmed_envs.push(err.traceback_envs[i])
}
}
RuntimeError::{
kind: err.kind,
message: err.message,
span: err.span,
traceback: trimmed_tb,
traceback_envs: trimmed_envs,
exc_type: err.exc_type,
exc_args: err.exc_args,
exc_value: err.exc_value,
exc_cause: err.exc_cause,
exc_context: err.exc_context,
exc_suppress_context: err.exc_suppress_context,
}
}
///|
fn pop_stack(stack : Array[Value]) -> Result[Value, RuntimeError] {
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: stack underflow".to_string(),
),
)
} else {
Ok(stack.remove(stack.length() - 1))
}
}
///|
fn pop_n(stack : Array[Value], n : Int32) -> Result[Array[Value], RuntimeError] {
if n < 0 || stack.length() < n {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: stack underflow".to_string(),
),
)
}
let tmp : Array[Value] = []
for _i = 0; _i < n; _i = _i + 1 {
tmp.push(stack.remove(stack.length() - 1))
}
// Caller expects left-to-right order; we popped right-to-left.
let out : Array[Value] = []
for i = tmp.length() - 1; i >= 0; i = i - 1 {
out.push(tmp[i])
if i == 0 {
break
}
}
Ok(out)
}
///|
fn unary_op_value(
op : UnaryOp,
value : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
// Keep this small initially; extend by extracting the full logic from
// `runtime_eval_expr.mbt` as the VM reaches parity.
match op {
UnaryOp::Not => {
let ok = match truthy_from_value_with_env(value, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::Bool(!ok))
}
UnaryOp::Neg =>
match value {
Value::Int(v) => Ok(Value::Int(-v))
Value::Float(v) => Ok(Value::Float(-v))
Value::Complex(real, imag) => Ok(Value::Complex(-real, -imag))
Value::Bool(v) => Ok(Value::Int(-(if v { 1N } else { 0N })))
other =>
match get_attr_from_value(other, "__neg__", globals, builtins, io) {
Ok(callable) =>
call_callable_with_env(callable, [], [], globals, builtins, io)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bad operand type for unary -".to_string(),
),
)
_ => Err(err)
}
}
}
UnaryOp::Pos =>
match value {
Value::Bool(v) => Ok(Value::Int(if v { 1N } else { 0N }))
Value::Int(_) | Value::Float(_) | Value::Complex(_, _) => Ok(value)
other =>
match get_attr_from_value(other, "__pos__", globals, builtins, io) {
Ok(callable) =>
call_callable_with_env(callable, [], [], globals, builtins, io)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute => Ok(other)
_ => Err(err)
}
}
}
UnaryOp::Invert =>
match value {
Value::Int(v) => Ok(Value::Int(-(v + 1N)))
Value::Bool(v) => {
// CPython 3.13+: `~bool` is deprecated.
let import_value = match get_named_value(builtins, "__import__") {
Some(v) => v
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"missing builtins.__import__".to_string(),
),
)
}
let warnings_module = match
call_callable_with_env(
import_value,
[Value::Str("warnings")],
[],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let warn_fn = match
get_attr_from_value(warnings_module, "warn", globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
let category = match get_named_value(builtins, "DeprecationWarning") {
Some(Value::Class(k)) => Value::Class(k)
_ => Value::None
}
let _ = match
call_callable_with_env(
warn_fn,
[
Value::Str(
"Bitwise inversion on bool is deprecated".to_string(),
),
category,
Value::Int(2N),
],
[],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::Int(-((if v { 1N } else { 0N }) + 1N)))
}
other =>
match
get_attr_from_value(other, "__invert__", globals, builtins, io) {
Ok(callable) =>
call_callable_with_env(callable, [], [], globals, builtins, io)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bad operand type for unary ~".to_string(),
),
)
_ => Err(err)
}
}
}
}
}
///|
fn get_subscr_value(
target : Value,
index : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
fn slice_parts_from_value(value : Value) -> (Value, Value, Value)? {
match value {
Value::Instance(inst) if inst.class.name == "slice" => {
let start = match get_named_value(inst.dict, "start") {
Some(v) => v
None => Value::None
}
let stop = match get_named_value(inst.dict, "stop") {
Some(v) => v
None => Value::None
}
let step = match get_named_value(inst.dict, "step") {
Some(v) => v
None => Value::None
}
Some((start, stop, step))
}
_ => None
}
}
match target {
Value::Dict(pairs) => {
let found = match dict_find_index(pairs, index) {
Ok(v) => v
Err(err) => return Err(err)
}
match found {
Some(i) => Ok(pairs[i].1)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Key,
"key not found".to_string(),
),
)
}
}
Value::List(items) =>
match slice_parts_from_value(index) {
Some((start, stop, step)) =>
match slice_indices_from_values(items.length(), start, stop, step) {
Ok(indices) => {
let out : Array[Value] = []
for idx in indices {
if idx >= 0 && idx < items.length() {
out.push(items[idx])
}
}
Ok(Value::List(out))
}
Err(err) => Err(err)
}
None =>
match index_from_value(index, 0) {
Ok(i) => {
let idx = normalize_index(i, items.length())
if idx < 0 || idx >= items.length() {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
} else {
Ok(items[idx])
}
}
Err(err) => Err(err)
}
}
Value::Tuple(items) =>
match slice_parts_from_value(index) {
Some((start, stop, step)) =>
match slice_indices_from_values(items.length(), start, stop, step) {
Ok(indices) => {
let out : Array[Value] = []
for idx in indices {
if idx >= 0 && idx < items.length() {
out.push(items[idx])
}
}
Ok(Value::Tuple(out))
}
Err(err) => Err(err)
}
None =>
match index_from_value(index, 0) {
Ok(i) => {
let idx = normalize_index(i, items.length())
if idx < 0 || idx >= items.length() {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
} else {
Ok(items[idx])
}
}
Err(err) => Err(err)
}
}
Value::Str(text) =>
match slice_parts_from_value(index) {
Some((start, stop, step)) => {
let chars = text.to_array()
match slice_indices_from_values(chars.length(), start, stop, step) {
Ok(indices) =>
Ok(Value::Str(slice_string_by_indices(chars, indices)))
Err(err) => Err(err)
}
}
None =>
match index_from_value(index, 0) {
Ok(i) => {
let chars = text.to_array()
let idx = normalize_index(i, chars.length())
if idx < 0 || idx >= chars.length() {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
} else {
Ok(Value::Str(char_to_string(chars[idx])))
}
}
Err(err) => Err(err)
}
}
Value::Bytes(bytes) =>
match slice_parts_from_value(index) {
Some((start, stop, step)) =>
match slice_indices_from_values(bytes.length(), start, stop, step) {
Ok(indices) =>
Ok(Value::Bytes(slice_ints_by_indices(bytes, indices)))
Err(err) => Err(err)
}
None =>
match index_from_value(index, 0) {
Ok(i) => {
let idx = normalize_index(i, bytes.length())
if idx < 0 || idx >= bytes.length() {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
} else {
Ok(Value::Int(@bigint.BigInt::from_int(bytes[idx])))
}
}
Err(err) => Err(err)
}
}
Value::ByteArray(bytes) =>
match slice_parts_from_value(index) {
Some((start, stop, step)) =>
match slice_indices_from_values(bytes.length(), start, stop, step) {
Ok(indices) =>
Ok(Value::ByteArray(slice_ints_by_indices(bytes, indices)))
Err(err) => Err(err)
}
None =>
match index_from_value(index, 0) {
Ok(i) => {
let idx = normalize_index(i, bytes.length())
if idx < 0 || idx >= bytes.length() {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
} else {
Ok(Value::Int(@bigint.BigInt::from_int(bytes[idx])))
}
}
Err(err) => Err(err)
}
}
Value::MemoryView(bytes) =>
match slice_parts_from_value(index) {
Some((start, stop, step)) =>
match slice_indices_from_values(bytes.length(), start, stop, step) {
Ok(indices) => {
let step_bigint = match step {
Value::None => 1N
Value::Int(v) => v
Value::Bool(v) => if v { 1N } else { 0N }
_ => 1N
}
let sliced = Value::MemoryView(
slice_ints_by_indices(bytes, indices),
)
if step_bigint != 1N {
let dict : Array[(String, Value)] = [
("__mpython_bytes__", sliced),
("__mpython_contiguous__", Value::Bool(false)),
]
Ok(
Value::Instance(InstanceValue::{
class: builtin_class_from_name("memoryview", builtins),
dict,
}),
)
} else {
Ok(sliced)
}
}
Err(err) => Err(err)
}
None =>
match index_from_value(index, 0) {
Ok(i) => {
let idx = normalize_index(i, bytes.length())
if idx < 0 || idx >= bytes.length() {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
} else {
Ok(Value::Int(@bigint.BigInt::from_int(bytes[idx])))
}
}
Err(err) => Err(err)
}
}
Value::Instance(inst) => {
// Treat tuple/list subclasses as sequences when they carry the internal
// storage. This is required for namedtuple (unittest logging watcher).
match get_named_value(inst.dict, list_storage_name) {
Some(Value::List(items)) =>
return get_subscr_value(
Value::List(items),
index,
globals,
builtins,
io,
)
_ => ()
}
match get_named_value(inst.dict, tuple_storage_name) {
Some(Value::Tuple(items)) =>
return get_subscr_value(
Value::Tuple(items),
index,
globals,
builtins,
io,
)
_ => ()
}
match get_attr_from_value(target, "__getitem__", globals, builtins, io) {
Ok(getitem) =>
call_callable_with_env(getitem, [index], [], globals, builtins, io)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" +
type_name_from_value(target) +
"' object is not subscriptable",
),
)
_ => Err(err)
}
}
}
Value::Function(func) => {
// Support `list[int]`-style generic aliases for builtin collection types.
if func.body.length() == 0 && is_generic_alias_origin_name(func.name) {
let args = match index {
Value::Tuple(values) => values
_ => [index]
}
return Ok(
make_generic_alias(
Value::Class(builtin_class_from_name(func.name, builtins)),
args,
),
)
}
// PEP 560: if a class (or builtin type modelled as Value::Function)
// defines __class_getitem__, use it for subscription.
match
get_attr_from_value(target, "__class_getitem__", globals, builtins, io) {
Ok(class_getitem) => {
let args = match class_getitem {
Value::BoundMethod(_) => [index]
Value::Function(_) => [target, index]
Value::Class(_) => [target, index]
_ => [index]
}
call_callable_with_env(class_getitem, args, [], globals, builtins, io)
}
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"subscript requires sequence".to_string(),
),
)
_ => Err(err)
}
}
}
Value::Class(klass) => {
if is_generic_alias_origin_name(klass.name) {
let args = match index {
Value::Tuple(values) => values
_ => [index]
}
return Ok(make_generic_alias(Value::Class(klass), args))
}
match
get_attr_from_value(target, "__class_getitem__", globals, builtins, io) {
Ok(class_getitem) => {
let args = match class_getitem {
Value::BoundMethod(_) => [index]
Value::Function(_) => [target, index]
Value::Class(_) => [target, index]
_ => [index]
}
call_callable_with_env(class_getitem, args, [], globals, builtins, io)
}
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"subscript requires sequence".to_string(),
),
)
_ => Err(err)
}
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"subscript requires sequence".to_string(),
),
)
}
}
///|
fn set_subscr_value(
target : Value,
index : Value,
value : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Unit, RuntimeError] {
fn slice_parts_from_value(value : Value) -> (Value, Value, Value)? {
match value {
Value::Instance(inst) if inst.class.name == "slice" => {
let start = match get_named_value(inst.dict, "start") {
Some(v) => v
None => Value::None
}
let stop = match get_named_value(inst.dict, "stop") {
Some(v) => v
None => Value::None
}
let step = match get_named_value(inst.dict, "step") {
Some(v) => v
None => Value::None
}
Some((start, stop, step))
}
_ => None
}
}
match target {
Value::List(items) =>
match slice_parts_from_value(index) {
Some((start, stop, step)) => {
let indices = match
slice_indices_from_values(items.length(), start, stop, step) {
Ok(v) => v
Err(err) => return Err(err)
}
let (start_i, end_i, step_i) = match
slice_params_from_values(items.length(), start, stop, step) {
Ok(v) => v
Err(err) => return Err(err)
}
let new_items = match iterable_values(value) {
Ok(v) => v
Err(err) => return Err(err)
}
if step_i == 1 {
let merged : Array[Value] = []
for i = 0; i < start_i; i = i + 1 {
merged.push(items[i])
}
for item in new_items {
merged.push(item)
}
for i = end_i; i < items.length(); i = i + 1 {
merged.push(items[i])
}
replace_array_values(items, merged)
Ok(())
} else {
if new_items.length() != indices.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: attempt to assign sequence of size " +
new_items.length().to_string() +
" to extended slice of size " +
indices.length().to_string(),
),
)
}
for i = 0; i < indices.length(); i = i + 1 {
items[indices[i]] = new_items[i]
}
Ok(())
}
}
None =>
match index_from_value(index, 0) {
Ok(i) => {
let idx = normalize_index(i, items.length())
if idx < 0 || idx >= items.length() {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"list assignment index out of range".to_string(),
),
)
} else {
items[idx] = value
Ok(())
}
}
Err(err) => Err(err)
}
}
Value::Dict(pairs) => {
let _ = match dict_set_item(pairs, index, value) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(())
}
Value::ByteArray(bytes) =>
match slice_parts_from_value(index) {
Some((start, stop, step)) => {
let indices = match
slice_indices_from_values(bytes.length(), start, stop, step) {
Ok(v) => v
Err(err) => return Err(err)
}
let (start_i, end_i, step_i) = match
slice_params_from_values(bytes.length(), start, stop, step) {
Ok(v) => v
Err(err) => return Err(err)
}
let new_bytes = match value {
Value::Bytes(items)
| Value::ByteArray(items)
| Value::MemoryView(items) => Ok(items)
_ =>
bytes_from_iterable(
"bytearray.__setitem__", value, globals, builtins, io,
)
}
let new_bytes = match new_bytes {
Ok(v) => v
Err(err) => return Err(err)
}
if step_i == 1 {
let merged : Array[Int32] = []
for i = 0; i < start_i; i = i + 1 {
merged.push(bytes[i])
}
for b in new_bytes {
merged.push(b & 0xFF)
}
for i = end_i; i < bytes.length(); i = i + 1 {
merged.push(bytes[i])
}
while bytes.length() > 0 {
let _ = bytes.remove(bytes.length() - 1)
}
for b in merged {
bytes.push(b)
}
Ok(())
} else {
if new_bytes.length() != indices.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: attempt to assign sequence of size " +
new_bytes.length().to_string() +
" to extended slice of size " +
indices.length().to_string(),
),
)
}
for i = 0; i < indices.length(); i = i + 1 {
bytes[indices[i]] = new_bytes[i] & 0xFF
}
Ok(())
}
}
None =>
match index_from_value(index, 0) {
Ok(i) => {
let idx = normalize_index(i, bytes.length())
if idx < 0 || idx >= bytes.length() {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"bytearray index out of range".to_string(),
),
)
} else {
let b = match byte_from_value("bytearray.__setitem__", value) {
Ok(v) => v
Err(err) => return Err(err)
}
bytes[idx] = b
Ok(())
}
}
Err(err) => Err(err)
}
}
Value::Instance(inst) => {
match get_named_value(inst.dict, list_storage_name) {
Some(Value::List(items)) =>
return set_subscr_value(
Value::List(items),
index,
value,
globals,
builtins,
io,
)
_ => ()
}
match get_named_value(inst.dict, tuple_storage_name) {
Some(Value::Tuple(_)) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" +
type_name_from_value(target) +
"' object does not support item assignment",
),
)
_ => ()
}
match get_attr_from_value(target, "__setitem__", globals, builtins, io) {
Ok(setitem) =>
match
call_callable_with_env(
setitem,
[index, value],
[],
globals,
builtins,
io,
) {
Ok(_) => Ok(())
Err(err) => Err(err)
}
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" +
type_name_from_value(target) +
"' object does not support item assignment",
),
)
_ => Err(err)
}
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"object does not support item assignment".to_string(),
),
)
}
}
///|
fn delete_subscr_value(
target : Value,
index : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Unit, RuntimeError] {
fn slice_parts_from_value(value : Value) -> (Value, Value, Value)? {
match value {
Value::Instance(inst) if inst.class.name == "slice" => {
let start = match get_named_value(inst.dict, "start") {
Some(v) => v
None => Value::None
}
let stop = match get_named_value(inst.dict, "stop") {
Some(v) => v
None => Value::None
}
let step = match get_named_value(inst.dict, "step") {
Some(v) => v
None => Value::None
}
Some((start, stop, step))
}
_ => None
}
}
match target {
Value::List(items) =>
match slice_parts_from_value(index) {
Some((start, stop, step)) => {
let indices = match
slice_indices_from_values(items.length(), start, stop, step) {
Ok(v) => v
Err(err) => return Err(err)
}
if indices.length() == 0 {
return Ok(())
}
let remove_mask : Array[Bool] = []
for i = 0; i < items.length(); i = i + 1 {
remove_mask.push(false)
}
for idx in indices {
if idx >= 0 && idx < remove_mask.length() {
remove_mask[idx] = true
}
}
let next : Array[Value] = []
for i = 0; i < items.length(); i = i + 1 {
if !remove_mask[i] {
next.push(items[i])
}
}
replace_array_values(items, next)
Ok(())
}
None =>
match index_from_value(index, 0) {
Ok(i) => {
let idx = normalize_index(i, items.length())
if idx < 0 || idx >= items.length() {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
} else {
let _ = items.remove(idx)
Ok(())
}
}
Err(err) => Err(err)
}
}
Value::Dict(pairs) => {
let removed = match dict_delete_key(pairs, index) {
Ok(v) => v
Err(err) => return Err(err)
}
if removed {
Ok(())
} else {
Err(
make_runtime_error(RuntimeErrorKind::Key, "key not found".to_string()),
)
}
}
Value::Instance(inst) => {
match get_named_value(inst.dict, list_storage_name) {
Some(Value::List(items)) =>
return delete_subscr_value(
Value::List(items),
index,
globals,
builtins,
io,
)
_ => ()
}
match get_named_value(inst.dict, tuple_storage_name) {
Some(Value::Tuple(_)) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"object does not support item deletion".to_string(),
),
)
_ => ()
}
let delitem = match
get_attr_from_value(target, "__delitem__", globals, builtins, io) {
Ok(v) => v
Err(err) =>
return match err.kind {
RuntimeErrorKind::Attribute =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"object does not support item deletion".to_string(),
),
)
_ => Err(err)
}
}
let _ = match
call_callable_with_env(delitem, [index], [], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(())
}
Value::ByteArray(_) => {
let delitem = match
get_attr_from_value(target, "__delitem__", globals, builtins, io) {
Ok(v) => v
Err(err) =>
return match err.kind {
RuntimeErrorKind::Attribute =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"object does not support item deletion".to_string(),
),
)
_ => Err(err)
}
}
let _ = match
call_callable_with_env(delitem, [index], [], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(())
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"object does not support item deletion".to_string(),
),
)
}
}
///|
///|
fn vm_unwind_return(stack : Array[Value], blocks : Array[VmBlock]) -> Int32? {
while blocks.length() > 0 {
let blk = blocks.remove(blocks.length() - 1)
while stack.length() > blk.stack_height {
let _ = stack.remove(stack.length() - 1)
}
match blk.kind {
VmBlockKind::Finally(target) => {
// Mark as active so exceptions inside the finally do not re-enter it.
blocks.push(VmBlock::{
kind: VmBlockKind::FinallyHandler,
stack_height: stack.length(),
pushed_exc: false,
star_base_err: None,
star_remaining_group: None,
star_active_match: false,
})
return Some(target)
}
// A return bypasses exception handlers; just drop them.
VmBlockKind::Except(_)
| VmBlockKind::ExceptHandler
| VmBlockKind::FinallyHandler => {
if blk.pushed_exc {
let _ = active_exception_stack.val.pop()
}
continue
}
}
}
None
}
///|
pub fn bc_exec(
code : BcCode,
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
fn refresh_raised_exception_traceback(
inst : InstanceValue,
err : RuntimeError,
) -> RuntimeError {
// `raise exc` uses the raise site for outer frames, but when raising an
// already-caught exception instance that carries an existing traceback, we
// must preserve its original tail (see CPython's test_traceback).
let current_tb = snapshot_traceback()
let current_envs = snapshot_frame_env_stack()
let has_existing_tb = match get_named_value(inst.dict, "__traceback__") {
Some(Value::None) => false
Some(_) => true
None => false
}
if has_existing_tb {
// `runtime_error_from_exception_instance` uses the instance's traceback
// when present; treat that as the tail we need to preserve.
let tail = err.traceback
let combined : Array[TracebackFrame] = []
for f in current_tb {
combined.push(f)
}
for f in tail {
combined.push(f)
}
RuntimeError::{
kind: err.kind,
message: err.message,
span: err.span,
traceback: combined,
traceback_envs: current_envs,
exc_type: err.exc_type,
exc_args: err.exc_args,
exc_value: err.exc_value,
exc_cause: err.exc_cause,
exc_context: err.exc_context,
exc_suppress_context: err.exc_suppress_context,
}
} else {
RuntimeError::{
kind: err.kind,
message: err.message,
span: err.span,
traceback: current_tb,
traceback_envs: current_envs,
exc_type: err.exc_type,
exc_args: err.exc_args,
exc_value: err.exc_value,
exc_cause: err.exc_cause,
exc_context: err.exc_context,
exc_suppress_context: err.exc_suppress_context,
}
}
}
fn prepend_current_callsite_frame(
err : RuntimeError,
callsite_span : Span?,
) -> RuntimeError {
// When an exception bubbles out of a callee back to its caller (even if the
// caller catches it), CPython adds the caller frame (at the call site) to
// the traceback chain. Do the same here so stdlib formatting (notably
// ExceptionGroup reporting in Lib/test/test_traceback.py) matches.
let active_frames = active_frame_stack.val
if active_frames.length() == 0 {
return err
}
let caller_raw = active_frames[active_frames.length() - 1]
let caller_frame = match callsite_span {
Some(s) => {
let mut width = s.end - s.start
if width <= 0 {
width = 1
}
let end_column = if width > 512 { 0 } else { s.column + width }
TracebackFrame::{
name: caller_raw.name,
filename: caller_raw.filename,
line: s.line,
column: s.column,
end_column,
}
}
None => caller_raw
}
if err.traceback.length() > 0 {
let head = err.traceback[0]
if head.name == caller_frame.name &&
head.filename == caller_frame.filename &&
head.line == caller_frame.line &&
// Avoid duplicating frames for errors raised directly in the caller
// frame (e.g. builtins). For recursive unwinding, we still want
// repeated frames so traceback.py can shrink them.
err.traceback.length() == 1 &&
err.exc_type != "RecursionError" {
return err
}
}
let combined_frames : Array[TracebackFrame] = []
combined_frames.push(caller_frame)
for f in err.traceback {
combined_frames.push(f)
}
let combined_envs : Array[TracebackEnv] = []
if err.traceback_envs.length() == err.traceback.length() {
let env_stack = active_frame_env_stack.val
if env_stack.length() > 0 {
combined_envs.push(env_stack[env_stack.length() - 1])
for env in err.traceback_envs {
combined_envs.push(env)
}
}
}
RuntimeError::{
kind: err.kind,
message: err.message,
span: err.span,
traceback: combined_frames,
traceback_envs: if combined_envs.length() == combined_frames.length() {
combined_envs
} else {
err.traceback_envs
},
exc_type: err.exc_type,
exc_args: err.exc_args,
exc_value: err.exc_value,
exc_cause: err.exc_cause,
exc_context: err.exc_context,
exc_suppress_context: err.exc_suppress_context,
}
}
let stack : Array[Value] = []
let blocks : Array[VmBlock] = []
let mut pending_exc : RuntimeError? = None
let mut pending_return : Value? = None
let mut pending_jump_target : Int32? = None
let mut pending_jump_count = 0
let mut pc = 0
fn find_current_except_handler_block(blocks : Array[VmBlock]) -> Int32? {
let mut i = blocks.length()
while i > 0 {
i = i - 1
match blocks[i].kind {
VmBlockKind::ExceptHandler => return Some(i)
_ => ()
}
}
None
}
fn find_except_star_handler_block(blocks : Array[VmBlock]) -> Int32? {
let mut i = blocks.length()
while i > 0 {
i = i - 1
match blocks[i].kind {
VmBlockKind::ExceptHandler =>
match blocks[i].star_base_err {
Some(_) => return Some(i)
None => ()
}
_ => ()
}
}
None
}
fn ensure_except_star_handler_classes(
handler_classes : Array[ClassValue],
) -> Result[Unit, RuntimeError] {
for klass in handler_classes {
let mro = match class_mro(klass) {
Ok(v) => v
Err(err) => return Err(err)
}
for cls in mro {
if cls.name == "BaseExceptionGroup" || cls.name == "ExceptionGroup" {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"except* cannot catch exception groups".to_string(),
),
)
}
}
}
Ok(())
}
while pc < code.ops.length() {
let op = code.ops[pc]
let span = code.spans[pc]
match span {
Some(s) => push_active_span(s)
None => ()
}
let mut next_pc = pc + 1
let step : Result[Unit, RuntimeError] = match op {
BcOp::Nop => Ok(())
BcOp::PopTop =>
match pop_stack(stack) {
Ok(_) => Ok(())
Err(err) => Err(err)
}
BcOp::DupTop =>
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: stack underflow".to_string(),
),
)
} else {
stack.push(stack[stack.length() - 1])
Ok(())
}
BcOp::RotTwo =>
if stack.length() < 2 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: stack underflow".to_string(),
),
)
} else {
let b = stack.remove(stack.length() - 1)
let a = stack.remove(stack.length() - 1)
// [a, b] -> [b, a]
stack.push(b)
stack.push(a)
Ok(())
}
BcOp::RotThree =>
if stack.length() < 3 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: stack underflow".to_string(),
),
)
} else {
let c = stack.remove(stack.length() - 1)
let b = stack.remove(stack.length() - 1)
let a = stack.remove(stack.length() - 1)
// [a, b, c] -> [c, a, b]
stack.push(c)
stack.push(a)
stack.push(b)
Ok(())
}
BcOp::LoadConst(i) => {
stack.push(code.consts[i])
Ok(())
}
BcOp::LoadName(i) => {
let name = code.names[i]
match lookup_name_value(name, locals, globals, builtins) {
Ok(v) => {
stack.push(v)
Ok(())
}
Err(err) => Err(err)
}
}
BcOp::StoreName(i) => {
let name = code.names[i]
match pop_stack(stack) {
Ok(v) => {
set_scoped_value(locals, globals, name, v)
Ok(())
}
Err(err) => Err(err)
}
}
BcOp::DeleteName(i) => {
let name = code.names[i]
if scope_name_is_nonlocal(name) {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: can't delete nonlocal".to_string(),
),
)
}
match delete_scoped_value(locals, globals, name) {
Ok(true) => Ok(())
Ok(false) =>
Err(
make_runtime_error(
RuntimeErrorKind::Name,
"name '" + name + "' is not defined",
),
)
Err(err) => Err(err)
}
}
BcOp::LoadAttr(i) => {
let attr = code.names[i]
match pop_stack(stack) {
Ok(target) =>
match get_attr_from_value(target, attr, globals, builtins, io) {
Ok(v) => {
stack.push(v)
Ok(())
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
}
BcOp::StoreAttr(i) => {
let attr = code.names[i]
match pop_stack(stack) {
Ok(value) =>
match pop_stack(stack) {
Ok(target) =>
match
set_attr_on_value(target, attr, value, globals, builtins, io) {
Ok(_) => Ok(())
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
}
BcOp::DeleteAttr(i) => {
let attr = code.names[i]
match pop_stack(stack) {
Ok(target) =>
delete_attr_on_value(target, attr, globals, builtins, io)
Err(err) => Err(err)
}
}
BcOp::LoadSubscr =>
match pop_stack(stack) {
Ok(index) =>
match pop_stack(stack) {
Ok(target) =>
match get_subscr_value(target, index, globals, builtins, io) {
Ok(v) => {
stack.push(v)
Ok(())
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::StoreSubscr =>
match pop_stack(stack) {
Ok(value) =>
match pop_stack(stack) {
Ok(index) =>
match pop_stack(stack) {
Ok(target) =>
match
set_subscr_value(
target, index, value, globals, builtins, io,
) {
Ok(_) => Ok(())
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::DeleteSubscr =>
match pop_stack(stack) {
Ok(index) =>
match pop_stack(stack) {
Ok(target) =>
delete_subscr_value(target, index, globals, builtins, io)
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::BuildTuple(n) =>
match pop_n(stack, n) {
Ok(values) => {
stack.push(Value::Tuple(values))
Ok(())
}
Err(err) => Err(err)
}
BcOp::BuildList(n) =>
match pop_n(stack, n) {
Ok(values) => {
stack.push(Value::List(values))
Ok(())
}
Err(err) => Err(err)
}
BcOp::BuildMap(pair_count) =>
match pop_n(stack, pair_count * 2) {
Ok(values) => {
let out : Array[(Value, Value)] = []
let mut err_opt : RuntimeError? = None
for i = 0; i < values.length(); i = i + 2 {
match dict_set_item(out, values[i], values[i + 1]) {
Ok(_) => ()
Err(err) => {
err_opt = Some(err)
break
}
}
}
match err_opt {
Some(err) => Err(err)
None => {
stack.push(Value::Dict(out))
Ok(())
}
}
}
Err(err) => Err(err)
}
BcOp::BuildSet(n) =>
match pop_n(stack, n) {
Ok(values) => {
let out : Array[Value] = []
let mut err_opt : RuntimeError? = None
for v in values {
match set_add_unique(out, v) {
Ok(_) => ()
Err(err) => {
err_opt = Some(err)
break
}
}
}
match err_opt {
Some(err) => Err(err)
None => {
stack.push(Value::Set(out))
Ok(())
}
}
}
Err(err) => Err(err)
}
BcOp::BuildSlice =>
match pop_stack(stack) {
Ok(step) =>
match pop_stack(stack) {
Ok(stop) =>
match pop_stack(stack) {
Ok(start) => {
let dict : Array[(String, Value)] = []
dict.push(("start", start))
dict.push(("stop", stop))
dict.push(("step", step))
let klass = builtin_class_from_name("slice", builtins)
stack.push(
Value::Instance(InstanceValue::{ class: klass, dict, }),
)
Ok(())
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::ListAppend =>
match pop_stack(stack) {
Ok(value) =>
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: ListAppend missing list".to_string(),
),
)
} else {
let idx = stack.length() - 1
match stack[idx] {
Value::List(values) => {
values.push(value)
stack[idx] = Value::List(values)
Ok(())
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: ListAppend expects list".to_string(),
),
)
}
}
Err(err) => Err(err)
}
BcOp::ListExtend =>
match pop_stack(stack) {
Ok(iterable) =>
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: ListExtend missing list".to_string(),
),
)
} else {
let idx = stack.length() - 1
match stack[idx] {
Value::List(values) =>
match iterable_values(iterable) {
Ok(items) => {
for item in items {
values.push(item)
}
stack[idx] = Value::List(values)
Ok(())
}
Err(err) => Err(err)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: ListExtend expects list".to_string(),
),
)
}
}
Err(err) => Err(err)
}
BcOp::SetAdd =>
match pop_stack(stack) {
Ok(value) =>
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: SetAdd missing set".to_string(),
),
)
} else {
let idx = stack.length() - 1
match stack[idx] {
Value::Set(values) =>
match set_add_unique(values, value) {
Ok(_) => {
stack[idx] = Value::Set(values)
Ok(())
}
Err(err) => Err(err)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: SetAdd expects set".to_string(),
),
)
}
}
Err(err) => Err(err)
}
BcOp::SetUpdate =>
match pop_stack(stack) {
Ok(iterable) =>
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: SetUpdate missing set".to_string(),
),
)
} else {
let idx = stack.length() - 1
match stack[idx] {
Value::Set(values) =>
match iterable_values(iterable) {
Ok(items) => {
let mut err_opt : RuntimeError? = None
for item in items {
match set_add_unique(values, item) {
Ok(_) => ()
Err(err) => {
err_opt = Some(err)
break
}
}
}
match err_opt {
Some(err) => Err(err)
None => {
stack[idx] = Value::Set(values)
Ok(())
}
}
}
Err(err) => Err(err)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: SetUpdate expects set".to_string(),
),
)
}
}
Err(err) => Err(err)
}
BcOp::DictSetItem =>
match pop_stack(stack) {
Ok(value) =>
match pop_stack(stack) {
Ok(key) =>
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: DictSetItem missing dict".to_string(),
),
)
} else {
let idx = stack.length() - 1
match stack[idx] {
Value::Dict(pairs) =>
match dict_set_item(pairs, key, value) {
Ok(_) => {
stack[idx] = Value::Dict(pairs)
Ok(())
}
Err(err) => Err(err)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: DictSetItem expects dict".to_string(),
),
)
}
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::DictUpdate =>
match pop_stack(stack) {
Ok(mapping) =>
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: DictUpdate missing dict".to_string(),
),
)
} else {
let idx = stack.length() - 1
match stack[idx] {
Value::Dict(out) => {
let update_result : Result[Unit, RuntimeError] = match
mapping {
Value::Dict(pairs) => {
let mut err_opt : RuntimeError? = None
for pair in pairs {
match dict_set_item(out, pair.0, pair.1) {
Ok(_) => ()
Err(err) => {
err_opt = Some(err)
break
}
}
}
match err_opt {
Some(err) => Err(err)
None => Ok(())
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"dict update sequence element has wrong length".to_string(),
),
)
}
match update_result {
Ok(_) => {
stack[idx] = Value::Dict(out)
Ok(())
}
Err(err) => Err(err)
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: DictUpdate expects dict".to_string(),
),
)
}
}
Err(err) => Err(err)
}
BcOp::KwListExtendFromDict =>
match pop_stack(stack) {
Ok(mapping) =>
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: KwListExtendFromDict missing list".to_string(),
),
)
} else {
let idx = stack.length() - 1
match stack[idx] {
Value::List(values) => {
let extend_result : Result[Unit, RuntimeError] = match
mapping {
Value::Dict(pairs) => {
let mut err_opt : RuntimeError? = None
for pair in pairs {
match pair.0 {
Value::Str(name) =>
values.push(
Value::Tuple([Value::Str(name), pair.1]),
)
_ => {
err_opt = Some(
make_runtime_error(
RuntimeErrorKind::Type,
"keywords must be strings".to_string(),
),
)
break
}
}
}
match err_opt {
Some(e) => Err(e)
None => Ok(())
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"keywords must be strings".to_string(),
),
)
}
match extend_result {
Ok(_) => {
stack[idx] = Value::List(values)
Ok(())
}
Err(err) => Err(err)
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: KwListExtendFromDict expects list".to_string(),
),
)
}
}
Err(err) => Err(err)
}
BcOp::ListToTuple =>
match pop_stack(stack) {
Ok(v) =>
match v {
Value::List(values) => {
stack.push(Value::Tuple(values))
Ok(())
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: ListToTuple expects list".to_string(),
),
)
}
Err(err) => Err(err)
}
BcOp::EvalFString =>
match pop_stack(stack) {
Ok(v) =>
match v {
Value::Str(text) =>
match eval_fstring_text(text, locals, globals, builtins, io) {
Ok(out) => {
stack.push(Value::Str(out))
Ok(())
}
Err(err) => Err(err)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: EvalFString expects a string".to_string(),
),
)
}
Err(err) => Err(err)
}
BcOp::UnpackSequence(n) =>
match pop_stack(stack) {
Ok(assigned) => {
let items = match assigned {
Value::Tuple(values) => Ok(values)
Value::List(values) => Ok(values)
_ =>
match iter_value_to_iterator(assigned, globals, builtins, io) {
Ok(iterator_value) => {
let acc : Array[Value] = []
let mut err_opt : RuntimeError? = None
while true {
match
iterator_next(
iterator_value,
None,
globals,
builtins,
io,
) {
Ok(v) => acc.push(v)
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
err_opt = Some(err)
break
}
}
}
match err_opt {
Some(e) => Err(e)
None => Ok(acc)
}
}
Err(err) =>
match err.kind {
RuntimeErrorKind::Type =>
if err.message == "object is not iterable" {
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"cannot unpack non-iterable".to_string(),
),
)
} else {
Err(err)
}
_ => Err(err)
}
}
}
match items {
Ok(values) =>
if values.length() != n {
if values.length() < n {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: not enough values to unpack (expected " +
n.to_string() +
")",
),
)
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: too many values to unpack (expected " +
n.to_string() +
")",
),
)
}
} else {
// Push in reverse so the first target consumes the first value.
let mut i = n
while i > 0 {
i -= 1
stack.push(values[i])
}
Ok(())
}
Err(err) => Err(err)
}
}
Err(err) => Err(err)
}
BcOp::UnpackEx(before, after) =>
match pop_stack(stack) {
Ok(assigned) => {
let items = match assigned {
Value::Tuple(values) => Ok(values)
Value::List(values) => Ok(values)
_ =>
match iter_value_to_iterator(assigned, globals, builtins, io) {
Ok(iterator_value) => {
let acc : Array[Value] = []
let mut err_opt : RuntimeError? = None
while true {
match
iterator_next(
iterator_value,
None,
globals,
builtins,
io,
) {
Ok(v) => acc.push(v)
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
err_opt = Some(err)
break
}
}
}
match err_opt {
Some(e) => Err(e)
None => Ok(acc)
}
}
Err(err) =>
match err.kind {
RuntimeErrorKind::Type =>
if err.message == "object is not iterable" {
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"cannot unpack non-iterable".to_string(),
),
)
} else {
Err(err)
}
_ => Err(err)
}
}
}
match items {
Ok(values) => {
let min_expected = before + after
if values.length() < min_expected {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: not enough values to unpack (expected at least " +
min_expected.to_string() +
")",
),
)
}
let out : Array[Value] = []
// leading fixed items
for i = 0; i < before; i = i + 1 {
out.push(values[i])
}
// starred captures the middle into a list
let mid_end = values.length() - after
let mid : Array[Value] = []
for i = before; i < mid_end; i = i + 1 {
mid.push(values[i])
}
out.push(Value::List(mid))
// trailing fixed items
for i = mid_end; i < values.length(); i = i + 1 {
out.push(values[i])
}
// Push in reverse so the first target consumes the first value.
let mut i = out.length()
while i > 0 {
i -= 1
stack.push(out[i])
}
Ok(())
}
Err(err) => Err(err)
}
}
Err(err) => Err(err)
}
BcOp::GetIter =>
match pop_stack(stack) {
Ok(value) =>
match iter_value_to_iterator(value, globals, builtins, io) {
Ok(iter) => {
stack.push(iter)
Ok(())
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::ForIter(target) =>
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: stack underflow".to_string(),
),
)
} else {
let iter = stack[stack.length() - 1]
match iterator_next(iter, None, globals, builtins, io) {
Ok(item) => {
// Keep iterator on stack; push current item.
stack.push(item)
Ok(())
}
Err(err) =>
if err.exc_type == "StopIteration" {
// Pop iterator and jump out of the loop.
let _ = stack.remove(stack.length() - 1)
next_pc = target
Ok(())
} else {
Err(err)
}
}
}
BcOp::Unary(op) =>
match pop_stack(stack) {
Ok(value) =>
match unary_op_value(op, value, globals, builtins, io) {
Ok(out) => {
stack.push(out)
Ok(())
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::Binary(op) =>
match pop_stack(stack) {
Ok(right) =>
match pop_stack(stack) {
Ok(left) =>
match
eval_binary_op_values(op, left, right, globals, builtins, io) {
Ok(out) => {
stack.push(out)
Ok(())
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::Compare(op) =>
match pop_stack(stack) {
Ok(right) =>
match pop_stack(stack) {
Ok(left) =>
match
compare_values_with_env(
op, left, right, globals, builtins, io,
) {
Ok(ok) => {
stack.push(Value::Bool(ok))
Ok(())
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::Jump(target) => {
next_pc = target
Ok(())
}
BcOp::UnwindJump(target, n) => {
// `break`/`continue` in a `finally` suite must override any pending
// return/exception being unwound into this handler.
if blocks.length() > 0 {
let top = blocks[blocks.length() - 1]
match top.kind {
VmBlockKind::FinallyHandler => {
pending_exc = None
pending_return = None
pending_jump_target = None
pending_jump_count = 0
}
_ => ()
}
}
if n <= 0 {
next_pc = target
Ok(())
} else {
pending_jump_target = Some(target)
pending_jump_count = n
match vm_unwind_return(stack, blocks) {
Some(handler) => {
next_pc = handler
Ok(())
}
None => {
pending_jump_target = None
pending_jump_count = 0
next_pc = target
Ok(())
}
}
}
}
BcOp::JumpIfFalse(target) =>
match pop_stack(stack) {
Ok(v) =>
match truthy_from_value_with_env(v, globals, builtins, io) {
Ok(ok) => {
if !ok {
next_pc = target
}
Ok(())
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::JumpIfTrue(target) =>
match pop_stack(stack) {
Ok(v) =>
match truthy_from_value_with_env(v, globals, builtins, io) {
Ok(ok) => {
if ok {
next_pc = target
}
Ok(())
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::JumpIfFalseOrPop(target) =>
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: stack underflow".to_string(),
),
)
} else {
let v = stack[stack.length() - 1]
match truthy_from_value_with_env(v, globals, builtins, io) {
Ok(ok) => {
if !ok {
next_pc = target
} else {
let _ = stack.remove(stack.length() - 1)
}
Ok(())
}
Err(err) => Err(err)
}
}
BcOp::JumpIfTrueOrPop(target) =>
if stack.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: stack underflow".to_string(),
),
)
} else {
let v = stack[stack.length() - 1]
match truthy_from_value_with_env(v, globals, builtins, io) {
Ok(ok) => {
if ok {
next_pc = target
} else {
let _ = stack.remove(stack.length() - 1)
}
Ok(())
}
Err(err) => Err(err)
}
}
BcOp::SetupExcept(target) => {
blocks.push(VmBlock::{
kind: VmBlockKind::Except(target),
stack_height: stack.length(),
pushed_exc: false,
star_base_err: None,
star_remaining_group: None,
star_active_match: false,
})
Ok(())
}
BcOp::SetupFinally(target) => {
blocks.push(VmBlock::{
kind: VmBlockKind::Finally(target),
stack_height: stack.length(),
pushed_exc: false,
star_base_err: None,
star_remaining_group: None,
star_active_match: false,
})
Ok(())
}
BcOp::PopExcept =>
if blocks.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: block underflow".to_string(),
),
)
} else {
let blk = blocks.remove(blocks.length() - 1)
match blk.kind {
VmBlockKind::Except(_) => Ok(())
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: PopExcept on non-except block".to_string(),
),
)
}
}
BcOp::EnterFinally(target) =>
if blocks.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: block underflow".to_string(),
),
)
} else {
let blk = blocks.remove(blocks.length() - 1)
match blk.kind {
VmBlockKind::Finally(_) => {
blocks.push(VmBlock::{
kind: VmBlockKind::FinallyHandler,
stack_height: blk.stack_height,
pushed_exc: false,
star_base_err: None,
star_remaining_group: None,
star_active_match: false,
})
next_pc = target
Ok(())
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: EnterFinally on non-finally block".to_string(),
),
)
}
}
BcOp::EndExcept =>
if blocks.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: block underflow".to_string(),
),
)
} else {
let blk = blocks.remove(blocks.length() - 1)
match blk.kind {
VmBlockKind::ExceptHandler => {
if blk.pushed_exc {
let _ = active_exception_stack.val.pop()
}
pending_exc = None
Ok(())
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: EndExcept on non-handler block".to_string(),
),
)
}
}
BcOp::EndFinally =>
if blocks.length() == 0 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: block underflow".to_string(),
),
)
} else {
let blk = blocks.remove(blocks.length() - 1)
match blk.kind {
VmBlockKind::FinallyHandler => {
if blk.pushed_exc {
let _ = active_exception_stack.val.pop()
}
match pending_return {
Some(v) =>
match vm_unwind_return(stack, blocks) {
Some(target) => {
next_pc = target
Ok(())
}
None => {
pending_return = None
if span is Some(_) {
pop_active_span()
}
return Ok(v)
}
}
None =>
match pending_jump_target {
Some(target) => {
pending_jump_count = pending_jump_count - 1
if pending_jump_count <= 0 {
pending_jump_target = None
pending_jump_count = 0
next_pc = target
Ok(())
} else {
match vm_unwind_return(stack, blocks) {
Some(handler) => {
next_pc = handler
Ok(())
}
None => {
// Inconsistent unwind count; fall back to the jump.
pending_jump_target = None
pending_jump_count = 0
next_pc = target
Ok(())
}
}
}
}
None =>
match pending_exc {
Some(err) => Err(err)
None => Ok(())
}
}
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: EndFinally on non-handler block".to_string(),
),
)
}
}
BcOp::CheckExceptionMatch =>
match pop_stack(stack) {
Ok(exc_value) =>
match pending_exc {
Some(err) =>
match handler_classes_from_value(exc_value, builtins) {
Ok(handler_classes) => {
let mut matches = false
let mut match_error : RuntimeError? = None
for klass in handler_classes {
match exception_matches_handler(err, klass, builtins) {
Ok(true) => {
matches = true
break
}
Ok(false) => ()
Err(e) => {
match_error = Some(e)
break
}
}
}
match match_error {
Some(e) => Err(e)
None => {
stack.push(Value::Bool(matches))
Ok(())
}
}
}
Err(e) => Err(e)
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: no pending exception".to_string(),
),
)
}
Err(err) => Err(err)
}
BcOp::LoadException =>
match pending_exc {
Some(err) => {
let v = exception_value_from_runtime_error(
err, globals, builtins, io,
)
stack.push(v)
Ok(())
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: no pending exception".to_string(),
),
)
}
BcOp::HasPendingException => {
stack.push(Value::Bool(pending_exc is Some(_)))
Ok(())
}
BcOp::ClearPendingException => {
pending_exc = None
Ok(())
}
BcOp::EnsureExceptionGroup =>
match pending_exc {
Some(err) => {
let group_value = match
exception_group_from_runtime_error(err, globals, builtins, io) {
Ok(v) => v
Err(e) => return Err(e)
}
match group_value {
Value::Instance(inst) => {
let group_err0 = vm_trim_runtime_error_for_active_handler(
runtime_error_from_exception_instance(inst),
)
// Avoid attaching PEP 657 caret positions to exception-group
// dispatch locations (CPython does not show carets for these).
let group_err = RuntimeError::{
kind: group_err0.kind,
message: group_err0.message,
span: None,
traceback: group_err0.traceback,
traceback_envs: group_err0.traceback_envs,
exc_type: group_err0.exc_type,
exc_args: group_err0.exc_args,
exc_value: group_err0.exc_value,
exc_cause: group_err0.exc_cause,
exc_context: group_err0.exc_context,
exc_suppress_context: group_err0.exc_suppress_context,
}
pending_exc = Some(group_err)
if active_exception_stack.val.length() > 0 {
let _ = active_exception_stack.val.pop()
active_exception_stack.val.push(group_err)
}
Ok(())
}
_ => Ok(())
}
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: no pending exception".to_string(),
),
)
}
BcOp::ExceptStarInit =>
match pending_exc {
Some(err) => {
let group_value = exception_value_from_runtime_error(
err, globals, builtins, io,
)
let idx_opt = find_current_except_handler_block(blocks)
match idx_opt {
Some(idx) => {
let blk = blocks[idx]
blocks[idx] = VmBlock::{
kind: blk.kind,
stack_height: blk.stack_height,
pushed_exc: blk.pushed_exc,
star_base_err: Some(err),
star_remaining_group: Some(group_value),
star_active_match: false,
}
Ok(())
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: ExceptStarInit without an active except handler".to_string(),
),
)
}
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: no pending exception".to_string(),
),
)
}
BcOp::ExceptStarMatch =>
match pop_stack(stack) {
Ok(exc_value) => {
let idx_opt = find_except_star_handler_block(blocks)
match idx_opt {
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: ExceptStarMatch without ExceptStarInit".to_string(),
),
)
Some(idx) => {
let blk = blocks[idx]
match blk.star_remaining_group {
None => {
stack.push(Value::Bool(false))
Ok(())
}
Some(group_value) =>
match handler_classes_from_value(exc_value, builtins) {
Ok(handler_classes) => {
let _ = match
ensure_except_star_handler_classes(handler_classes) {
Ok(v) => v
Err(err) => return Err(err)
}
let (matched_opt, remaining_opt) = match
exception_group_split_by_classes(
group_value, handler_classes, globals, builtins, io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
match matched_opt {
None => {
stack.push(Value::Bool(false))
Ok(())
}
Some(matched_group) => {
// Update remaining group state for subsequent handlers.
blocks[idx] = VmBlock::{
kind: blk.kind,
stack_height: blk.stack_height,
pushed_exc: blk.pushed_exc,
star_base_err: blk.star_base_err,
star_remaining_group: remaining_opt,
star_active_match: true,
}
// Set the current exception (pending + active) to the matched group.
let match_err0 = match matched_group {
Value::Instance(inst) =>
runtime_error_from_exception_instance(inst)
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"except* requires an ExceptionGroup".to_string(),
),
)
}
let match_err1 = vm_trim_runtime_error_for_active_handler(
match_err0,
)
let match_err = RuntimeError::{
kind: match_err1.kind,
message: match_err1.message,
span: None,
traceback: match_err1.traceback,
traceback_envs: match_err1.traceback_envs,
exc_type: match_err1.exc_type,
exc_args: match_err1.exc_args,
exc_value: match_err1.exc_value,
exc_cause: match_err1.exc_cause,
exc_context: match_err1.exc_context,
exc_suppress_context: match_err1.exc_suppress_context,
}
pending_exc = Some(match_err)
if active_exception_stack.val.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: no active exception".to_string(),
),
)
}
let _ = active_exception_stack.val.pop()
active_exception_stack.val.push(match_err)
stack.push(Value::Bool(true))
Ok(())
}
}
}
Err(err) => Err(err)
}
}
}
}
}
Err(err) => Err(err)
}
BcOp::ExceptStarEndHandler => {
let idx_opt = find_except_star_handler_block(blocks)
match idx_opt {
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: ExceptStarEndHandler without ExceptStarInit".to_string(),
),
)
Some(idx) => {
let blk = blocks[idx]
match (blk.star_base_err, blk.star_active_match) {
(Some(base_err), true) => {
// Restore the base exception as the active exception for subsequent handler matching.
if active_exception_stack.val.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: no active exception".to_string(),
),
)
}
let _ = active_exception_stack.val.pop()
active_exception_stack.val.push(base_err)
blocks[idx] = VmBlock::{
kind: blk.kind,
stack_height: blk.stack_height,
pushed_exc: blk.pushed_exc,
star_base_err: blk.star_base_err,
star_remaining_group: blk.star_remaining_group,
star_active_match: false,
}
}
_ => ()
}
// Keep pending exception cleared while running handler code.
pending_exc = None
stack.push(Value::Bool(blk.star_remaining_group is Some(_)))
Ok(())
}
}
}
BcOp::ExceptStarRaiseRemaining => {
let idx_opt = find_except_star_handler_block(blocks)
match idx_opt {
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: ExceptStarRaiseRemaining without ExceptStarInit".to_string(),
),
)
Some(idx) => {
let blk = blocks[idx]
match blk.star_remaining_group {
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: no remaining exception group".to_string(),
),
)
Some(rem) => {
let err0 = match rem {
Value::Instance(inst) =>
runtime_error_from_exception_instance(inst)
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"except* requires an ExceptionGroup".to_string(),
),
)
}
// Avoid attaching the base exception as implicit context when re-raising
// the remaining group: it is logically a continuation of the original.
let err = if err0.exc_context is None && err0.exc_cause is None {
RuntimeError::{
kind: err0.kind,
message: err0.message,
span: err0.span,
traceback: err0.traceback,
traceback_envs: err0.traceback_envs,
exc_type: err0.exc_type,
exc_args: err0.exc_args,
exc_value: err0.exc_value,
exc_cause: err0.exc_cause,
exc_context: Some(Value::None),
exc_suppress_context: err0.exc_suppress_context,
}
} else {
err0
}
Err(err)
}
}
}
}
}
BcOp::AssertFail =>
match pop_stack(stack) {
Ok(msg_value) =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"AssertionError: " + value_to_string(msg_value),
),
)
Err(err) => Err(err)
}
BcOp::AssertFailNone =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"AssertionError: ".to_string(),
),
)
BcOp::LoadExceptionType =>
match pending_exc {
Some(err) => {
let exc_value = exception_value_from_runtime_error(
err, globals, builtins, io,
)
let exc_type_value = match exc_value {
Value::Instance(inst) => Value::Class(inst.class)
_ => Value::Str(err.exc_type)
}
stack.push(exc_type_value)
Ok(())
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: no pending exception".to_string(),
),
)
}
BcOp::LoadExceptionTypeName =>
match pending_exc {
Some(err) => {
stack.push(Value::Str(err.exc_type))
Ok(())
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: no pending exception".to_string(),
),
)
}
BcOp::LoadExceptionTraceback =>
match pending_exc {
Some(err) => {
stack.push(
make_traceback_value_with_env(
err.traceback,
err.traceback_envs,
frame_builtins_dict_from_builtins(builtins),
err.span,
),
)
Ok(())
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: no pending exception".to_string(),
),
)
}
BcOp::Raise =>
match pop_stack(stack) {
Ok(exc_value) => {
let context_err_opt = active_exception()
match
ensure_exception_instance_from_value(
exc_value, globals, builtins, io,
) {
Ok(inst_value) => {
let err0_opt = match inst_value {
Value::Instance(inst) =>
Some(
refresh_raised_exception_traceback(
inst,
runtime_error_from_exception_instance(inst),
),
)
_ => None
}
match err0_opt {
Some(err0) => {
let err1 = match (context_err_opt, err0.exc_context) {
(Some(ctx_err), None) => {
let ctx_value = exception_value_from_runtime_error(
ctx_err, globals, builtins, io,
)
RuntimeError::{
kind: err0.kind,
message: err0.message,
span: err0.span,
traceback: err0.traceback,
traceback_envs: err0.traceback_envs,
exc_type: err0.exc_type,
exc_args: err0.exc_args,
exc_value: err0.exc_value,
exc_cause: err0.exc_cause,
exc_context: Some(ctx_value),
exc_suppress_context: err0.exc_suppress_context,
}
}
_ => err0
}
// Keep the raised exception object's core metadata in sync so
// stdlib helpers can rely on `exc.__traceback__`, etc.
match err1.exc_value {
Some(Value::Instance(inst)) => {
set_named_value(
inst.dict,
"__traceback__",
make_traceback_value_with_env(
err1.traceback,
err1.traceback_envs,
frame_builtins_dict_from_builtins(builtins),
err1.span,
),
)
set_named_value(
inst.dict,
"__cause__",
match err1.exc_cause {
Some(v) => v
None => Value::None
},
)
set_named_value(
inst.dict,
"__context__",
match err1.exc_context {
Some(v) => v
None => Value::None
},
)
set_named_value(
inst.dict,
"__suppress_context__",
Value::Bool(err1.exc_suppress_context),
)
}
_ => ()
}
Err(err1)
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"RuntimeError: invalid exception".to_string(),
),
)
}
}
Err(err) => Err(err)
}
}
Err(err) => Err(err)
}
BcOp::RaiseFrom =>
match pop_stack(stack) {
Ok(cause_value) =>
match pop_stack(stack) {
Ok(exc_value) => {
let context_err_opt = active_exception()
match
ensure_exception_instance_from_value(
exc_value, globals, builtins, io,
) {
Ok(inst_value) => {
let err0_opt = match inst_value {
Value::Instance(inst) =>
Some(
refresh_raised_exception_traceback(
inst,
runtime_error_from_exception_instance(inst),
),
)
_ => None
}
match err0_opt {
Some(err0) => {
let err1 = match (context_err_opt, err0.exc_context) {
(Some(ctx_err), None) => {
let ctx_value = exception_value_from_runtime_error(
ctx_err, globals, builtins, io,
)
RuntimeError::{
kind: err0.kind,
message: err0.message,
span: err0.span,
traceback: err0.traceback,
traceback_envs: err0.traceback_envs,
exc_type: err0.exc_type,
exc_args: err0.exc_args,
exc_value: err0.exc_value,
exc_cause: err0.exc_cause,
exc_context: Some(ctx_value),
exc_suppress_context: err0.exc_suppress_context,
}
}
_ => err0
}
let cause_inst = match cause_value {
Value::None => Ok(Value::None)
_ =>
ensure_exception_instance_from_value(
cause_value, globals, builtins, io,
)
}
match cause_inst {
Ok(cause_inst) => {
let err2 = RuntimeError::{
kind: err1.kind,
message: err1.message,
span: err1.span,
traceback: err1.traceback,
traceback_envs: err1.traceback_envs,
exc_type: err1.exc_type,
exc_args: err1.exc_args,
exc_value: err1.exc_value,
exc_cause: Some(cause_inst),
exc_context: err1.exc_context,
exc_suppress_context: true,
}
match err2.exc_value {
Some(Value::Instance(inst)) => {
set_named_value(
inst.dict,
"__traceback__",
make_traceback_value_with_env(
err2.traceback,
err2.traceback_envs,
frame_builtins_dict_from_builtins(builtins),
err2.span,
),
)
set_named_value(
inst.dict,
"__cause__",
match err2.exc_cause {
Some(v) => v
None => Value::None
},
)
set_named_value(
inst.dict,
"__context__",
match err2.exc_context {
Some(v) => v
None => Value::None
},
)
set_named_value(
inst.dict,
"__suppress_context__",
Value::Bool(err2.exc_suppress_context),
)
}
_ => ()
}
Err(err2)
}
Err(err) => Err(err)
}
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"RuntimeError: invalid exception".to_string(),
),
)
}
}
Err(err) => Err(err)
}
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::Reraise =>
match active_exception() {
Some(e) => Err(e)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"RuntimeError: No active exception to reraise".to_string(),
),
)
}
BcOp::Await =>
match pop_stack(stack) {
Ok(coro) =>
match coroutine_await(coro) {
Ok(v) => {
stack.push(v)
Ok(())
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::MatchPattern(pat_idx) =>
match pop_stack(stack) {
Ok(subject) => {
let pattern = code.patterns[pat_idx]
match match_pattern(subject, pattern) {
Some(bindings) => {
let pairs : Array[Value] = []
for binding in bindings {
pairs.push(Value::Tuple([Value::Str(binding.0), binding.1]))
}
stack.push(Value::List(pairs))
Ok(())
}
None => {
stack.push(Value::None)
Ok(())
}
}
}
Err(err) => Err(err)
}
BcOp::MatchBind =>
match pop_stack(stack) {
Ok(bindings_val) =>
match bindings_val {
Value::List(items) => {
let restore : Array[Value] = []
let mut err_opt : RuntimeError? = None
for item in items {
if err_opt is Some(_) {
break
}
match item {
Value::Tuple(pair) =>
if pair.length() != 2 {
err_opt = Some(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: MatchBind expects (name, value) pairs".to_string(),
),
)
} else {
let name = match pair[0] {
Value::Str(text) => text
_ => {
err_opt = Some(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: MatchBind name must be str".to_string(),
),
)
""
}
}
if err_opt is None {
let value = pair[1]
let prev_opt = get_scoped_value(locals, globals, name)
let existed = prev_opt is Some(_)
let prev_value = match prev_opt {
Some(v) => v
None => Value::None
}
restore.push(
Value::Tuple([
Value::Str(name),
Value::Bool(existed),
prev_value,
]),
)
set_scoped_value(locals, globals, name, value)
}
}
_ =>
err_opt = Some(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: MatchBind expects a list of tuples".to_string(),
),
)
}
}
match err_opt {
Some(e) => Err(e)
None => {
stack.push(Value::List(restore))
Ok(())
}
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: MatchBind expects a list".to_string(),
),
)
}
Err(err) => Err(err)
}
BcOp::MatchRestore =>
match pop_stack(stack) {
Ok(restore_val) =>
match restore_val {
Value::List(items) => {
let mut err_opt : RuntimeError? = None
for item in items {
if err_opt is Some(_) {
break
}
match item {
Value::Tuple(tup) =>
if tup.length() != 3 {
err_opt = Some(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: MatchRestore expects (name, existed, value)".to_string(),
),
)
} else {
let name = match tup[0] {
Value::Str(text) => text
_ => {
err_opt = Some(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: MatchRestore name must be str".to_string(),
),
)
""
}
}
let existed = match tup[1] {
Value::Bool(v) => v
_ => {
err_opt = Some(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: MatchRestore existed must be bool".to_string(),
),
)
false
}
}
let value = tup[2]
if err_opt is None {
if existed {
set_scoped_value(locals, globals, name, value)
} else {
let _ = delete_scoped_value(locals, globals, name)
}
}
}
_ =>
err_opt = Some(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: MatchRestore expects a list of tuples".to_string(),
),
)
}
}
match err_opt {
Some(e) => Err(e)
None => Ok(())
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: MatchRestore expects a list".to_string(),
),
)
}
Err(err) => Err(err)
}
BcOp::GenExpNew(template_idx, iter_name_idx, target_names_const_idx) =>
match
(if iter_name_idx >= 0 { pop_stack(stack) } else { Ok(Value::None) }) {
Ok(iter_or_dummy) => {
let iter_value : Value? = if iter_name_idx >= 0 {
Some(iter_or_dummy)
} else {
None
}
match code.consts[template_idx] {
Value::Function(template) => {
let gen_locals = clone_locals(locals)
match iter_value {
Some(it) => {
let iter_name = code.names[iter_name_idx]
set_local_value(gen_locals, iter_name, it)
}
None => ()
}
match code.consts[target_names_const_idx] {
Value::Tuple(items) => {
let target_names : Array[String] = []
let mut err_opt : RuntimeError? = None
for item in items {
match item {
Value::Str(name) => {
target_names.push(name)
shadow_local_cell(gen_locals, name, Value::None)
}
_ =>
err_opt = Some(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: GenExpNew target names must be strings".to_string(),
),
)
}
if err_opt is Some(_) {
break
}
}
match err_opt {
Some(e) => Err(e)
None => {
let filename = current_traceback_filename()
let scope = scope_decls_without_bindings(target_names)
let gen = generator_new(
template.body,
gen_locals,
globals,
builtins,
io,
scope.globals,
scope.nonlocals,
current_closure_env(),
template.name,
filename,
)
stack.push(gen)
Ok(())
}
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: GenExpNew target names must be a tuple".to_string(),
),
)
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: GenExpNew template is not a function".to_string(),
),
)
}
}
Err(err) => Err(err)
}
BcOp::AsyncGenExpNew(spec_idx, iter_name_idx, target_names_const_idx) =>
match
(if iter_name_idx >= 0 { pop_stack(stack) } else { Ok(Value::None) }) {
Ok(iter_or_dummy) => {
let iter_value : Value? = if iter_name_idx >= 0 {
Some(iter_or_dummy)
} else {
None
}
let gen_locals = clone_locals(locals)
match iter_value {
Some(it) => {
let iter_name = code.names[iter_name_idx]
set_local_value(gen_locals, iter_name, it)
}
None => ()
}
match code.consts[target_names_const_idx] {
Value::Tuple(items) => {
let mut err_opt : RuntimeError? = None
for item in items {
match item {
Value::Str(name) =>
shadow_local_cell(gen_locals, name, Value::None)
_ =>
err_opt = Some(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: AsyncGenExpNew target names must be strings".to_string(),
),
)
}
if err_opt is Some(_) {
break
}
}
match err_opt {
Some(e) => Err(e)
None => {
let spec = code.genexps[spec_idx]
let gen = async_genexp_new(
spec.elt,
spec.clauses,
spec.filters,
gen_locals,
globals,
builtins,
io,
)
stack.push(gen)
Ok(())
}
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: AsyncGenExpNew target names must be a tuple".to_string(),
),
)
}
}
Err(err) => Err(err)
}
BcOp::CallFunction(argc) =>
match pop_n(stack, argc) {
Ok(args) =>
match pop_stack(stack) {
Ok(callee) =>
match callee {
// Builtins need access to the caller's locals for features like
// zero-arg `super()`. The AST evaluator special-cases this via
// `eval_builtin_call`, so mirror that here.
Value::Function(func) if func.body.length() == 0 =>
match
eval_builtin_call(
func.name,
args,
[],
locals,
globals,
builtins,
io,
) {
Ok(Some(out)) => {
stack.push(out)
Ok(())
}
Ok(None) =>
match
call_callable_with_env(
Value::Function(func),
args,
[],
globals,
builtins,
io,
) {
Ok(out) => {
stack.push(out)
Ok(())
}
Err(err) =>
Err(prepend_current_callsite_frame(err, span))
}
Err(err) => Err(err)
}
_ =>
match
call_callable_with_env(
callee,
args,
[],
globals,
builtins,
io,
) {
Ok(out) => {
stack.push(out)
Ok(())
}
Err(err) => Err(prepend_current_callsite_frame(err, span))
}
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::CallFunctionKw(argc) =>
match pop_stack(stack) {
Ok(kw_names_val) => {
let kw_names = match kw_names_val {
Value::Tuple(values) => {
let names : Array[String] = []
for v in values {
match v {
Value::Str(name) => names.push(name)
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: CallFunctionKw expects tuple of str names".to_string(),
),
)
}
}
names
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: CallFunctionKw expects tuple of keyword names".to_string(),
),
)
}
let kwc = kw_names.length()
match pop_n(stack, kwc) {
Ok(kw_values) =>
match pop_n(stack, argc) {
Ok(pos_args) =>
match pop_stack(stack) {
Ok(callee) => {
let keywords : Array[(String, Value)] = []
for i = 0; i < kwc; i = i + 1 {
keywords.push((kw_names[i], kw_values[i]))
}
match callee {
Value::Function(func) if func.body.length() == 0 =>
match
eval_builtin_call(
func.name,
pos_args,
keywords,
locals,
globals,
builtins,
io,
) {
Ok(Some(out)) => {
stack.push(out)
Ok(())
}
Ok(None) =>
match
call_callable_with_env(
Value::Function(func),
pos_args,
keywords,
globals,
builtins,
io,
) {
Ok(out) => {
stack.push(out)
Ok(())
}
Err(err) =>
Err(
prepend_current_callsite_frame(err, span),
)
}
Err(err) => Err(err)
}
_ =>
match
call_callable_with_env(
callee, pos_args, keywords, globals, builtins, io,
) {
Ok(out) => {
stack.push(out)
Ok(())
}
Err(err) =>
Err(prepend_current_callsite_frame(err, span))
}
}
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
}
Err(err) => Err(err)
}
BcOp::CallFunctionVar =>
match pop_stack(stack) {
Ok(keyword_pairs_val) =>
match pop_stack(stack) {
Ok(positional_list_val) =>
match pop_stack(stack) {
Ok(callee) => {
let positional = match positional_list_val {
Value::List(values) => values
Value::Tuple(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: CallFunctionVar expects positional list".to_string(),
),
)
}
let keyword_items = match keyword_pairs_val {
Value::List(values) => values
Value::Tuple(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: CallFunctionVar expects keyword list".to_string(),
),
)
}
let keywords : Array[(String, Value)] = []
for item in keyword_items {
match item {
Value::Tuple(pair) =>
if pair.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: CallFunctionVar keyword pair arity".to_string(),
),
)
} else {
match pair[0] {
Value::Str(name) => keywords.push((name, pair[1]))
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"keywords must be strings".to_string(),
),
)
}
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: CallFunctionVar expects keyword tuples".to_string(),
),
)
}
}
match callee {
Value::Function(func) if func.body.length() == 0 =>
match
eval_builtin_call(
func.name,
positional,
keywords,
locals,
globals,
builtins,
io,
) {
Ok(Some(out)) => {
stack.push(out)
Ok(())
}
Ok(None) =>
match
call_callable_with_env(
Value::Function(func),
positional,
keywords,
globals,
builtins,
io,
) {
Ok(out) => {
stack.push(out)
Ok(())
}
Err(err) =>
Err(prepend_current_callsite_frame(err, span))
}
Err(err) => Err(err)
}
_ =>
match
call_callable_with_env(
callee, positional, keywords, globals, builtins, io,
) {
Ok(out) => {
stack.push(out)
Ok(())
}
Err(err) =>
Err(prepend_current_callsite_frame(err, span))
}
}
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::MakeFunction(template, default_count, decorator_count) =>
match pop_n(stack, default_count) {
Ok(defaults) =>
match pop_n(stack, decorator_count) {
Ok(decorators) => {
let template_value = code.consts[template]
match template_value {
Value::Function(template_func) =>
match
make_function_from_template_with_env(
template_func, defaults, decorators, locals, globals, builtins,
io,
) {
Ok(defined) => {
stack.push(defined)
Ok(())
}
Err(err) => Err(prepend_current_callsite_frame(err, span))
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: MakeFunction template is not a function".to_string(),
),
)
}
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::MakeTypeAlias(name_idx) =>
match pop_stack(stack) {
Ok(value_func) =>
match value_func {
Value::Function(_) => {
let name = code.names[name_idx]
stack.push(
Value::Instance(InstanceValue::{
class: ClassValue::{
name: "TypeAlias",
bases: [],
dict: [],
},
dict: [
("__name__", Value::Str(name)),
("__value_func__", value_func),
],
}),
)
Ok(())
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: MakeTypeAlias expects function".to_string(),
),
)
}
Err(err) => Err(err)
}
BcOp::MakeClass(template, name_idx, decorator_count) =>
match pop_stack(stack) {
Ok(keyword_specs_val) =>
match pop_stack(stack) {
Ok(base_specs_val) =>
match pop_n(stack, decorator_count) {
Ok(decorator_values) => {
fn inner() -> Result[Unit, RuntimeError] {
let class_name = code.names[name_idx]
let template_value = code.consts[template]
let body = match template_value {
Value::Function(template_func) => template_func.body
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytecode vm: MakeClass template is not a function".to_string(),
),
)
}
let base_specs = match base_specs_val {
Value::List(items) => items
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: MakeClass expects base spec list".to_string(),
),
)
}
let raw_base_values : Array[Value] = []
for spec in base_specs {
match spec {
Value::Tuple(values) if values.length() == 2 =>
match values[0] {
Value::Bool(true) => {
let iter_value = values[1]
let items = match iterable_values(iter_value) {
Ok(v) => v
Err(err) => return Err(err)
}
for item in items {
raw_base_values.push(item)
}
}
Value::Bool(false) =>
raw_base_values.push(values[1])
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: invalid base spec".to_string(),
),
)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: invalid base spec".to_string(),
),
)
}
}
let base_values : Array[Value] = []
for base_value in raw_base_values {
let expanded = match base_value {
Value::Instance(_) =>
match
get_attr_from_value(
base_value, "__mro_entries__", globals, builtins,
io,
) {
Ok(mro_entries) => {
let bases_tuple : Array[Value] = []
for item in raw_base_values {
bases_tuple.push(item)
}
let entries = match
call_callable_with_env(
mro_entries,
[Value::Tuple(bases_tuple)],
[],
globals,
builtins,
io,
) {
Ok(value) => value
Err(err) => return Err(err)
}
match entries {
Value::Tuple(values) => values
Value::List(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"base class must be a class".to_string(),
),
)
}
}
Err(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"base class must be a class".to_string(),
),
)
}
_ => [base_value]
}
for value in expanded {
match value {
Value::Class(_) => base_values.push(value)
Value::Function(func) =>
if func.body.length() == 0 &&
is_builtin_class_name(func.name) {
base_values.push(
Value::Class(
builtin_class_from_name(func.name, builtins),
),
)
} else {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"base class must be a class".to_string(),
),
)
}
other => base_values.push(other)
}
}
}
let keyword_specs = match keyword_specs_val {
Value::List(items) => items
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: MakeClass expects keyword spec list".to_string(),
),
)
}
let class_kwargs : Array[(String, Value)] = []
let mut metaclass_value : Value? = None
for spec in keyword_specs {
let (key, value) = match spec {
Value::Tuple(values) if values.length() == 2 =>
match values[0] {
Value::Str(k) => (k, values[1])
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: class keyword name must be str".to_string(),
),
)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: invalid class keyword spec".to_string(),
),
)
}
if key == "**" {
let pairs = match
dict_pairs_from_value("class", value) {
Ok(v) => v
Err(err) => return Err(err)
}
for pair in pairs {
match pair.0 {
Value::Str(k) =>
if k == "metaclass" {
metaclass_value = Some(pair.1)
} else {
set_global_value(class_kwargs, k, pair.1)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"keywords must be strings".to_string(),
),
)
}
}
continue
}
if key == "metaclass" {
metaclass_value = Some(value)
continue
}
set_global_value(class_kwargs, key, value)
}
if base_values.length() == 0 {
for pair in builtins {
if pair.0 == "object" {
match pair.1 {
Value::Class(object_class) =>
base_values.push(Value::Class(object_class))
_ => ()
}
break
}
}
}
for base_value in base_values {
match base_value {
Value::Class(base_class) =>
match
get_named_value(base_class.dict, "__module__") {
Some(Value::Str(module_name)) =>
if module_name == "builtins" &&
base_class.name == "bool" {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"type 'bool' is not an acceptable base type".to_string(),
),
)
}
_ => ()
}
_ => ()
}
}
if get_scoped_value(locals, globals, "__class__")
is Some(_) &&
stmts_have_class_body_comprehension_name(
body, "__class__",
) {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SystemError: compiler_lookup_arg(name='__class__') with reftype=1 failed in ; freevars of code " +
class_name +
": ('__class__',)",
),
)
}
let module_name_value = get_global_value(
globals, "__name__",
)
if module_name_value is None {
return Err(
make_runtime_error(
RuntimeErrorKind::Name,
"name '__name__' is not defined",
),
)
}
let module_name = match module_name_value.unwrap() {
Value::Str(text) => text
other => value_to_string(other)
}
let class_dict : Array[(String, Value)] = []
let mut qualname = class_name
match get_global_value(locals, "__qualname__") {
Some(Value::Str(parent)) =>
qualname = parent + "." + class_name
_ => ()
}
class_dict.push(("__module__", Value::Str(module_name)))
class_dict.push(("__qualname__", Value::Str(qualname)))
ensure_local_cell(class_dict, "__class__", Value::None)
let mut pushed_closure = false
if has_active_closure_env() {
for i = 0; i < locals.length(); i = i + 1 {
let name = locals[i].0
let value = locals[i].1
if !is_cell_value(value) {
ensure_local_cell(locals, name, value)
}
}
let class_closure = merge_cell_closures(
locals,
current_closure_env(),
)
push_closure_env(class_closure)
pushed_closure = true
}
let flow = match
eval_block_flow(
body,
class_dict,
globals,
builtins,
io,
current_config(),
0,
) {
Ok(value) => value
Err(err) => return Err(err)
}
if pushed_closure {
pop_closure_env()
}
match flow {
BlockFlow::Normal(_) => ()
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: invalid class body".to_string(),
),
)
}
let mut selected_meta = Value::Class(
builtin_class_from_name("type", builtins),
)
match metaclass_value {
Some(v) => selected_meta = v
None =>
for base_value in base_values {
match base_value {
Value::Class(base_class) =>
match
get_named_value(
base_class.dict,
"__mpython_metaclass__",
) {
Some(Value::Class(meta)) => {
selected_meta = Value::Class(meta)
break
}
_ => ()
}
_ => ()
}
}
}
let namespace_pairs : Array[(Value, Value)] = []
for pair in class_dict {
namespace_pairs.push((Value::Str(pair.0), pair.1))
}
let metaclass_args : Array[Value] = [
Value::Str(class_name),
Value::Tuple(base_values),
Value::Dict(namespace_pairs),
]
let created_value = match
call_callable_with_env(
selected_meta, metaclass_args, class_kwargs, globals, builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
fn apply_decorators(
defined0 : Value,
decorator_values : Array[Value],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
let mut defined = defined0
for i = decorator_values.length(); i > 0; i = i - 1 {
let decorator_value = decorator_values[i - 1]
defined = match decorator_value {
Value::Function(dec_func) =>
if dec_func.body.length() == 0 {
match
eval_builtin_call(
dec_func.name,
[defined],
[],
locals,
globals,
builtins,
io,
) {
Ok(Some(value)) => value
Ok(None) =>
match
call_callable_with_env(
decorator_value,
[defined],
[],
globals,
builtins,
io,
) {
Ok(value) => value
Err(err) =>
return Err(
prepend_current_callsite_frame(
err, span,
),
)
}
Err(err) =>
return Err(
prepend_current_callsite_frame(err, span),
)
}
} else {
match
call_callable_with_env(
decorator_value,
[defined],
[],
globals,
builtins,
io,
) {
Ok(value) => value
Err(err) =>
return Err(
prepend_current_callsite_frame(err, span),
)
}
}
_ =>
match
call_callable_with_env(
decorator_value,
[defined],
[],
globals,
builtins,
io,
) {
Ok(value) => value
Err(err) =>
return Err(
prepend_current_callsite_frame(err, span),
)
}
}
}
Ok(defined)
}
match created_value {
Value::Class(klass) => {
match get_named_value(class_dict, "__class__") {
Some(cell) =>
cell_set_value(cell, Value::Class(klass))
None => ()
}
if get_named_value(klass.dict, "hashvalue") is None {
// Some metaclasses (notably EnumMeta) treat setting a
// new int-valued attribute as defining a new enum
// member. Avoid injecting our identity hash into such
// classes to keep stdlib enums (e.g. calendar.Month)
// behaving predictably.
let mut should_set = true
for base_value in base_values {
match base_value {
Value::Class(base_class) =>
if base_class.name == "Enum" ||
base_class.name == "IntEnum" ||
base_class.name == "StrEnum" {
should_set = false
break
}
_ => ()
}
}
if should_set {
set_named_value(
klass.dict,
"hashvalue",
Value::Int(fresh_object_hashvalue()),
)
}
}
if get_named_value(
klass.dict,
"__mpython_metaclass__",
)
is None {
match selected_meta {
Value::Class(meta) =>
set_named_value(
klass.dict,
"__mpython_metaclass__",
Value::Class(meta),
)
_ => ()
}
}
let _ = match class_mro(klass) {
Ok(_) => ()
Err(err) => return Err(err)
}
for pair in klass.dict {
match pair.1 {
Value::Instance(desc_inst) =>
if class_has_base_name(
desc_inst.class,
"property",
) {
if get_named_value(desc_inst.dict, "__name__")
is None {
set_named_value(
desc_inst.dict,
"__name__",
Value::Str(pair.0),
)
}
}
_ => ()
}
}
fn value_is_abstract(
value : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Bool, RuntimeError] {
match
get_attr_from_value(
value, "__isabstractmethod__", globals, builtins,
io,
) {
Ok(flag) =>
truthy_from_value_with_env(
flag, globals, builtins, io,
)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute => Ok(false)
_ =>
if err.exc_type == "AttributeError" {
Ok(false)
} else {
Err(err)
}
}
}
}
let abstract_names : Array[Value] = []
for pair in class_dict {
let is_abstract = match
value_is_abstract(pair.1, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
if is_abstract {
let _ = set_add_unique(
abstract_names,
Value::Str(pair.0),
)
}
}
let mut has_abstractmethods_attr = false
for base_value in base_values {
match base_value {
Value::Class(base_class) => {
let base_abs = get_named_value(
base_class.dict,
"__abstractmethods__",
)
if base_abs is Some(_) {
has_abstractmethods_attr = true
}
match base_abs {
Some(Value::Set(items))
| Some(Value::Tuple(items)) =>
for item in items {
match item {
Value::Str(name) =>
match lookup_class_attr(klass, name) {
Ok(Some(value)) => {
let is_abstract = match
value_is_abstract(
value, globals, builtins, io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
if is_abstract {
let _ = set_add_unique(
abstract_names,
Value::Str(name),
)
}
}
_ => ()
}
_ => ()
}
}
_ => ()
}
}
_ => ()
}
}
if abstract_names.length() > 0 ||
has_abstractmethods_attr {
set_named_value(
klass.dict,
"__abstractmethods__",
Value::Set(abstract_names),
)
}
let inherited_init_subclass : Value? = match
class_mro(klass) {
Ok(mro) => {
let mut found : Value? = None
for i = 1; i < mro.length(); i = i + 1 {
match
get_named_value(
mro[i].dict,
"__init_subclass__",
) {
Some(Value::Function(func)) => {
found = Some(
Value::BoundMethod(BoundMethodValue::{
function: func,
self: Value::Class(klass),
}),
)
break
}
_ => ()
}
}
found
}
Err(_) => None
}
match inherited_init_subclass {
Some(hook) => {
let _ = match
call_callable_with_env(
hook,
[],
class_kwargs,
globals,
builtins,
io,
) {
Ok(value) => value
Err(err) => return Err(err)
}
}
None => ()
}
let defined = match
apply_decorators(
Value::Class(klass),
decorator_values,
locals,
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
stack.push(defined)
Ok(())
}
other => {
let defined = match
apply_decorators(
other, decorator_values, locals, globals, builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
stack.push(defined)
Ok(())
}
}
}
inner()
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::StoreAnnotation(i) =>
match pop_stack(stack) {
Ok(ann_value) => {
let name = code.names[i]
let pairs = match
get_scoped_value(locals, globals, "__annotations__") {
Some(Value::Dict(pairs)) => pairs
Some(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__annotations__ must be a dict".to_string(),
),
)
None => {
let pairs : Array[(Value, Value)] = []
set_scoped_value(
locals,
globals,
"__annotations__",
Value::Dict(pairs),
)
pairs
}
}
let _ = match dict_set_item(pairs, Value::Str(name), ann_value) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(())
}
Err(err) => Err(err)
}
BcOp::AugAssignName(i, op) =>
match pop_stack(stack) {
Ok(rhs) => {
let name = code.names[i]
match lookup_name_value(name, locals, globals, builtins) {
Ok(current) =>
match
eval_augassign_op_values(
op, current, rhs, globals, builtins, io,
) {
Ok(next) => {
set_scoped_value(locals, globals, name, next)
Ok(())
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
}
Err(err) => Err(err)
}
BcOp::AugAssignAttr(i, op) =>
match pop_stack(stack) {
Ok(target) =>
match pop_stack(stack) {
Ok(rhs) => {
let attr = code.names[i]
match get_attr_from_value(target, attr, globals, builtins, io) {
Ok(current) =>
match
eval_augassign_op_values(
op, current, rhs, globals, builtins, io,
) {
Ok(next) =>
match
set_attr_on_value(
target, attr, next, globals, builtins, io,
) {
Ok(_) => Ok(())
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::AugAssignSubscr(op) =>
match pop_stack(stack) {
Ok(index) =>
match pop_stack(stack) {
Ok(target) =>
match pop_stack(stack) {
Ok(rhs) =>
match
get_subscr_value(target, index, globals, builtins, io) {
Ok(current) =>
match
eval_augassign_op_values(
op, current, rhs, globals, builtins, io,
) {
Ok(next) =>
match
set_subscr_value(
target, index, next, globals, builtins, io,
) {
Ok(_) => Ok(())
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
Err(err) => Err(err)
}
BcOp::ImportName(mod_idx, alias_idx) => {
let mod_value = code.consts[mod_idx]
let module_name = match mod_value {
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: ImportName expects module name str".to_string(),
),
)
}
match
import_module(module_name, globals, builtins, io, current_config()) {
Ok(module_value) => {
let binding = if alias_idx >= 0 {
code.names[alias_idx]
} else {
module_top_name(module_name)
}
let binding_value = if alias_idx >= 0 {
module_value
} else if module_name.contains(".") {
match module_cache_get(binding) {
Some(value) => value
None => module_value
}
} else {
module_value
}
set_scoped_value(locals, globals, binding, binding_value)
Ok(())
}
Err(err) => Err(err)
}
}
BcOp::FromImport(mod_idx, names_idx, aliases_idx) => {
let mod_value = code.consts[mod_idx]
let module_name = match mod_value {
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: FromImport expects module name str".to_string(),
),
)
}
let names_value = code.consts[names_idx]
let aliases_value = code.consts[aliases_idx]
let names = match names_value {
Value::Tuple(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: FromImport expects tuple of names".to_string(),
),
)
}
let aliases = match aliases_value {
Value::Tuple(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: FromImport expects tuple of aliases".to_string(),
),
)
}
if names.length() != aliases.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: FromImport expects equal names/aliases".to_string(),
),
)
}
let resolved_result = if module_name.length() == 0 {
Ok(module_name)
} else {
resolve_relative_module_name(module_name, globals)
}
match resolved_result {
Ok(resolved_name) => {
if resolved_name == "__future__" {
for item in names {
match item {
Value::Str(sym) =>
if sym == "annotations" {
set_global_value(
globals,
"__mpython_future_annotations__",
Value::Bool(true),
)
}
_ => ()
}
}
}
match
import_module(
resolved_name,
globals,
builtins,
io,
current_config(),
) {
Ok(module_value) => {
for i = 0; i < names.length(); i = i + 1 {
let name = match names[i] {
Value::Str(s) => s
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: FromImport name must be str".to_string(),
),
)
}
let alias_opt : String? = match aliases[i] {
Value::None => None
Value::Str(s) => Some(s)
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"bytecode vm: FromImport alias must be str/None".to_string(),
),
)
}
if name == "*" {
match module_value {
Value::Instance(inst) => {
let mut imported_any = false
match get_named_value(inst.dict, "__all__") {
Some(Value::List(items))
| Some(Value::Tuple(items)) =>
for item in items {
match item {
Value::Str(sym) =>
match
get_attr_from_value(
module_value, sym, globals, builtins, io,
) {
Ok(value) => {
set_scoped_value(
locals, globals, sym, value,
)
imported_any = true
}
Err(err) => return Err(err)
}
_ => ()
}
}
_ => ()
}
if !imported_any {
for pair in inst.dict {
if pair.0 != "__name__" && !pair.0.has_prefix("_") {
set_scoped_value(locals, globals, pair.0, pair.1)
}
}
}
}
_ => ()
}
continue
}
let imported = match
get_attr_from_value(
module_value, name, globals, builtins, io,
) {
Ok(value) => Ok(value)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute => {
let submodule = resolved_name + "." + name
match
import_module(
submodule,
globals,
builtins,
io,
current_config(),
) {
Ok(value) => Ok(value)
Err(err) => {
let missing_submodule_message = "ModuleNotFoundError: No module named '" +
submodule +
"'"
if err.exc_type == "ModuleNotFoundError" &&
err.message == missing_submodule_message {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ImportError: cannot import name '" +
name +
"' from '" +
resolved_name +
"'",
),
)
} else {
Err(err)
}
}
}
}
_ => Err(err)
}
}
match imported {
Ok(value) => {
let binding = match alias_opt {
Some(alias_name) => alias_name
None => name
}
set_scoped_value(locals, globals, binding, value)
}
Err(err) => return Err(err)
}
}
Ok(())
}
Err(err) => Err(err)
}
}
Err(err) => Err(err)
}
}
BcOp::CheckInFunctionScope(msg) =>
if !in_function_scope() {
Err(make_runtime_error(RuntimeErrorKind::Runtime, msg))
} else {
Ok(())
}
BcOp::CheckInCoroutineScope(msg) =>
if !coroutine_is_active() {
Err(make_runtime_error(RuntimeErrorKind::Runtime, msg))
} else {
Ok(())
}
BcOp::ReturnValue =>
match pop_stack(stack) {
Ok(v) =>
match vm_unwind_return(stack, blocks) {
Some(target) => {
pending_return = Some(v)
next_pc = target
Ok(())
}
None => {
if span is Some(_) {
pop_active_span()
}
return Ok(v)
}
}
Err(err) => Err(err)
}
}
match span {
Some(_) => pop_active_span()
None => ()
}
match step {
Ok(_) => pc = next_pc
Err(err) => {
// An exception overrides any pending return (including one in-flight from
// an inner finally handler).
pending_return = None
pending_jump_target = None
pending_jump_count = 0
let mut handler_pc : Int32? = None
let mut exc = err
// If we are currently handling another exception (inside an except/finally
// path), attach it as implicit context, mirroring CPython's behavior.
match active_exception() {
Some(ctx_err) =>
if exc.exc_context is None &&
exc.exc_cause is None &&
!exc.exc_suppress_context {
let ctx_value = exception_value_from_runtime_error(
ctx_err, globals, builtins, io,
)
exc = RuntimeError::{
kind: exc.kind,
message: exc.message,
span: exc.span,
traceback: exc.traceback,
traceback_envs: exc.traceback_envs,
exc_type: exc.exc_type,
exc_args: exc.exc_args,
exc_value: exc.exc_value,
exc_cause: exc.exc_cause,
exc_context: Some(ctx_value),
exc_suppress_context: exc.exc_suppress_context,
}
}
None => ()
}
let exc_for_handler = vm_trim_runtime_error_for_active_handler(exc)
// Unwind blocks until we find a handler.
while blocks.length() > 0 {
let blk = blocks.remove(blocks.length() - 1)
while stack.length() > blk.stack_height {
let _ = stack.remove(stack.length() - 1)
}
match blk.kind {
VmBlockKind::Except(target) => {
pending_exc = Some(exc_for_handler)
blocks.push(VmBlock::{
kind: VmBlockKind::ExceptHandler,
stack_height: stack.length(),
pushed_exc: true,
star_base_err: None,
star_remaining_group: None,
star_active_match: false,
})
active_exception_stack.val.push(exc_for_handler)
handler_pc = Some(target)
break
}
VmBlockKind::Finally(target) => {
pending_exc = Some(exc_for_handler)
blocks.push(VmBlock::{
kind: VmBlockKind::FinallyHandler,
stack_height: stack.length(),
pushed_exc: true,
star_base_err: None,
star_remaining_group: None,
star_active_match: false,
})
active_exception_stack.val.push(exc_for_handler)
handler_pc = Some(target)
break
}
VmBlockKind::ExceptHandler => {
// An exception raised while already in an except path.
if blk.pushed_exc {
let _ = active_exception_stack.val.pop()
}
pending_exc = None
continue
}
VmBlockKind::FinallyHandler => {
// An exception raised while already in a finally path.
if blk.pushed_exc {
let _ = active_exception_stack.val.pop()
}
pending_exc = None
continue
}
}
}
match handler_pc {
Some(target) => pc = target
None => return Err(exc)
}
}
}
}
// If bytecode falls off the end, return None.
Ok(Value::None)
}