///|
fn Vm::import_file(
self : Vm,
path : String,
raw : Bool,
from : String,
loc : Location,
) -> Value raise {
let request = (from, path)
let source = match self.sources.get(request) {
Some(s) => s
None => {
if self.accessed_sources.length() >= self.limits.imports {
raise problem(Limit, loc, "import count limit exceeded")
}
guard self.resolver is Some(resolve) else {
raise problem(Import, loc, "imports disabled: no resolver supplied")
}
let s = resolve(from, path) catch {
Error(d) => {
d.context.push(loc)
raise Error(d)
}
e => raise problem(Import, loc, e.to_string())
}
s
}
}
if !self.accessed_sources.contains((from, path, false)) {
self.account_import(
from,
path,
false,
@utf8.encode(source.text).length(),
loc,
)
self.sources[request] = source
}
let key = (source.id, raw)
if self.imports.get(key) is Some(t) {
if self.thunks[t] is Busy || self.thunks[t] is Running(_) {
raise problem(Import, loc, "cyclic import '\{source.id}'")
}
return self.force(t, loc)
}
if raw {
let v = Str(source.text)
self.imports[key] = self.store(Ready(v))
return v
}
let expression = try {
if self.parsed_imports.get(source.id) is Some(expr) {
check_ast_depth(expr, self.limits.depth)
expr
} else {
let expr = parse(source.text, source.id, self.limits.depth)
self.parsed_imports[source.id] = expr
expr
}
} catch {
Error(d) => {
d.context.push(loc)
// Go's importer reports compile failures as runtime errors at the import
// call. Preserve the public category while capturing that execution site.
let runtime = self.traced({ ..d, kind: Runtime, trace: [], })
raise Error({ ..runtime, kind: d.kind, })
}
e => raise e
}
let t = self.store(Pending(expression, self.base_env(file=source.id)))
self.imports[key] = t
self.force(t, loc) catch {
Error(d) => {
d.context.push(loc)
raise Error(d)
}
e => raise e
}
}
///|
fn Vm::base_env(
self : Vm,
file? : String = "",
import_from? : String,
) -> Env raise {
let import_from = import_from.unwrap_or(file)
let env : Env = {
parent: None,
names: Map([]),
import_from,
debug_visible: None,
diagnostic_context: ("$", "anonymous"),
diagnostic_suppressed: false,
}
let loc : Location = { file, line: 1, column: 1, }
let fields : Map[String, LayerField] = Map([])
for name in builtin_names() {
if source_standard_function(name) {
continue
}
fields[name] = {
field: {
key: { kind: Literal(Json::string(name)), loc, end: None, },
value: { kind: StandardFunction(name), loc, end: None, },
visibility: 2,
additive: false,
},
env,
}
}
fields["thisFile"] = {
field: {
key: { kind: Literal(Json::string("thisFile")), loc, end: None, },
value: { kind: StandardValue(Json::string(file)), loc, end: None, },
visibility: 2,
additive: false,
},
env,
}
guard self.eval(self.library, env) is ObjectValue(library_id, _) else {
raise problem(Runtime, loc, "standard library must be an object")
}
let layers = self.objects[library_id].layers.copy()
layers.push({ fields, locals: [], assertions: [], assertion_sites: [], env, })
let value = self.object_value(layers)
if value is ObjectValue(id, _) {
self.objects[id].standard = true
}
let library_thunk = self.store(Ready(value))
{
parent: None,
names: Map([("std", library_thunk), ("$std", library_thunk)]),
import_from,
debug_visible: Some(_tick => ["$std", "std"]),
diagnostic_context: ("$", "anonymous"),
diagnostic_suppressed: false,
}
}
///|
fn Vm::import_binary(
self : Vm,
path : String,
from : String,
loc : Location,
) -> Value raise {
let request = (from, path)
let source = match self.binary_sources.get(request) {
Some(s) => s
None => {
if self.accessed_sources.length() >= self.limits.imports {
raise problem(Limit, loc, "import count limit exceeded")
}
guard self.binary_resolver is Some(resolve) else {
raise problem(
Import,
loc,
"binary imports disabled: no binary resolver supplied",
)
}
let s = resolve(from, path) catch {
Error(d) => {
d.context.push(loc)
raise Error(d)
}
e => raise problem(Import, loc, e.to_string())
}
s
}
}
if !self.accessed_sources.contains((from, path, true)) {
self.account_import(from, path, true, source.bytes.length(), loc)
self.binary_sources[request] = source
}
if self.binary_imports.get(source.id) is Some(t) {
return self.force(t, loc)
}
let items = []
for b in source.bytes {
self.tick(loc)
items.push(self.store(Ready(Num(b.to_int().to_double()))))
}
let value = ArrayValue(items)
self.binary_imports[source.id] = self.store(Ready(value))
value
}