///|
pub(all) enum EvaluationResult {
EvaluationSucceeded(Value)
EvaluationFailed(Diagnostic)
}
///|
/// Summary returned by a persistent VM garbage-collection pass.
pub(all) struct VmGcStats {
before_objects : Int
reachable_objects : Int
reclaimed_objects : Int
before_instances : Int
reachable_instances : Int
reclaimed_instances : Int
finalized_foreign_instances : Int
}
///|
priv struct Closure {
parameters : Array[String]
body : Array[Stmt]
environment : Environment
source_name : String
alive : Bool
}
///|
priv struct ClassDefinition {
name : String
superclass : Int?
inherits_sequence : Bool
methods : Array[MethodDecl]
is_foreign : Bool
foreign_binding : Int?
environment : Environment
static_fields : Array[InstanceField]
attributes : Value
source_name : String
alive : Bool
}
///|
priv struct ResolvedMethod {
declaration : MethodDecl
owner_identifier : Int
}
///|
priv struct InstanceField {
owner_identifier : Int
name : String
mut value : Value
}
///|
priv struct Instance {
class_identifier : Int
fields : Array[InstanceField]
foreign_handle : Int?
mut finalized : Bool
mut alive : Bool
}
///|
priv struct RuntimeList {
items : Array[Value]
mut alive : Bool
}
///|
priv struct RuntimeMapEntry {
key : Value
mut value : Value
}
///|
priv struct RuntimeMap {
entries : Array[RuntimeMapEntry]
mut alive : Bool
}
///|
priv struct RuntimeMapEntryValue {
key : Value
value : Value
alive : Bool
}
///|
priv enum RuntimeSequenceKind {
SequenceSnapshot(Array[Value])
SequenceMapView(Array[Value], String)
SequenceStringBytes(Array[Value])
SequenceStringCodePoints(Array[Value], Array[Value])
SequenceMap(Value, Int)
SequenceWhere(Value, Int)
SequenceSkip(Value, Int)
SequenceTake(Value, Int)
}
///|
priv enum IteratorAdvanceResult {
IteratorAdvanced(Value, Value)
IteratorComplete
IteratorAdvanceFailed(Diagnostic)
}
///|
priv enum CollectedValuesResult {
ValuesCollected(Array[Value])
ValueCollectionFailed(Diagnostic)
}
///|
priv struct RuntimeSequence {
kind : RuntimeSequenceKind
alive : Bool
}
///|
priv struct RuntimeIterator {
sequence_identifier : Int
mut source_iterator : Value
mut current : Value
mut yielded : Int
mut started : Bool
mut completed : Bool
alive : Bool
}
///|
priv struct RuntimeFiber {
function_identifier : Int
arguments : Array[Value]
identity : Int
is_call_frame : Bool
mut environment : Environment
mut initialized : Bool
mut state : Int
mut next_statement : Int
mut pending : ((Value) -> FiberStatementStep)?
mut last : Value
mut error : Value
alive : Bool
}
///|
priv struct RuntimeModule {
name : String
environment : Environment
mut state : Int
}
///|
/// Runtime state owned by `Vm`. Its fields and constructors remain internal;
/// embedders interact with it through the persistent VM API.
priv struct RuntimeContext {
start_clock : @bench.Timestamp
output : Array[String]
mut output_line_open : Bool
closures : Array[Closure]
classes : Array[ClassDefinition]
instances : Array[Instance]
lists : Array[RuntimeList]
maps : Array[RuntimeMap]
map_entries : Array[RuntimeMapEntryValue]
sequences : Array[RuntimeSequence]
iterators : Array[RuntimeIterator]
fibers : Array[RuntimeFiber]
free_instances : Array[Int]
free_lists : Array[Int]
free_maps : Array[Int]
free_map_entries : Array[Int]
free_sequences : Array[Int]
free_fibers : Array[Int]
modules : Array[RuntimeModule]
foreign_methods : Array[ForeignMethodBinding]
foreign_classes : Array[ForeignClassBinding]
loader : (String, String) -> ModuleLoadResult
mut current_source : String
mut steps : Int
max_steps : Int
mut call_depth : Int
max_call_depth : Int
mut peak_call_depth : Int
mut foreign_calls : Int
mut output_characters : Int
max_output_characters : Int
max_modules : Int
max_objects : Int
max_source_characters : Int
should_cancel : () -> Bool
mut current_fiber : Int
mut transfer_target : Int
mut transferred_to_main : Bool
mut terminate_main : Bool
mut gc_requested : Bool
mut debugger : RuntimeDebugger?
builtin_random_states : Array[BuiltinRandomState]
}
///|
fn RuntimeContext::new_with_loader(
max_steps : Int,
source_name : String,
loader : (String, String) -> ModuleLoadResult,
foreign_methods : Array[ForeignMethodBinding],
foreign_classes : Array[ForeignClassBinding],
) -> RuntimeContext {
RuntimeContext::new_with_limits(
max_steps,
64,
2147483647,
2147483647,
2147483647,
2147483647,
source_name,
loader,
foreign_methods,
foreign_classes,
fn() { false },
)
}
///|
fn RuntimeContext::new_with_limits(
max_steps : Int,
max_call_depth : Int,
max_output_characters : Int,
max_modules : Int,
max_objects : Int,
max_source_characters : Int,
source_name : String,
loader : (String, String) -> ModuleLoadResult,
foreign_methods : Array[ForeignMethodBinding],
foreign_classes : Array[ForeignClassBinding],
should_cancel : () -> Bool,
) -> RuntimeContext {
let runtime_foreign_methods : Array[ForeignMethodBinding] = []
for binding in foreign_methods {
runtime_foreign_methods.push(binding)
}
let runtime_foreign_classes : Array[ForeignClassBinding] = []
for binding in foreign_classes {
runtime_foreign_classes.push(binding)
}
let builtin_random_states : Array[BuiltinRandomState] = []
append_optional_bindings(
runtime_foreign_methods, runtime_foreign_classes, builtin_random_states,
)
{
start_clock: @bench.monotonic_clock_start(),
output: [],
output_line_open: false,
closures: [],
classes: [],
instances: [],
lists: [],
maps: [],
map_entries: [],
sequences: [],
iterators: [],
fibers: [
{
function_identifier: -1,
arguments: [],
identity: 0,
is_call_frame: false,
environment: Environment::new(),
initialized: true,
state: 1,
next_statement: 0,
pending: None,
last: Value::null(),
error: Value::null(),
alive: true,
},
],
free_instances: [],
free_lists: [],
free_maps: [],
free_map_entries: [],
free_sequences: [],
free_fibers: [],
modules: [],
foreign_methods: runtime_foreign_methods,
foreign_classes: runtime_foreign_classes,
loader,
current_source: source_name,
steps: 0,
max_steps,
call_depth: 0,
max_call_depth,
peak_call_depth: 0,
foreign_calls: 0,
output_characters: 0,
max_output_characters,
max_modules,
max_objects,
max_source_characters,
should_cancel,
current_fiber: 0,
transfer_target: -1,
transferred_to_main: false,
terminate_main: false,
gc_requested: false,
debugger: None,
builtin_random_states,
}
}
///|
fn RuntimeContext::new(max_steps : Int) -> RuntimeContext {
RuntimeContext::new_with_loader(
max_steps,
"",
fn(_, name) { ModuleLoadFailed("module '\{name}' is not available") },
[],
[],
)
}
///|
fn RuntimeContext::begin_operation(self : RuntimeContext) -> Unit {
self.steps = 0
self.call_depth = 0
self.peak_call_depth = 0
self.foreign_calls = 0
self.output.clear()
self.output_line_open = false
self.output_characters = 0
self.transferred_to_main = false
self.terminate_main = false
}
///|
fn RuntimeContext::push_call(self : RuntimeContext) -> Unit {
self.call_depth = self.call_depth + 1
if self.call_depth > self.peak_call_depth {
self.peak_call_depth = self.call_depth
}
}
///|
fn RuntimeContext::pop_call(self : RuntimeContext) -> Unit {
self.call_depth = self.call_depth - 1
}
///|
fn RuntimeContext::allocated_objects(self : RuntimeContext) -> Int {
let mut live = self.instances.length() -
self.free_instances.length() +
self.lists.length() -
self.free_lists.length() +
self.maps.length() -
self.free_maps.length() +
self.map_entries.length() -
self.free_map_entries.length() +
self.sequences.length() -
self.free_sequences.length() +
self.fibers.length() -
self.free_fibers.length()
for closure in self.closures {
if closure.alive {
live = live + 1
}
}
for class_definition in self.classes {
if class_definition.alive {
live = live + 1
}
}
for iterator in self.iterators {
if iterator.alive {
live = live + 1
}
}
live
}
///|
fn RuntimeContext::new_instance(
self : RuntimeContext,
class_identifier : Int,
foreign_handle : Int?,
) -> Int {
match self.free_instances.pop() {
Some(identifier) => {
self.instances[identifier] = {
class_identifier,
fields: [],
foreign_handle,
finalized: false,
alive: true,
}
return identifier
}
None => ()
}
let identifier = self.instances.length()
self.instances.push({
class_identifier,
fields: [],
foreign_handle,
finalized: false,
alive: true,
})
identifier
}
///|
fn RuntimeContext::new_list(
self : RuntimeContext,
items : Array[Value],
) -> Value {
match self.free_lists.pop() {
Some(identifier) => {
self.lists[identifier] = { items, alive: true, }
return Value::list(identifier)
}
None => ()
}
let identifier = self.lists.length()
self.lists.push({ items, alive: true, })
Value::list(identifier)
}
///|
fn RuntimeContext::new_map(self : RuntimeContext) -> Value {
match self.free_maps.pop() {
Some(identifier) => {
self.maps[identifier] = { entries: [], alive: true, }
return Value::map(identifier)
}
None => ()
}
let identifier = self.maps.length()
self.maps.push({ entries: [], alive: true, })
Value::map(identifier)
}
///|
fn RuntimeContext::new_fiber(
self : RuntimeContext,
function_identifier : Int,
) -> Value {
match self.free_fibers.pop() {
Some(identifier) => {
self.fibers[identifier] = {
function_identifier,
arguments: [],
identity: identifier,
is_call_frame: false,
environment: Environment::new(),
initialized: false,
state: 0,
next_statement: 0,
pending: None,
last: Value::null(),
error: Value::null(),
alive: true,
}
return Value::fiber(identifier)
}
None => ()
}
let identifier = self.fibers.length()
self.fibers.push({
function_identifier,
arguments: [],
identity: identifier,
is_call_frame: false,
environment: Environment::new(),
initialized: false,
state: 0,
next_statement: 0,
pending: None,
last: Value::null(),
error: Value::null(),
alive: true,
})
Value::fiber(identifier)
}
///|
fn RuntimeContext::new_function_fiber(
self : RuntimeContext,
function_identifier : Int,
arguments : Array[Value],
identity : Int,
) -> Int {
match self.free_fibers.pop() {
Some(identifier) => {
self.fibers[identifier] = {
function_identifier,
arguments,
identity,
is_call_frame: true,
environment: Environment::new(),
initialized: false,
state: 0,
next_statement: 0,
pending: None,
last: Value::null(),
error: Value::null(),
alive: true,
}
return identifier
}
None => ()
}
let identifier = self.fibers.length()
self.fibers.push({
function_identifier,
arguments,
identity,
is_call_frame: true,
environment: Environment::new(),
initialized: false,
state: 0,
next_statement: 0,
pending: None,
last: Value::null(),
error: Value::null(),
alive: true,
})
identifier
}
///|
fn RuntimeContext::new_sequence(
self : RuntimeContext,
items : Array[Value],
) -> Value {
self.new_sequence_kind(SequenceSnapshot(items))
}
///|
fn RuntimeContext::new_sequence_kind(
self : RuntimeContext,
kind : RuntimeSequenceKind,
) -> Value {
match self.free_sequences.pop() {
Some(identifier) => {
self.sequences[identifier] = { kind, alive: true, }
return Value::sequence(identifier)
}
None => ()
}
let identifier = self.sequences.length()
self.sequences.push({ kind, alive: true, })
Value::sequence(identifier)
}
///|
fn RuntimeContext::new_map_entry(
self : RuntimeContext,
key : Value,
value : Value,
) -> Value {
match self.free_map_entries.pop() {
Some(identifier) => {
self.map_entries[identifier] = { key, value, alive: true, }
return Value::map_entry(identifier)
}
None => ()
}
let identifier = self.map_entries.length()
self.map_entries.push({ key, value, alive: true, })
Value::map_entry(identifier)
}
///|
fn bind_core_globals(environment : Environment) -> Unit {
ignore(environment.bind("System", Value::system()))
ignore(environment.bind("Object", Value::core_class(ObjectClass)))
ignore(environment.bind("Bool", Value::core_class(BoolClass)))
ignore(environment.bind("Class", Value::core_class(ClassClass)))
ignore(environment.bind("Fiber", Value::core_class(FiberClass)))
ignore(environment.bind("Fn", Value::core_class(FnClass)))
ignore(environment.bind("List", Value::core_class(ListClass)))
ignore(environment.bind("Map", Value::core_class(MapClass)))
ignore(environment.bind("MapEntry", Value::core_class(MapEntryClass)))
ignore(
environment.bind("MapKeySequence", Value::core_class(MapKeySequenceClass)),
)
ignore(
environment.bind(
"MapValueSequence",
Value::core_class(MapValueSequenceClass),
),
)
ignore(environment.bind("Null", Value::core_class(NullClass)))
ignore(environment.bind("Num", Value::core_class(NumClass)))
ignore(environment.bind("Range", Value::core_class(RangeClass)))
ignore(environment.bind("Sequence", Value::core_class(SequenceClass)))
ignore(environment.bind("String", Value::core_class(StringClass)))
ignore(
environment.bind(
"StringByteSequence",
Value::core_class(StringByteSequenceClass),
),
)
ignore(
environment.bind(
"StringCodePointSequence",
Value::core_class(StringCodePointSequenceClass),
),
)
}
///|
fn find_map_key(entries : Array[RuntimeMapEntry], key : Value) -> Int? {
for index = 0; index < entries.length(); index = index + 1 {
if runtime_values_equal(entries[index].key, key) {
return Some(index)
}
}
None
}
///|
fn integer_arrays_equal(left : Array[Int], right : Array[Int]) -> Bool {
if left.length() != right.length() {
return false
}
for index = 0; index < left.length(); index = index + 1 {
if left[index] != right[index] {
return false
}
}
true
}
///|
fn runtime_values_equal(left : Value, right : Value) -> Bool {
match (string_bytes_of(left), string_bytes_of(right)) {
(Some(left_bytes), Some(right_bytes)) =>
integer_arrays_equal(left_bytes, right_bytes)
_ => left == right
}
}
///|
fn is_valid_map_key(value : Value) -> Bool {
match value {
NullValue
| BoolValue(_)
| NumberValue(_)
| StringValue(_)
| RawStringValue(_)
| RangeValue(_, _, _)
| ClassValue(_)
| CoreClassValue(_) => true
_ => false
}
}
///|
fn invalid_map_key(value : Value, span : SourceSpan) -> EvaluationResult {
evaluation_error(
InvalidMapKey,
"Map keys must be immutable values, received \{value.type_name()}",
span,
)
}
///|
fn RuntimeContext::set_map_entry(
self : RuntimeContext,
identifier : Int,
key : Value,
value : Value,
) -> Bool {
if identifier < 0 || identifier >= self.maps.length() {
return false
}
let entries = self.maps[identifier].entries
match find_map_key(entries, key) {
Some(index) => entries[index].value = value
None => entries.push({ key, value, })
}
true
}
///|
fn runtime_value_to_text(
value : Value,
context : RuntimeContext,
depth : Int,
) -> String {
if depth >= 8 {
return match value {
ListValue(_) => "[...]"
MapValue(_) => "{...}"
SequenceValue(_) => "(...)"
_ => value.to_text()
}
}
match value {
RawStringValue(bytes) => string_display_from_bytes(bytes)
ListValue(identifier) =>
if identifier < 0 || identifier >= context.lists.length() {
""
} else {
let items = context.lists[identifier].items
let mut text = "["
for index = 0; index < items.length(); index = index + 1 {
if index > 0 {
text = text + ", "
}
text = text + runtime_value_to_text(items[index], context, depth + 1)
}
text + "]"
}
MapValue(identifier) =>
if identifier < 0 || identifier >= context.maps.length() {
"