chore: tidy

This commit is contained in:
2026-02-20 10:25:38 +08:00
parent 45096f5254
commit e1517c338e
10 changed files with 113 additions and 142 deletions

View File

@@ -289,13 +289,14 @@ export const toStringFunc = (value: NixValue): NixString => {
return coerceToStringWithContext(value, StringCoercionMode.ToString, false);
};
export type JsonValue = number | boolean | string | null | { [key: string]: JsonValue } | Array<JsonValue>;
export const nixValueToJson = (
value: NixValue,
strict: boolean,
outContext: NixStringContext,
copyToStore: boolean,
seen: Set<NixValue> = new Set(),
): unknown => {
): JsonValue => {
const v = strict ? force(value) : value;
if (isThunk(v) || typeof v === "function")
@@ -358,7 +359,7 @@ export const nixValueToJson = (
return nixValueToJson(v.get("outPath") as NixValue, strict, outContext, copyToStore, seen);
}
const result: Record<string, unknown> = {};
const result: { [key: string]: JsonValue } = {};
const keys = Array.from(v.keys()).sort();
for (const key of keys) {
result[key] = nixValueToJson(v.get(key) as NixValue, strict, outContext, copyToStore, seen);

View File

@@ -7,7 +7,7 @@ import {
import { force } from "../thunk";
import { forceAttrs, forceList, forceStringNoCtx, forceStringValue } from "../type-assert";
import type { NixAttrs, NixValue } from "../types";
import { coerceToString, nixValueToJson, StringCoercionMode } from "./conversion";
import { coerceToString, type JsonValue, nixValueToJson, StringCoercionMode } from "./conversion";
export interface OutputInfo {
path: string;
@@ -205,9 +205,9 @@ const structuredAttrsExcludedKeys = new Set([
const specialAttrs = new Set(["args", "__ignoreNulls", "__contentAddressed", "__impure"]);
const sortedJsonStringify = (obj: Record<string, unknown>): string => {
const sortedJsonStringify = (obj: Record<string, JsonValue>): string => {
const sortedKeys = Object.keys(obj).sort();
const sortedObj: Record<string, unknown> = {};
const sortedObj: Record<string, JsonValue> = {};
for (const key of sortedKeys) {
sortedObj[key] = obj[key];
}
@@ -224,14 +224,14 @@ const extractEnv = (
const env = new Map<string, string>();
if (structuredAttrs) {
const jsonAttrs: Record<string, unknown> = {};
const jsonAttrs: Record<string, JsonValue> = {};
for (const [key, value] of attrs) {
if (!structuredAttrsExcludedKeys.has(key)) {
const forcedValue = force(value as NixValue);
const forcedValue = force(value);
if (ignoreNulls && forcedValue === null) {
continue;
}
jsonAttrs[key] = nixValueToJson(value as NixValue, true, outContext, true);
jsonAttrs[key] = nixValueToJson(value, true, outContext, true);
}
if (key === "allowedReferences") {

View File

@@ -27,7 +27,7 @@ export const deepSeq =
recurse(val);
}
} else if (isAttrs(forced)) {
for (const [_, val] of Object.entries(forced)) {
for (const [_, val] of forced.entries()) {
recurse(val);
}
}

View File

@@ -1,5 +1,5 @@
import { createThunk, force } from "../thunk";
import type { NixAttrs, NixValue } from "../types";
import type { NixAttrs, NixFunction, NixValue } from "../types";
import * as arithmetic from "./arithmetic";
import * as attrs from "./attrs";
import * as conversion from "./conversion";
@@ -24,31 +24,31 @@ export interface PrimopMetadata {
}
export const mkPrimop = (
func: (...args: NixValue[]) => NixValue,
func: NixFunction,
name: string,
arity: number,
applied: number = 0,
): ((...args: NixValue[]) => NixValue) => {
(func as unknown as Record<symbol, unknown>)[PRIMOP_METADATA] = {
func[PRIMOP_METADATA] = {
name,
arity,
applied,
} satisfies PrimopMetadata;
if (applied < arity - 1) {
const wrappedFunc = ((...args: NixValue[]) => {
const result = func(...args);
const wrappedFunc: NixFunction = ((arg: NixValue) => {
const result = func(arg);
if (typeof result === "function") {
return mkPrimop(result, name, arity, applied + args.length);
return mkPrimop(result, name, arity, applied + 1);
}
return result;
}) as (...args: NixValue[]) => NixValue;
});
(wrappedFunc as unknown as Record<symbol, unknown>)[PRIMOP_METADATA] = {
wrappedFunc[PRIMOP_METADATA] = {
name,
arity,
applied,
} satisfies PrimopMetadata;
};
return wrappedFunc;
}
@@ -57,8 +57,8 @@ export const mkPrimop = (
};
export const isPrimop = (
value: unknown,
): value is ((...args: never[]) => unknown) & { [PRIMOP_METADATA]: PrimopMetadata } => {
value: NixValue,
): value is NixFunction & { [PRIMOP_METADATA]: PrimopMetadata } => {
return (
typeof value === "function" &&
PRIMOP_METADATA in value &&
@@ -67,7 +67,7 @@ export const isPrimop = (
);
};
export const getPrimopMetadata = (func: unknown): PrimopMetadata | undefined => {
export const getPrimopMetadata = (func: NixValue): PrimopMetadata | undefined => {
if (isPrimop(func)) {
return func[PRIMOP_METADATA];
}

View File

@@ -318,10 +318,12 @@ export const splitVersion = (s: NixValue): NixValue => {
return components;
};
export const traceVerbose = (_e1: NixValue, e2: NixValue): NixStrictValue => {
// TODO: implement traceVerbose
return force(e2);
};
export const traceVerbose =
(_e1: NixValue) =>
(e2: NixValue): NixStrictValue => {
// TODO: implement traceVerbose
return force(e2);
};
export const tryEval = (e: NixValue): NixAttrs => {
try {

View File

@@ -39,36 +39,32 @@ export const printValue = (value: NixValue, seen: WeakSet<object> = new WeakSet(
return "<LAMBDA>";
}
if (typeof value === "object") {
if (IS_CYCLE in value) {
return "«repeated»";
}
if (seen.has(value)) {
return "«repeated»";
}
seen.add(value);
if (isNixPath(value)) {
return value.value;
}
if (isStringWithContext(value)) {
return printString(value.value);
}
if (Array.isArray(value)) {
const items = value.map((v) => printValue(v, seen)).join(" ");
return `[ ${items} ]`;
}
const entries = Object.entries(value)
.map(([k, v]) => `${printSymbol(k)} = ${printValue(v, seen)};`)
.join(" ");
return `{${entries ? ` ${entries} ` : " "}}`;
if (IS_CYCLE in value) {
return "«repeated»";
}
throw new Error("unreachable");
if (seen.has(value)) {
return "«repeated»";
}
seen.add(value);
if (isNixPath(value)) {
return value.value;
}
if (isStringWithContext(value)) {
return printString(value.value);
}
if (Array.isArray(value)) {
const items = value.map((v) => printValue(v, seen)).join(" ");
return `[ ${items} ]`;
}
const entries = [...value.entries()]
.map(([k, v]) => `${printSymbol(k)} = ${printValue(v, seen)};`)
.join(" ");
return `{${entries ? ` ${entries} ` : " "}}`;
};
const printString = (s: string): string => {

View File

@@ -1,3 +1,4 @@
import { PRIMOP_METADATA, type PrimopMetadata } from "./builtins";
import { HAS_CONTEXT, isStringWithContext, type StringWithContext } from "./string-context";
import { type CYCLE_MARKER, force, type NixThunk } from "./thunk";
import { forceAttrs, forceStringNoCtx } from "./type-assert";
@@ -28,7 +29,10 @@ export type NixNull = null;
export const ATTR_POSITIONS = Symbol("attrPositions");
export type NixList = NixValue[];
export type NixAttrs = Map<string, NixValue> & { [ATTR_POSITIONS]?: Map<string, number> };
export type NixFunction = ((arg: NixValue) => NixValue) & { args?: NixArgs };
export type NixFunction = ((arg: NixValue) => NixValue) & {
args?: NixArgs;
[PRIMOP_METADATA]?: PrimopMetadata;
};
export class NixArgs {
required: string[];
optional: string[];

View File

@@ -1,5 +1,6 @@
import type { NixRuntime } from "..";
import type { builtins } from "../builtins";
import { JsonValue } from "../builtins/conversion";
import type { FetchGitResult, FetchTarballResult, FetchUrlResult } from "../builtins/io";
import type {
assert,
@@ -15,7 +16,7 @@ import type {
import type { op } from "../operators";
import type { createThunk, force } from "../thunk";
import type { forceBool } from "../type-assert";
import type { mkAttrs, mkFunction, NixAttrs } from "../types";
import type { mkAttrs, mkFunction, NixAttrs, NixStrictValue } from "../types";
declare global {
var Nix: NixRuntime;
@@ -92,8 +93,8 @@ declare global {
function op_match(regex: string, text: string): (string | null)[] | null;
function op_split(regex: string, text: string): (string | (string | null)[])[];
function op_from_json(json: string): unknown;
function op_from_toml(toml: string): unknown;
function op_from_json(json: string): NixStrictValue;
function op_from_toml(toml: string): NixStrictValue;
function op_to_xml(e: NixValue): [string, string[]];
function op_finalize_derivation(

View File

@@ -120,11 +120,7 @@ pub(crate) struct Runtime<Ctx: RuntimeContext> {
rt: tokio::runtime::Runtime,
#[cfg(feature = "inspector")]
wait_for_inspector: bool,
is_thunk_symbol: v8::Global<v8::Symbol>,
primop_metadata_symbol: v8::Global<v8::Symbol>,
has_context_symbol: v8::Global<v8::Symbol>,
is_path_symbol: v8::Global<v8::Symbol>,
is_cycle_symbol: v8::Global<v8::Symbol>,
symbols: GlobalSymbols,
_marker: PhantomData<Ctx>,
}
@@ -166,13 +162,7 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
js_runtime.op_state().borrow_mut().put(RegexCache::new());
js_runtime.op_state().borrow_mut().put(DrvHashCache::new());
let (
is_thunk_symbol,
primop_metadata_symbol,
has_context_symbol,
is_path_symbol,
is_cycle_symbol,
) = {
let symbols = {
deno_core::scope!(scope, &mut js_runtime);
Self::get_symbols(scope)?
};
@@ -186,11 +176,7 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
.expect("failed to build tokio runtime"),
#[cfg(feature = "inspector")]
wait_for_inspector: inspector_options.wait,
is_thunk_symbol,
primop_metadata_symbol,
has_context_symbol,
is_path_symbol,
is_cycle_symbol,
symbols,
_marker: PhantomData,
})
}
@@ -236,34 +222,12 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
// Retrieve scope from JsRuntime
deno_core::scope!(scope, self.js_runtime);
let local_value = v8::Local::new(scope, &global_value);
let is_thunk_symbol = v8::Local::new(scope, &self.is_thunk_symbol);
let primop_metadata_symbol = v8::Local::new(scope, &self.primop_metadata_symbol);
let has_context_symbol = v8::Local::new(scope, &self.has_context_symbol);
let is_path_symbol = v8::Local::new(scope, &self.is_path_symbol);
let is_cycle_symbol = v8::Local::new(scope, &self.is_cycle_symbol);
let symbols = &self.symbols.local(scope);
Ok(to_value(
local_value,
scope,
is_thunk_symbol,
primop_metadata_symbol,
has_context_symbol,
is_path_symbol,
is_cycle_symbol,
))
Ok(to_value(local_value, scope, symbols))
}
/// get (IS_THUNK, PRIMOP_METADATA, HAS_CONTEXT, IS_PATH, IS_CYCLE)
#[allow(clippy::type_complexity)]
fn get_symbols(
scope: &ScopeRef,
) -> Result<(
v8::Global<v8::Symbol>,
v8::Global<v8::Symbol>,
v8::Global<v8::Symbol>,
v8::Global<v8::Symbol>,
v8::Global<v8::Symbol>,
)> {
fn get_symbols(scope: &ScopeRef) -> Result<GlobalSymbols> {
let global = scope.get_current_context().global(scope);
let nix_key = v8::String::new(scope, "Nix")
.ok_or_else(|| Error::internal("failed to create V8 String".into()))?;
@@ -295,18 +259,48 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
let is_path = get_symbol("IS_PATH")?;
let is_cycle = get_symbol("IS_CYCLE")?;
Ok((is_thunk, primop_metadata, has_context, is_path, is_cycle))
Ok(GlobalSymbols {
is_thunk,
primop_metadata,
has_context,
is_path,
is_cycle,
})
}
}
struct GlobalSymbols {
is_thunk: v8::Global<v8::Symbol>,
primop_metadata: v8::Global<v8::Symbol>,
has_context: v8::Global<v8::Symbol>,
is_path: v8::Global<v8::Symbol>,
is_cycle: v8::Global<v8::Symbol>,
}
impl GlobalSymbols {
fn local<'a>(&self, scope: &ScopeRef<'a, '_>) -> LocalSymbols<'a> {
LocalSymbols {
is_thunk: v8::Local::new(scope, &self.is_thunk),
primop_metadata: v8::Local::new(scope, &self.primop_metadata),
has_context: v8::Local::new(scope, &self.has_context),
is_path: v8::Local::new(scope, &self.is_path),
is_cycle: v8::Local::new(scope, &self.is_cycle),
}
}
}
struct LocalSymbols<'a> {
is_thunk: v8::Local<'a, v8::Symbol>,
primop_metadata: v8::Local<'a, v8::Symbol>,
has_context: v8::Local<'a, v8::Symbol>,
is_path: v8::Local<'a, v8::Symbol>,
is_cycle: v8::Local<'a, v8::Symbol>,
}
fn to_value<'a>(
val: LocalValue<'a>,
scope: &ScopeRef<'a, '_>,
is_thunk_symbol: LocalSymbol<'a>,
primop_metadata_symbol: LocalSymbol<'a>,
has_context_symbol: LocalSymbol<'a>,
is_path_symbol: LocalSymbol<'a>,
is_cycle_symbol: LocalSymbol<'a>,
symbols: &LocalSymbols<'a>,
) -> Value {
match () {
_ if val.is_big_int() => {
@@ -336,21 +330,13 @@ fn to_value<'a>(
let list = (0..len)
.map(|i| {
let val = val.get_index(scope, i).expect("infallible index operation");
to_value(
val,
scope,
is_thunk_symbol,
primop_metadata_symbol,
has_context_symbol,
is_path_symbol,
is_cycle_symbol,
)
to_value(val, scope, symbols)
})
.collect();
Value::List(List::new(list))
}
_ if val.is_function() => {
if let Some(primop) = to_primop(val, scope, primop_metadata_symbol) {
if let Some(primop) = to_primop(val, scope, symbols.primop_metadata) {
primop
} else {
Value::Func
@@ -369,34 +355,26 @@ fn to_value<'a>(
let val = array
.get_index(scope, i * 2 + 1)
.expect("infallible index operation");
let val = to_value(
val,
scope,
is_thunk_symbol,
primop_metadata_symbol,
has_context_symbol,
is_path_symbol,
is_cycle_symbol,
);
let val = to_value(val, scope, symbols);
(Symbol::new(Cow::Owned(key)), val)
})
.collect();
Value::AttrSet(AttrSet::new(attrs))
}
_ if val.is_object() => {
if is_thunk(val, scope, is_thunk_symbol) {
if is_thunk(val, scope, symbols.is_thunk) {
return Value::Thunk;
}
if is_cycle(val, scope, is_cycle_symbol) {
if is_cycle(val, scope, symbols.is_cycle) {
return Value::Repeated;
}
if let Some(path_val) = extract_path(val, scope, is_path_symbol) {
if let Some(path_val) = extract_path(val, scope, symbols.is_path) {
return Value::Path(path_val);
}
if let Some(string_val) = extract_string_with_context(val, scope, has_context_symbol) {
if let Some(string_val) = extract_string_with_context(val, scope, symbols.has_context) {
return Value::String(string_val);
}
@@ -412,18 +390,7 @@ fn to_value<'a>(
.expect("infallible index operation");
let val = val.get(scope, key).expect("infallible operation");
let key = key.to_rust_string_lossy(scope);
(
Symbol::from(key),
to_value(
val,
scope,
is_thunk_symbol,
primop_metadata_symbol,
has_context_symbol,
is_path_symbol,
is_cycle_symbol,
),
)
(Symbol::from(key), to_value(val, scope, symbols))
})
.collect();
Value::AttrSet(AttrSet::new(attrs))

View File

@@ -1,7 +1,7 @@
[files]
extend-exclude = [
"nix-js/tests/basic/regex.rs",
"nix-js/tests/lang",
"nix-js/tests/tests/regex.rs",
"nix-js/tests/tests/lang",
]
[default.extend-words]