Compare commits
7 Commits
3cc7c7be75
...
7eb3acf26f
| Author | SHA1 | Date | |
|---|---|---|---|
| 7eb3acf26f | |||
| b424f60f9f | |||
| 42031edac1 | |||
| 04dcadfd61 | |||
| c3c39bda0c | |||
| 782092b91e | |||
| ae5febd5dd |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,3 +7,6 @@ flamegraph*.svg
|
||||
perf.data*
|
||||
profile.json.gz
|
||||
prof.json
|
||||
*.cpuprofile
|
||||
*.cpuprofile.gz
|
||||
*v8.log*
|
||||
|
||||
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -1986,7 +1986,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nix-compat"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.snix.dev/snix/snix.git#db30e92b30e18ca4d813206ac1b60d1f670adb8c"
|
||||
source = "git+https://git.snix.dev/snix/snix.git#1b37f68842a7e5e226d9dc009e9a90d400c5fb14"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bstr",
|
||||
@@ -2009,7 +2009,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nix-compat-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.snix.dev/snix/snix.git#db30e92b30e18ca4d813206ac1b60d1f670adb8c"
|
||||
source = "git+https://git.snix.dev/snix/snix.git#1b37f68842a7e5e226d9dc009e9a90d400c5fb14"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3271,9 +3271,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn-match"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "783c4140d7ed89f37116e865b49e5a9fdd28608b9071a9dd1e158b50fc0a31fc"
|
||||
checksum = "54b8f0a9004d6aafa6a588602a1119e6cdaacec9921aa1605383e6e7d6258fd6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
8
Justfile
8
Justfile
@@ -21,3 +21,11 @@
|
||||
[no-exit-message]
|
||||
@evali expr:
|
||||
cargo run --release --features inspector -- --inspect-brk 127.0.0.1:9229 eval --expr '{{expr}}'
|
||||
|
||||
[no-exit-message]
|
||||
@replp:
|
||||
cargo run --release --features prof -- repl
|
||||
|
||||
[no-exit-message]
|
||||
@evalp expr:
|
||||
cargo run --release --features prof -- eval --expr '{{expr}}'
|
||||
|
||||
@@ -76,6 +76,7 @@ uuid = { version = "1", features = ["v4"], optional = true }
|
||||
|
||||
[features]
|
||||
inspector = ["dep:fastwebsockets", "dep:hyper", "dep:hyper-util", "dep:http-body-util", "dep:http", "dep:uuid"]
|
||||
prof = []
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.8", features = ["html_reports"] }
|
||||
|
||||
@@ -154,6 +154,6 @@ export const unsafeGetAttrPos =
|
||||
return null;
|
||||
}
|
||||
|
||||
const span = positions.get(name) as string;
|
||||
const span = positions.get(name) as number;
|
||||
return mkPos(span);
|
||||
};
|
||||
|
||||
@@ -2,11 +2,16 @@
|
||||
* Conversion and serialization builtin functions
|
||||
*/
|
||||
|
||||
import { addBuiltContext, mkStringWithContext, type NixStringContext } from "../string-context";
|
||||
import {
|
||||
addBuiltContext,
|
||||
mkStringWithContext,
|
||||
type NixStringContext,
|
||||
StringWithContext,
|
||||
} from "../string-context";
|
||||
import { force, isThunk } from "../thunk";
|
||||
import { forceFunction, forceStringNoCtx } from "../type-assert";
|
||||
import type { NixString, NixValue } from "../types";
|
||||
import { HAS_CONTEXT, IS_PATH, isNixPath, isStringWithContext } from "../types";
|
||||
import { isNixPath, isStringWithContext, NixPath } from "../types";
|
||||
import { isAttrs, isPath, typeOf } from "./type-check";
|
||||
|
||||
export const fromJSON = (e: NixValue): NixValue => {
|
||||
@@ -306,13 +311,13 @@ export const nixValueToJson = (
|
||||
if (typeof v === "number") return v;
|
||||
if (typeof v === "boolean") return v;
|
||||
if (typeof v === "string") return v;
|
||||
if (typeof v === "object" && HAS_CONTEXT in v) {
|
||||
if (v instanceof StringWithContext) {
|
||||
for (const elem of v.context) {
|
||||
outContext.add(elem);
|
||||
}
|
||||
return v.value;
|
||||
}
|
||||
if (typeof v === "object" && IS_PATH in v) {
|
||||
if (v instanceof NixPath) {
|
||||
if (copyToStore) {
|
||||
const storePath = Deno.core.ops.op_copy_path_to_store(v.value);
|
||||
outContext.add(storePath);
|
||||
|
||||
@@ -377,16 +377,16 @@ export const derivationStrict = (args: NixValue): NixAttrs => {
|
||||
const rustResult: {
|
||||
drvPath: string;
|
||||
outputs: [string, string][];
|
||||
} = Deno.core.ops.op_finalize_derivation({
|
||||
name: drvName,
|
||||
} = Deno.core.ops.op_finalize_derivation(
|
||||
drvName,
|
||||
builder,
|
||||
platform,
|
||||
outputs,
|
||||
args: drvArgs,
|
||||
env: envEntries,
|
||||
context: contextArray,
|
||||
fixedOutput: fixedOutputInfo,
|
||||
});
|
||||
drvArgs,
|
||||
envEntries,
|
||||
contextArray,
|
||||
fixedOutputInfo,
|
||||
);
|
||||
|
||||
const result: NixAttrs = new Map();
|
||||
|
||||
|
||||
@@ -30,9 +30,5 @@ export const convertHash = (args: NixValue): string => {
|
||||
|
||||
const toHashFormat = forceStringNoCtx(select(attrs, ["toHashFormat"]));
|
||||
|
||||
return Deno.core.ops.op_convert_hash({
|
||||
hash,
|
||||
hashAlgo,
|
||||
toHashFormat,
|
||||
});
|
||||
return Deno.core.ops.op_convert_hash(hash, hashAlgo, toHashFormat);
|
||||
};
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
forceStringNoCtx,
|
||||
forceStringValue,
|
||||
} from "../type-assert";
|
||||
import type { NixAttrs, NixPath, NixString, NixValue } from "../types";
|
||||
import { CatchableError, IS_PATH, isNixPath } from "../types";
|
||||
import type { NixAttrs, NixString, NixValue } from "../types";
|
||||
import { CatchableError, isNixPath, NixPath } from "../types";
|
||||
import { coerceToPath, coerceToString, StringCoercionMode } from "./conversion";
|
||||
import { baseNameOf } from "./path";
|
||||
import { isAttrs, isPath, isString } from "./type-check";
|
||||
@@ -49,8 +49,7 @@ export const importFunc = (path: NixValue): NixValue => {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const code = Deno.core.ops.op_import(pathStr);
|
||||
const result = Function(`return (${code})`)();
|
||||
const result = Deno.core.ops.op_import(pathStr);
|
||||
|
||||
importCache.set(pathStr, result);
|
||||
return result;
|
||||
@@ -85,24 +84,24 @@ export const fetchClosure = (_args: NixValue): never => {
|
||||
};
|
||||
|
||||
export interface FetchUrlResult {
|
||||
store_path: string;
|
||||
storePath: string;
|
||||
hash: string;
|
||||
}
|
||||
|
||||
export interface FetchTarballResult {
|
||||
store_path: string;
|
||||
nar_hash: string;
|
||||
storePath: string;
|
||||
narHash: string;
|
||||
}
|
||||
|
||||
export interface FetchGitResult {
|
||||
out_path: string;
|
||||
outPath: string;
|
||||
rev: string;
|
||||
short_rev: string;
|
||||
rev_count: number;
|
||||
last_modified: number;
|
||||
last_modified_date: string;
|
||||
shortRev: string;
|
||||
revCount: number;
|
||||
lastModified: number;
|
||||
lastModifiedDate: string;
|
||||
submodules: boolean;
|
||||
nar_hash: string | null;
|
||||
narHash: string | null;
|
||||
}
|
||||
|
||||
const normalizeUrlInput = (
|
||||
@@ -154,16 +153,16 @@ export const fetchurl = (args: NixValue): NixString => {
|
||||
executable ?? false,
|
||||
);
|
||||
const context: NixStringContext = new Set();
|
||||
addOpaqueContext(context, result.store_path);
|
||||
return mkStringWithContext(result.store_path, context);
|
||||
addOpaqueContext(context, result.storePath);
|
||||
return mkStringWithContext(result.storePath, context);
|
||||
};
|
||||
|
||||
export const fetchTarball = (args: NixValue): NixString => {
|
||||
const { url, name, sha256 } = normalizeTarballInput(args);
|
||||
const result: FetchTarballResult = Deno.core.ops.op_fetch_tarball(url, name ?? null, sha256 ?? null);
|
||||
const context: NixStringContext = new Set();
|
||||
addOpaqueContext(context, result.store_path);
|
||||
return mkStringWithContext(result.store_path, context);
|
||||
addOpaqueContext(context, result.storePath);
|
||||
return mkStringWithContext(result.storePath, context);
|
||||
};
|
||||
|
||||
export const fetchGit = (args: NixValue): NixAttrs => {
|
||||
@@ -173,16 +172,16 @@ export const fetchGit = (args: NixValue): NixAttrs => {
|
||||
const url = coerceToString(forced, StringCoercionMode.Base, false, disposedContext);
|
||||
const result = Deno.core.ops.op_fetch_git(url, null, null, false, false, false, null);
|
||||
const outContext: NixStringContext = new Set();
|
||||
addOpaqueContext(outContext, result.out_path);
|
||||
addOpaqueContext(outContext, result.outPath);
|
||||
return new Map<string, NixValue>([
|
||||
["outPath", mkStringWithContext(result.out_path, outContext)],
|
||||
["outPath", mkStringWithContext(result.outPath, outContext)],
|
||||
["rev", result.rev],
|
||||
["shortRev", result.short_rev],
|
||||
["revCount", BigInt(result.rev_count)],
|
||||
["lastModified", BigInt(result.last_modified)],
|
||||
["lastModifiedDate", result.last_modified_date],
|
||||
["shortRev", result.shortRev],
|
||||
["revCount", BigInt(result.revCount)],
|
||||
["lastModified", BigInt(result.lastModified)],
|
||||
["lastModifiedDate", result.lastModifiedDate],
|
||||
["submodules", result.submodules],
|
||||
["narHash", result.nar_hash],
|
||||
["narHash", result.narHash],
|
||||
]);
|
||||
}
|
||||
const attrs = forceAttrs(args);
|
||||
@@ -205,16 +204,16 @@ export const fetchGit = (args: NixValue): NixAttrs => {
|
||||
);
|
||||
|
||||
const outContext: NixStringContext = new Set();
|
||||
addOpaqueContext(outContext, result.out_path);
|
||||
addOpaqueContext(outContext, result.outPath);
|
||||
return new Map<string, NixValue>([
|
||||
["outPath", mkStringWithContext(result.out_path, outContext)],
|
||||
["outPath", mkStringWithContext(result.outPath, outContext)],
|
||||
["rev", result.rev],
|
||||
["shortRev", result.short_rev],
|
||||
["revCount", BigInt(result.rev_count)],
|
||||
["lastModified", BigInt(result.last_modified)],
|
||||
["lastModifiedDate", result.last_modified_date],
|
||||
["shortRev", result.shortRev],
|
||||
["revCount", BigInt(result.revCount)],
|
||||
["lastModified", BigInt(result.lastModified)],
|
||||
["lastModifiedDate", result.lastModifiedDate],
|
||||
["submodules", result.submodules],
|
||||
["narHash", result.nar_hash],
|
||||
["narHash", result.narHash],
|
||||
]);
|
||||
};
|
||||
|
||||
@@ -307,12 +306,7 @@ const autoDetectAndFetch = (attrs: NixAttrs): NixAttrs => {
|
||||
|
||||
export const readDir = (path: NixValue): NixAttrs => {
|
||||
const pathStr = realisePath(path);
|
||||
const entries: Record<string, string> = Deno.core.ops.op_read_dir(pathStr);
|
||||
const result: NixAttrs = new Map();
|
||||
for (const [name, type] of Object.entries(entries)) {
|
||||
result.set(name, type);
|
||||
}
|
||||
return result;
|
||||
return Deno.core.ops.op_read_dir(pathStr);
|
||||
};
|
||||
|
||||
export const readFile = (path: NixValue): string => {
|
||||
@@ -475,13 +469,13 @@ export const findFile =
|
||||
suffix.length > 0 ? Deno.core.ops.op_resolve_path(suffix, resolvedPath) : resolvedPath;
|
||||
|
||||
if (Deno.core.ops.op_path_exists(candidatePath)) {
|
||||
return { [IS_PATH]: true, value: candidatePath };
|
||||
return new NixPath(candidatePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (lookupPathStr.startsWith("nix/")) {
|
||||
// FIXME: special path type
|
||||
return { [IS_PATH]: true, value: `<${lookupPathStr}>` };
|
||||
return new NixPath(`<${lookupPathStr}>`);
|
||||
}
|
||||
|
||||
throw new CatchableError(`file '${lookupPathStr}' was not found in the Nix search path`);
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { NixAttrs, NixBool, NixPath, NixString, NixValue } from "./types";
|
||||
import { CatchableError, isNixPath } from "./types";
|
||||
|
||||
interface StackFrame {
|
||||
span: string;
|
||||
span: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ function enrichError(error: unknown): Error {
|
||||
return err;
|
||||
}
|
||||
|
||||
const pushContext = (message: string, span: string): void => {
|
||||
const pushContext = (message: string, span: number): void => {
|
||||
if (callStack.length >= MAX_STACK_DEPTH) {
|
||||
callStack.shift();
|
||||
}
|
||||
@@ -43,7 +43,7 @@ const popContext = (): void => {
|
||||
callStack.pop();
|
||||
};
|
||||
|
||||
export const withContext = <T>(message: string, span: string, fn: () => T): T => {
|
||||
export const withContext = <T>(message: string, span: number, fn: () => T): T => {
|
||||
pushContext(message, span);
|
||||
try {
|
||||
return fn();
|
||||
@@ -142,19 +142,22 @@ export const resolvePath = (currentDir: string, path: NixValue): NixPath => {
|
||||
return mkPath(resolved);
|
||||
};
|
||||
|
||||
export const select = (obj: NixValue, attrpath: NixValue[], span?: string): NixValue => {
|
||||
if (span) {
|
||||
const pathStrings = attrpath.map((a) => forceStringValue(a));
|
||||
const path = pathStrings.join(".");
|
||||
const message = path ? `while selecting attribute [${path}]` : "while selecting attribute";
|
||||
|
||||
export const select = (obj: NixValue, attrpath: NixValue[], span?: number): NixValue => {
|
||||
if (span !== undefined) {
|
||||
if (callStack.length >= MAX_STACK_DEPTH) {
|
||||
callStack.shift();
|
||||
}
|
||||
callStack.push({ span, message });
|
||||
const frame: StackFrame = { span, message: "while selecting attribute" };
|
||||
callStack.push(frame);
|
||||
try {
|
||||
return selectImpl(obj, attrpath);
|
||||
} catch (error) {
|
||||
try {
|
||||
const path = attrpath.map((a) => forceStringValue(a)).join(".");
|
||||
if (path) frame.message = `while selecting attribute [${path}]`;
|
||||
} catch {
|
||||
throw enrichError(error);
|
||||
}
|
||||
throw enrichError(error);
|
||||
} finally {
|
||||
callStack.pop();
|
||||
@@ -167,8 +170,8 @@ export const select = (obj: NixValue, attrpath: NixValue[], span?: string): NixV
|
||||
function selectImpl(obj: NixValue, attrpath: NixValue[]): NixValue {
|
||||
let attrs = forceAttrs(obj);
|
||||
|
||||
for (const attr of attrpath.slice(0, -1)) {
|
||||
const key = forceStringValue(attr);
|
||||
for (let i = 0; i < attrpath.length - 1; i++) {
|
||||
const key = forceStringValue(attrpath[i]);
|
||||
if (!attrs.has(key)) {
|
||||
throw new Error(`Attribute '${key}' not found`);
|
||||
}
|
||||
@@ -187,20 +190,23 @@ export const selectWithDefault = (
|
||||
obj: NixValue,
|
||||
attrpath: NixValue[],
|
||||
defaultVal: NixValue,
|
||||
span?: string,
|
||||
span?: number,
|
||||
): NixValue => {
|
||||
if (span) {
|
||||
const pathStrings = attrpath.map((a) => forceStringValue(a));
|
||||
const path = pathStrings.join(".");
|
||||
const message = path ? `while selecting attribute [${path}]` : "while selecting attribute";
|
||||
|
||||
if (span !== undefined) {
|
||||
if (callStack.length >= MAX_STACK_DEPTH) {
|
||||
callStack.shift();
|
||||
}
|
||||
callStack.push({ span, message });
|
||||
const frame: StackFrame = { span, message: "while selecting attribute" };
|
||||
callStack.push(frame);
|
||||
try {
|
||||
return selectWithDefaultImpl(obj, attrpath, defaultVal);
|
||||
} catch (error) {
|
||||
try {
|
||||
const path = attrpath.map((a) => forceStringValue(a)).join(".");
|
||||
if (path) frame.message = `while selecting attribute [${path}]`;
|
||||
} catch {
|
||||
throw enrichError(error);
|
||||
}
|
||||
throw enrichError(error);
|
||||
} finally {
|
||||
callStack.pop();
|
||||
@@ -216,8 +222,8 @@ function selectWithDefaultImpl(obj: NixValue, attrpath: NixValue[], defaultVal:
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
for (const attr of attrpath.slice(0, -1)) {
|
||||
const key = forceStringValue(attr);
|
||||
for (let i = 0; i < attrpath.length - 1; i++) {
|
||||
const key = forceStringValue(attrpath[i]);
|
||||
if (!attrs.has(key)) {
|
||||
return defaultVal;
|
||||
}
|
||||
@@ -242,8 +248,8 @@ export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
|
||||
}
|
||||
let attrs = forced;
|
||||
|
||||
for (const attr of attrpath.slice(0, -1)) {
|
||||
const key = forceStringNoCtx(attr);
|
||||
for (let i = 0; i < attrpath.length - 1; i++) {
|
||||
const key = forceStringNoCtx(attrpath[i]);
|
||||
if (!attrs.has(key)) {
|
||||
return false;
|
||||
}
|
||||
@@ -257,8 +263,8 @@ export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
|
||||
return attrs.has(forceStringValue(attrpath[attrpath.length - 1]));
|
||||
};
|
||||
|
||||
export const call = (func: NixValue, arg: NixValue, span?: string): NixValue => {
|
||||
if (span) {
|
||||
export const call = (func: NixValue, arg: NixValue, span?: number): NixValue => {
|
||||
if (span !== undefined) {
|
||||
if (callStack.length >= MAX_STACK_DEPTH) {
|
||||
callStack.shift();
|
||||
}
|
||||
@@ -276,19 +282,19 @@ export const call = (func: NixValue, arg: NixValue, span?: string): NixValue =>
|
||||
};
|
||||
|
||||
function callImpl(func: NixValue, arg: NixValue): NixValue {
|
||||
const forcedFunc = force(func);
|
||||
if (typeof forcedFunc === "function") {
|
||||
forcedFunc.args?.check(arg);
|
||||
return forcedFunc(arg);
|
||||
const forced = force(func);
|
||||
if (typeof forced === "function") {
|
||||
forced.args?.check(arg);
|
||||
return forced(arg);
|
||||
}
|
||||
if (forcedFunc instanceof Map && forcedFunc.has("__functor")) {
|
||||
const functor = forceFunction(forcedFunc.get("__functor") as NixValue);
|
||||
return call(functor(forcedFunc), arg);
|
||||
if (forced instanceof Map && forced.has("__functor")) {
|
||||
const functor = forceFunction(forced.get("__functor") as NixValue);
|
||||
return call(callImpl(functor, forced), arg);
|
||||
}
|
||||
throw new Error(`attempt to call something which is not a function but ${typeOf(forcedFunc)}`);
|
||||
throw new Error(`attempt to call something which is not a function but ${typeOf(forced)}`);
|
||||
}
|
||||
|
||||
export const assert = (assertion: NixValue, expr: NixValue, assertionRaw: string, span: string): NixValue => {
|
||||
export const assert = (assertion: NixValue, expr: NixValue, assertionRaw: string, span: number): NixValue => {
|
||||
if (forceBool(assertion)) {
|
||||
return expr;
|
||||
}
|
||||
@@ -298,15 +304,8 @@ export const assert = (assertion: NixValue, expr: NixValue, assertionRaw: string
|
||||
throw "unreachable";
|
||||
};
|
||||
|
||||
export const ifFunc = (cond: NixValue, consq: NixValue, alter: NixValue) => {
|
||||
if (forceBool(cond)) {
|
||||
return consq;
|
||||
}
|
||||
return alter;
|
||||
};
|
||||
|
||||
export const mkPos = (span: string): NixAttrs => {
|
||||
return new Map(Object.entries(Deno.core.ops.op_decode_span(span)));
|
||||
export const mkPos = (span: number): NixAttrs => {
|
||||
return Deno.core.ops.op_decode_span(span);
|
||||
};
|
||||
|
||||
interface WithScope {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import { builtins, PRIMOP_METADATA } from "./builtins";
|
||||
import { coerceToString, StringCoercionMode } from "./builtins/conversion";
|
||||
import {
|
||||
assert,
|
||||
call,
|
||||
@@ -16,40 +15,31 @@ import {
|
||||
resolvePath,
|
||||
select,
|
||||
selectWithDefault,
|
||||
withContext,
|
||||
} from "./helpers";
|
||||
import { op } from "./operators";
|
||||
import { HAS_CONTEXT } from "./string-context";
|
||||
import {
|
||||
createThunk,
|
||||
DEBUG_THUNKS,
|
||||
force,
|
||||
forceDeep,
|
||||
forceShallow,
|
||||
IS_CYCLE,
|
||||
IS_THUNK,
|
||||
isThunk,
|
||||
} from "./thunk";
|
||||
import { createThunk, DEBUG_THUNKS, force, forceDeep, forceShallow, IS_CYCLE, IS_THUNK } from "./thunk";
|
||||
import { forceBool } from "./type-assert";
|
||||
import { ATTR_POSITIONS, IS_PATH, mkAttrs, mkAttrsWithPos, mkFunction, type NixValue } from "./types";
|
||||
import { IS_PATH, mkAttrs, mkFunction, type NixValue } from "./types";
|
||||
|
||||
export type NixRuntime = typeof Nix;
|
||||
|
||||
const replBindings: Map<string, NixValue> = new Map;
|
||||
const replBindings: Map<string, NixValue> = new Map();
|
||||
|
||||
export const Nix = {
|
||||
createThunk,
|
||||
force,
|
||||
forceShallow,
|
||||
forceDeep,
|
||||
forceBool,
|
||||
isThunk,
|
||||
IS_THUNK,
|
||||
IS_CYCLE,
|
||||
HAS_CONTEXT,
|
||||
IS_PATH,
|
||||
PRIMOP_METADATA,
|
||||
DEBUG_THUNKS,
|
||||
|
||||
createThunk,
|
||||
force,
|
||||
forceBool,
|
||||
forceShallow,
|
||||
forceDeep,
|
||||
|
||||
assert,
|
||||
call,
|
||||
hasAttr,
|
||||
@@ -57,20 +47,13 @@ export const Nix = {
|
||||
selectWithDefault,
|
||||
lookupWith,
|
||||
resolvePath,
|
||||
coerceToString,
|
||||
concatStringsWithContext,
|
||||
StringCoercionMode,
|
||||
mkAttrs,
|
||||
mkAttrsWithPos,
|
||||
mkFunction,
|
||||
mkPos,
|
||||
ATTR_POSITIONS,
|
||||
|
||||
withContext,
|
||||
|
||||
op,
|
||||
builtins,
|
||||
PRIMOP_METADATA,
|
||||
|
||||
replBindings,
|
||||
setReplBinding: (name: string, value: NixValue) => {
|
||||
@@ -80,3 +63,29 @@ export const Nix = {
|
||||
};
|
||||
|
||||
globalThis.Nix = Nix;
|
||||
globalThis.$t = createThunk;
|
||||
globalThis.$f = force;
|
||||
globalThis.$fb = forceBool;
|
||||
globalThis.$a = assert;
|
||||
globalThis.$c = call;
|
||||
globalThis.$h = hasAttr;
|
||||
globalThis.$s = select;
|
||||
globalThis.$sd = selectWithDefault;
|
||||
globalThis.$l = lookupWith;
|
||||
globalThis.$r = resolvePath;
|
||||
globalThis.$cs = concatStringsWithContext;
|
||||
globalThis.$ma = mkAttrs;
|
||||
globalThis.$mf = mkFunction;
|
||||
globalThis.$mp = mkPos;
|
||||
globalThis.$gb = Nix.getReplBinding;
|
||||
|
||||
globalThis.$oa = op.add;
|
||||
globalThis.$os = op.sub;
|
||||
globalThis.$om = op.mul;
|
||||
globalThis.$od = op.div;
|
||||
globalThis.$oe = op.eq;
|
||||
globalThis.$ol = op.lt;
|
||||
globalThis.$og = op.gt;
|
||||
globalThis.$oc = op.concat;
|
||||
globalThis.$ou = op.update;
|
||||
globalThis.$b = builtins;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IS_PATH, type NixPath } from "./types";
|
||||
import { NixPath } from "./types";
|
||||
|
||||
const canonicalizePath = (path: string): string => {
|
||||
const parts: string[] = [];
|
||||
@@ -30,7 +30,7 @@ const canonicalizePath = (path: string): string => {
|
||||
};
|
||||
|
||||
export const mkPath = (value: string): NixPath => {
|
||||
return { [IS_PATH]: true, value: canonicalizePath(value) };
|
||||
return new NixPath(canonicalizePath(value));
|
||||
};
|
||||
|
||||
export const getPathValue = (p: NixPath): string => {
|
||||
|
||||
@@ -22,18 +22,22 @@ export type StringContextElem = StringContextOpaque | StringContextDrvDeep | Str
|
||||
|
||||
export type NixStringContext = Set<string>;
|
||||
|
||||
export interface StringWithContext {
|
||||
readonly [HAS_CONTEXT]: true;
|
||||
export class StringWithContext {
|
||||
readonly [HAS_CONTEXT] = true as const;
|
||||
value: string;
|
||||
context: NixStringContext;
|
||||
constructor(value: string, context: NixStringContext) {
|
||||
this.value = value;
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
|
||||
export const isStringWithContext = (v: NixStrictValue): v is StringWithContext => {
|
||||
return typeof v === "object" && v !== null && HAS_CONTEXT in v;
|
||||
return v instanceof StringWithContext;
|
||||
};
|
||||
|
||||
export const mkStringWithContext = (value: string, context: NixStringContext): StringWithContext => {
|
||||
return { [HAS_CONTEXT]: true, value, context };
|
||||
return new StringWithContext(value, context);
|
||||
};
|
||||
|
||||
export const mkPlainString = (value: string): string => value;
|
||||
@@ -45,11 +49,12 @@ export const getStringValue = (s: string | StringWithContext): string => {
|
||||
return s;
|
||||
};
|
||||
|
||||
const emptyContext: NixStringContext = new Set();
|
||||
export const getStringContext = (s: string | StringWithContext): NixStringContext => {
|
||||
if (isStringWithContext(s)) {
|
||||
return s.context;
|
||||
}
|
||||
return new Set();
|
||||
return emptyContext;
|
||||
};
|
||||
|
||||
export const mergeContexts = (...contexts: NixStringContext[]): NixStringContext => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { isAttrs, isList } from "./builtins/type-check";
|
||||
import { HAS_CONTEXT } from "./string-context";
|
||||
import type { NixAttrs, NixStrictValue, NixThunkInterface, NixValue } from "./types";
|
||||
import { IS_PATH } from "./types";
|
||||
import { StringWithContext } from "./string-context";
|
||||
import type { NixAttrs, NixStrictValue, NixValue } from "./types";
|
||||
import { NixPath } from "./types";
|
||||
|
||||
export const IS_THUNK = Symbol("is_thunk");
|
||||
|
||||
@@ -21,8 +21,7 @@ export const DEBUG_THUNKS = { enabled: true };
|
||||
* - Evaluating (blackhole): func is undefined, result is undefined
|
||||
* - Evaluated: func is undefined, result is defined
|
||||
*/
|
||||
export class NixThunk implements NixThunkInterface {
|
||||
[key: symbol]: unknown;
|
||||
export class NixThunk {
|
||||
readonly [IS_THUNK] = true as const;
|
||||
func: (() => NixValue) | undefined;
|
||||
result: NixStrictValue | undefined;
|
||||
@@ -42,8 +41,8 @@ export class NixThunk implements NixThunkInterface {
|
||||
}
|
||||
}
|
||||
|
||||
export const isThunk = (value: NixValue): value is NixThunkInterface => {
|
||||
return value !== null && typeof value === "object" && IS_THUNK in value && value[IS_THUNK] === true;
|
||||
export const isThunk = (value: NixValue): value is NixThunk => {
|
||||
return value instanceof NixThunk;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -112,7 +111,7 @@ export const force = (value: NixValue): NixStrictValue => {
|
||||
}
|
||||
};
|
||||
|
||||
export const createThunk = (func: () => NixValue, label?: string): NixThunkInterface => {
|
||||
export const createThunk = (func: () => NixValue, label?: string): NixThunk => {
|
||||
return new NixThunk(func, label);
|
||||
};
|
||||
|
||||
@@ -142,7 +141,7 @@ export const forceDeep = (value: NixValue, seen: WeakSet<object> = new WeakSet()
|
||||
seen.add(forced);
|
||||
}
|
||||
|
||||
if (HAS_CONTEXT in forced || IS_PATH in forced) {
|
||||
if (forced instanceof StringWithContext || forced instanceof NixPath) {
|
||||
return forced;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import { HAS_CONTEXT, isStringWithContext, type StringWithContext } from "./string-context";
|
||||
import { type CYCLE_MARKER, force, IS_THUNK } from "./thunk";
|
||||
import { type CYCLE_MARKER, force, type NixThunk } from "./thunk";
|
||||
import { forceAttrs, forceStringNoCtx } from "./type-assert";
|
||||
export { HAS_CONTEXT, isStringWithContext };
|
||||
export type { StringWithContext };
|
||||
|
||||
export const IS_PATH = Symbol("IS_PATH");
|
||||
|
||||
export interface NixPath {
|
||||
readonly [IS_PATH]: true;
|
||||
export class NixPath {
|
||||
readonly [IS_PATH] = true as const;
|
||||
value: string;
|
||||
constructor(value: string) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
export const isNixPath = (v: NixStrictValue): v is NixPath => {
|
||||
return typeof v === "object" && v !== null && IS_PATH in v;
|
||||
return v instanceof NixPath;
|
||||
};
|
||||
|
||||
export type NixInt = bigint;
|
||||
@@ -24,18 +27,18 @@ export type NixNull = null;
|
||||
|
||||
export const ATTR_POSITIONS = Symbol("attrPositions");
|
||||
export type NixList = NixValue[];
|
||||
export type NixAttrs = Map<string, NixValue> & { [ATTR_POSITIONS]?: Map<string, string> };
|
||||
export type NixAttrs = Map<string, NixValue> & { [ATTR_POSITIONS]?: Map<string, number> };
|
||||
export type NixFunction = ((arg: NixValue) => NixValue) & { args?: NixArgs };
|
||||
export class NixArgs {
|
||||
required: string[];
|
||||
optional: string[];
|
||||
allowed: Set<string>;
|
||||
ellipsis: boolean;
|
||||
positions: Map<string, string>;
|
||||
constructor(required: string[], optional: string[], positions: Record<string, string>, ellipsis: boolean) {
|
||||
positions: Map<string, number>;
|
||||
constructor(required: string[], optional: string[], positions: Map<string, number>, ellipsis: boolean) {
|
||||
this.required = required;
|
||||
this.optional = optional;
|
||||
this.positions = new Map(Object.entries(positions));
|
||||
this.positions = positions;
|
||||
this.ellipsis = ellipsis;
|
||||
this.allowed = new Set(required.concat(optional));
|
||||
}
|
||||
@@ -61,7 +64,7 @@ export const mkFunction = (
|
||||
f: (arg: NixValue) => NixValue,
|
||||
required: string[],
|
||||
optional: string[],
|
||||
positions: Record<string, string>,
|
||||
positions: Map<string, number>,
|
||||
ellipsis: boolean,
|
||||
): NixFunction => {
|
||||
const func: NixFunction = f;
|
||||
@@ -69,26 +72,11 @@ export const mkFunction = (
|
||||
return func;
|
||||
};
|
||||
|
||||
export const mkAttrs = (attrs: NixAttrs, keys: NixValue[], values: NixValue[]): NixAttrs => {
|
||||
const len = keys.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const key = force(keys[i]);
|
||||
if (key === null) {
|
||||
continue;
|
||||
}
|
||||
const str = forceStringNoCtx(key);
|
||||
attrs.set(str, values[i]);
|
||||
}
|
||||
return attrs;
|
||||
};
|
||||
|
||||
export const mkAttrsWithPos = (
|
||||
obj: Record<string, NixValue>,
|
||||
positions: Record<string, string>,
|
||||
dyns?: { dynKeys: NixValue[]; dynVals: NixValue[]; dynSpans: string[] },
|
||||
export const mkAttrs = (
|
||||
attrs: NixAttrs,
|
||||
positions: Map<string, number>,
|
||||
dyns?: { dynKeys: NixValue[]; dynVals: NixValue[]; dynSpans: number[] },
|
||||
): NixAttrs => {
|
||||
const attrs: NixAttrs = new Map(Object.entries(obj));
|
||||
|
||||
if (dyns) {
|
||||
const len = dyns.dynKeys.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
@@ -98,23 +86,17 @@ export const mkAttrsWithPos = (
|
||||
}
|
||||
const str = forceStringNoCtx(key);
|
||||
attrs.set(str, dyns.dynVals[i]);
|
||||
positions[str] = dyns.dynSpans[i];
|
||||
positions.set(str, dyns.dynSpans[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(positions).length > 0) {
|
||||
attrs[ATTR_POSITIONS] = new Map(Object.entries(positions));
|
||||
if (positions.size > 0) {
|
||||
attrs[ATTR_POSITIONS] = positions;
|
||||
}
|
||||
|
||||
return attrs;
|
||||
};
|
||||
|
||||
export interface NixThunkInterface {
|
||||
readonly [IS_THUNK]: true;
|
||||
func: (() => NixValue) | undefined;
|
||||
result: NixStrictValue | undefined;
|
||||
}
|
||||
|
||||
export type NixPrimitive = NixNull | NixBool | NixInt | NixFloat | NixString;
|
||||
export type NixValue =
|
||||
| NixPrimitive
|
||||
@@ -122,8 +104,8 @@ export type NixValue =
|
||||
| NixList
|
||||
| NixAttrs
|
||||
| NixFunction
|
||||
| NixThunkInterface
|
||||
| NixThunk
|
||||
| typeof CYCLE_MARKER;
|
||||
export type NixStrictValue = Exclude<NixValue, NixThunkInterface>;
|
||||
export type NixStrictValue = Exclude<NixValue, NixThunk>;
|
||||
|
||||
export class CatchableError extends Error {}
|
||||
|
||||
78
nix-js/runtime-ts/src/types/global.d.ts
vendored
78
nix-js/runtime-ts/src/types/global.d.ts
vendored
@@ -1,30 +1,68 @@
|
||||
import type { NixRuntime } from "..";
|
||||
import type { builtins } from "../builtins";
|
||||
import type { FetchGitResult, FetchTarballResult, FetchUrlResult } from "../builtins/io";
|
||||
import type {
|
||||
assert,
|
||||
call,
|
||||
concatStringsWithContext,
|
||||
hasAttr,
|
||||
lookupWith,
|
||||
mkPos,
|
||||
resolvePath,
|
||||
select,
|
||||
selectWithDefault,
|
||||
} from "../helpers";
|
||||
import type { op } from "../operators";
|
||||
import type { createThunk, force } from "../thunk";
|
||||
import type { forceBool } from "../type-assert";
|
||||
import type { mkAttrs, mkFunction, NixAttrs } from "../types";
|
||||
|
||||
declare global {
|
||||
var Nix: NixRuntime;
|
||||
var $t: typeof createThunk;
|
||||
var $f: typeof force;
|
||||
var $fb: typeof forceBool;
|
||||
var $a: typeof assert;
|
||||
var $c: typeof call;
|
||||
var $h: typeof hasAttr;
|
||||
var $s: typeof select;
|
||||
var $sd: typeof selectWithDefault;
|
||||
var $l: typeof lookupWith;
|
||||
var $r: typeof resolvePath;
|
||||
var $cs: typeof concatStringsWithContext;
|
||||
var $ma: typeof mkAttrs;
|
||||
var $mf: typeof mkFunction;
|
||||
var $mp: typeof mkPos;
|
||||
var $oa: typeof op.add;
|
||||
var $os: typeof op.sub;
|
||||
var $om: typeof op.mul;
|
||||
var $od: typeof op.div;
|
||||
var $oe: typeof op.eq;
|
||||
var $ol: typeof op.lt;
|
||||
var $og: typeof op.gt;
|
||||
var $oc: typeof op.concat;
|
||||
var $ou: typeof op.update;
|
||||
var $b: typeof builtins;
|
||||
var $gb: typeof Nix.getReplBinding;
|
||||
|
||||
namespace Deno {
|
||||
namespace core {
|
||||
namespace ops {
|
||||
function op_import(path: string): string;
|
||||
function op_import(path: string): NixValue;
|
||||
function op_scoped_import(path: string, scopeKeys: string[]): string;
|
||||
|
||||
function op_resolve_path(currentDir: string, path: string): string;
|
||||
|
||||
function op_read_file(path: string): string;
|
||||
function op_read_file_type(path: string): string;
|
||||
function op_read_dir(path: string): Record<string, string>;
|
||||
function op_read_dir(path: string): Map<string, string>;
|
||||
function op_path_exists(path: string): boolean;
|
||||
function op_walk_dir(path: string): [string, string][];
|
||||
|
||||
function op_make_placeholder(output: string): string;
|
||||
function op_store_path(path: string): string;
|
||||
|
||||
function op_convert_hash(input: {
|
||||
hash: string;
|
||||
hashAlgo: string | null;
|
||||
toHashFormat: string;
|
||||
}): string;
|
||||
function op_convert_hash(hash: string, hashAlgo: string | null, toHashFormat: string): string;
|
||||
function op_hash_string(algo: string, data: string): string;
|
||||
function op_hash_file(algo: string, path: string): string;
|
||||
function op_parse_hash(hashStr: string, algo: string | null): { hex: string; algo: string };
|
||||
@@ -43,11 +81,7 @@ declare global {
|
||||
includePaths: string[],
|
||||
): string;
|
||||
|
||||
function op_decode_span(span: string): {
|
||||
file: string | null;
|
||||
line: number | null;
|
||||
column: number | null;
|
||||
};
|
||||
function op_decode_span(span: number): NixAttrs;
|
||||
|
||||
function op_to_file(name: string, contents: string, references: string[]): string;
|
||||
|
||||
@@ -62,16 +96,16 @@ declare global {
|
||||
function op_from_toml(toml: string): unknown;
|
||||
function op_to_xml(e: NixValue): [string, string[]];
|
||||
|
||||
function op_finalize_derivation(input: {
|
||||
name: string;
|
||||
builder: string;
|
||||
platform: string;
|
||||
outputs: string[];
|
||||
args: string[];
|
||||
env: [string, string][];
|
||||
context: string[];
|
||||
fixedOutput: { hashAlgo: string; hash: string; hashMode: string } | null;
|
||||
}): { drvPath: string; outputs: [string, string][] };
|
||||
function op_finalize_derivation(
|
||||
name: string,
|
||||
builder: string,
|
||||
platform: string,
|
||||
outputs: string[],
|
||||
args: string[],
|
||||
env: [string, string][],
|
||||
context: string[],
|
||||
fixedOutput: { hashAlgo: string; hash: string; hashMode: string } | null,
|
||||
): { drvPath: string; outputs: [string, string][] };
|
||||
|
||||
function op_fetch_url(
|
||||
url: string,
|
||||
|
||||
@@ -31,14 +31,10 @@ pub(crate) fn compile(expr: &Ir, ctx: &impl CodegenContext) -> String {
|
||||
|
||||
code!(&mut buf, ctx; "(()=>{");
|
||||
|
||||
if std::env::var("NIX_JS_DEBUG_THUNKS").is_ok() {
|
||||
code!(&mut buf, ctx; "Nix.DEBUG_THUNKS.enabled=true;");
|
||||
}
|
||||
|
||||
code!(&mut buf, ctx;
|
||||
"const __currentDir="
|
||||
"const _d="
|
||||
quoted(&ctx.get_current_dir().display().to_string())
|
||||
";const __with=null;return "
|
||||
",_w=null;return "
|
||||
expr
|
||||
"})()");
|
||||
|
||||
@@ -48,16 +44,12 @@ pub(crate) fn compile(expr: &Ir, ctx: &impl CodegenContext) -> String {
|
||||
pub(crate) fn compile_scoped(expr: &Ir, ctx: &impl CodegenContext) -> String {
|
||||
let mut buf = CodeBuffer::with_capacity(8192);
|
||||
|
||||
code!(&mut buf, ctx; "((__scope)=>{");
|
||||
|
||||
if std::env::var("NIX_JS_DEBUG_THUNKS").is_ok() {
|
||||
code!(&mut buf, ctx; "Nix.DEBUG_THUNKS.enabled=true;");
|
||||
}
|
||||
code!(&mut buf, ctx; "((_s)=>{");
|
||||
|
||||
code!(&mut buf, ctx;
|
||||
"const __currentDir="
|
||||
"const _d="
|
||||
quoted(&ctx.get_current_dir().display().to_string())
|
||||
";return "
|
||||
",_w=null;return "
|
||||
expr
|
||||
"})"
|
||||
);
|
||||
@@ -191,13 +183,7 @@ where
|
||||
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for rnix::TextRange {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
code!(
|
||||
buf,
|
||||
"\"{}:{}:{}\"",
|
||||
ctx.get_current_source_id(),
|
||||
usize::from(self.start()),
|
||||
usize::from(self.end())
|
||||
);
|
||||
code!(buf, "{}", ctx.register_span(*self));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,7 +193,7 @@ pub(crate) trait CodegenContext {
|
||||
fn get_current_dir(&self) -> &Path;
|
||||
fn get_store_dir(&self) -> &str;
|
||||
fn get_current_source_id(&self) -> usize;
|
||||
fn get_current_source(&self) -> crate::error::Source;
|
||||
fn register_span(&self, range: rnix::TextRange) -> usize;
|
||||
}
|
||||
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for ExprId {
|
||||
@@ -241,7 +227,8 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||
code!(buf, ctx; quoted(&s.val));
|
||||
}
|
||||
Ir::Path(p) => {
|
||||
code!(buf, ctx; "Nix.resolvePath(__currentDir," ctx.get_ir(p.expr) ")");
|
||||
// Nix.resolvePath
|
||||
code!(buf, ctx; "$r(_d," ctx.get_ir(p.expr) ")");
|
||||
}
|
||||
Ir::If(x) => x.compile(ctx, buf),
|
||||
Ir::BinOp(x) => x.compile(ctx, buf),
|
||||
@@ -259,11 +246,13 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||
code!(buf, "expr{}", expr_id.0);
|
||||
}
|
||||
Ir::Builtins(_) => {
|
||||
code!(buf, ctx; "Nix.builtins");
|
||||
// Nix.builtins
|
||||
code!(buf, ctx; "$b");
|
||||
}
|
||||
&Ir::Builtin(Builtin { inner: name, .. }) => {
|
||||
// Nix.builtins
|
||||
code!(buf, ctx;
|
||||
"Nix.builtins.get("
|
||||
"$b.get("
|
||||
ctx.get_sym(name)
|
||||
")"
|
||||
);
|
||||
@@ -276,15 +265,13 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||
ref assertion_raw,
|
||||
span: assert_span,
|
||||
}) => {
|
||||
let assertion_ir = ctx.get_ir(assertion);
|
||||
let assertion_span = assertion_ir.span();
|
||||
let assertion = ctx.get_ir(assertion);
|
||||
|
||||
// Nix.assert
|
||||
code!(buf, ctx;
|
||||
"Nix.assert(Nix.withContext(\"while evaluating the condition of the assert statement\","
|
||||
assertion_span
|
||||
",()=>("
|
||||
assertion_ir
|
||||
")),"
|
||||
"$a("
|
||||
assertion
|
||||
","
|
||||
ctx.get_ir(expr)
|
||||
","
|
||||
quoted(assertion_raw)
|
||||
@@ -294,32 +281,35 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||
);
|
||||
}
|
||||
Ir::CurPos(cur_pos) => {
|
||||
// Nix.mkPos
|
||||
code!(buf, ctx;
|
||||
"Nix.mkPos("
|
||||
"$mp("
|
||||
cur_pos.span
|
||||
")"
|
||||
);
|
||||
}
|
||||
&Ir::ReplBinding(ReplBinding { inner: name, .. }) => {
|
||||
// Nix.getReplBinding
|
||||
code!(buf, ctx;
|
||||
"Nix.getReplBinding("
|
||||
"$gb("
|
||||
ctx.get_sym(name)
|
||||
")"
|
||||
);
|
||||
}
|
||||
&Ir::ScopedImportBinding(ScopedImportBinding { inner: name, .. }) => {
|
||||
code!(buf, ctx;
|
||||
"__scope.get("
|
||||
"_s.get("
|
||||
ctx.get_sym(name)
|
||||
")"
|
||||
);
|
||||
}
|
||||
Ir::WithExpr(x) => x.compile(ctx, buf),
|
||||
&Ir::WithLookup(WithLookup { inner: name, .. }) => {
|
||||
// Nix.lookupWith
|
||||
code!(buf, ctx;
|
||||
"Nix.lookupWith("
|
||||
"$l("
|
||||
ctx.get_sym(name)
|
||||
",__with)"
|
||||
",_w)"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -334,13 +324,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for If {
|
||||
alter,
|
||||
span: _,
|
||||
} = self;
|
||||
let cond_ir = ctx.get_ir(cond);
|
||||
let cond_span = cond_ir.span();
|
||||
let cond = ctx.get_ir(cond);
|
||||
|
||||
code!(buf, ctx;
|
||||
"(Nix.withContext(\"while evaluating a branch condition\"," cond_span ",()=>Nix.forceBool(" cond_ir ")))"
|
||||
"?(" consq "):(" alter ")"
|
||||
);
|
||||
// Nix.forceBool
|
||||
code!(buf, ctx; "$fb(" cond ")?(" consq "):(" alter ")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,65 +340,50 @@ impl<Ctx: CodegenContext> Compile<Ctx> for BinOp {
|
||||
|
||||
match self.kind {
|
||||
Add | Sub | Mul | Div | Eq | Neq | Lt | Gt | Leq | Geq | Con | Upd => {
|
||||
let op_name = match self.kind {
|
||||
Add => "+",
|
||||
Sub => "-",
|
||||
Mul => "*",
|
||||
Div => "/",
|
||||
Eq => "==",
|
||||
Neq => "!=",
|
||||
Lt => "<",
|
||||
Gt => ">",
|
||||
Leq => "<=",
|
||||
Geq => ">=",
|
||||
Con => "++",
|
||||
Upd => "//",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let op_func = match self.kind {
|
||||
Add => "Nix.op.add",
|
||||
Sub => "Nix.op.sub",
|
||||
Mul => "Nix.op.mul",
|
||||
Div => "Nix.op.div",
|
||||
Eq => "Nix.op.eq",
|
||||
Neq => "Nix.op.neq",
|
||||
Lt => "Nix.op.lt",
|
||||
Gt => "Nix.op.gt",
|
||||
Leq => "Nix.op.lte",
|
||||
Geq => "Nix.op.gte",
|
||||
Con => "Nix.op.concat",
|
||||
Upd => "Nix.op.update",
|
||||
Add => "$oa",
|
||||
Sub => "$os",
|
||||
Mul => "$om",
|
||||
Div => "$od",
|
||||
Eq => "$oe",
|
||||
Neq => "!$oe",
|
||||
Lt => "$ol",
|
||||
Gt => "$og",
|
||||
Leq => "!$og",
|
||||
Geq => "!$ol",
|
||||
Con => "$oc",
|
||||
Upd => "$ou",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating the " op_name " operator\"," self.span ",()=>(" op_func "(" lhs "," rhs ")))"
|
||||
op_func "(" lhs "," rhs ")"
|
||||
);
|
||||
}
|
||||
And => {
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating the && operator\"," self.span ",()=>(Nix.forceBool(" lhs ")&&Nix.forceBool(" rhs ")))"
|
||||
"$fb(" lhs ")" "&&" "$fb(" rhs ")"
|
||||
);
|
||||
}
|
||||
Or => {
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating the || operator\"," self.span ",()=>(Nix.forceBool(" lhs ")||Nix.forceBool(" rhs ")))"
|
||||
"$fb(" lhs ")" "||" "$fb(" rhs ")"
|
||||
);
|
||||
}
|
||||
Impl => {
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating the -> operator\"," self.span ",()=>(!Nix.forceBool(" lhs ")||Nix.forceBool(" rhs ")))"
|
||||
"!$fb(" lhs ")" "||" "$fb(" rhs ")"
|
||||
);
|
||||
}
|
||||
PipeL => {
|
||||
code!(buf, ctx; "Nix.call(" rhs "," lhs ")");
|
||||
code!(buf, ctx; "$c(" rhs "," lhs ")");
|
||||
}
|
||||
PipeR => {
|
||||
code!(buf, ctx; "Nix.call(" lhs "," rhs ")");
|
||||
code!(buf, ctx; "$c(" lhs "," rhs ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -423,10 +395,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for UnOp {
|
||||
let rhs = ctx.get_ir(self.rhs);
|
||||
match self.kind {
|
||||
Neg => {
|
||||
code!(buf, ctx; "Nix.op.sub(0n," rhs ")");
|
||||
code!(buf, ctx; "$os(0n," rhs ")");
|
||||
}
|
||||
Not => {
|
||||
code!(buf, ctx; "Nix.op.bnot(" ctx.get_ir(self.rhs) ")");
|
||||
code!(buf, ctx; "!$fb(" ctx.get_ir(self.rhs) ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -444,7 +416,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Func {
|
||||
ellipsis,
|
||||
}) = &self.param
|
||||
{
|
||||
code!(buf, "Nix.mkFunction(arg{}=>", id);
|
||||
code!(buf, "$mf(arg{}=>", id);
|
||||
if has_thunks {
|
||||
code!(buf, ctx; "{" self.thunks "return " self.body "}");
|
||||
} else {
|
||||
@@ -459,11 +431,11 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Func {
|
||||
joined(optional.iter(), ",", |ctx: &Ctx, buf, &(sym, _)| {
|
||||
code!(buf, ctx; ctx.get_sym(sym));
|
||||
})
|
||||
"],{"
|
||||
"],new Map(["
|
||||
joined(required.iter().chain(optional.iter()), ",", |ctx: &Ctx, buf, &(sym, span)| {
|
||||
code!(buf, ctx; ctx.get_sym(sym) ":" span);
|
||||
code!(buf, ctx; "[" ctx.get_sym(sym) "," span "]");
|
||||
})
|
||||
"},"
|
||||
"]),"
|
||||
ellipsis
|
||||
")"
|
||||
);
|
||||
@@ -481,7 +453,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Func {
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for Call {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
code!(buf, ctx;
|
||||
"Nix.call("
|
||||
"$c("
|
||||
ctx.get_ir(self.func)
|
||||
","
|
||||
ctx.get_ir(self.arg)
|
||||
@@ -500,16 +472,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for [(ExprId, ExprId)] {
|
||||
|
||||
for &(slot, inner) in self {
|
||||
let inner_ir = ctx.get_ir(inner);
|
||||
let inner_span = inner_ir.span();
|
||||
|
||||
code!(
|
||||
buf, ctx;
|
||||
"let expr" slot.0 "=Nix.createThunk(()=>(" inner_ir "),"
|
||||
"\"expr" slot.0 " "
|
||||
ctx.get_current_source().get_name() ":"
|
||||
usize::from(inner_span.start()) ":"
|
||||
usize::from(inner_span.end())
|
||||
"\");"
|
||||
"let expr" slot.0 "=$t(()=>(" inner_ir "),"
|
||||
"\"expr" slot.0 "\");"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -532,9 +498,9 @@ impl<Ctx: CodegenContext> Compile<Ctx> for WithExpr {
|
||||
let body = ctx.get_ir(self.body);
|
||||
let has_thunks = !self.thunks.is_empty();
|
||||
if has_thunks {
|
||||
code!(buf, ctx; "((__with)=>{" self.thunks "return " body "})({env:" namespace ",last:__with})");
|
||||
code!(buf, ctx; "((_w)=>{" self.thunks "return " body "})({env:" namespace ",last:_w})");
|
||||
} else {
|
||||
code!(buf, ctx; "((__with)=>(" body "))({env:" namespace ",last:__with})");
|
||||
code!(buf, ctx; "((_w)=>(" body "))({env:" namespace ",last:_w})");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -543,7 +509,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Select {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
if let Some(default) = self.default {
|
||||
code!(buf, ctx;
|
||||
"Nix.selectWithDefault("
|
||||
"$sd("
|
||||
ctx.get_ir(self.expr)
|
||||
",["
|
||||
joined(self.attrpath.iter(), ",", |ctx: &Ctx, buf, attr| {
|
||||
@@ -560,7 +526,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Select {
|
||||
);
|
||||
} else {
|
||||
code!(buf, ctx;
|
||||
"Nix.select("
|
||||
"$s("
|
||||
ctx.get_ir(self.expr)
|
||||
",["
|
||||
joined(self.attrpath.iter(), ",", |ctx: &Ctx, buf, attr| {
|
||||
@@ -581,31 +547,28 @@ impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
if !self.dyns.is_empty() {
|
||||
code!(buf, ctx;
|
||||
"Nix.mkAttrsWithPos({"
|
||||
"$ma(new Map(["
|
||||
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(expr, _))| {
|
||||
let key = ctx.get_sym(sym);
|
||||
let val = ctx.get_ir(expr);
|
||||
|
||||
code!(
|
||||
buf, ctx;
|
||||
key ":Nix.withContext(\"while evaluating the attribute '" escaped(&key) "'\"," val.span() ",()=>(" val "))"
|
||||
"[" key "," val "]"
|
||||
);
|
||||
})
|
||||
"},{"
|
||||
"]),new Map(["
|
||||
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(_, span))| {
|
||||
code!(buf, ctx; ctx.get_sym(sym) ":" span);
|
||||
code!(buf, ctx; "[" ctx.get_sym(sym) "," span "]");
|
||||
})
|
||||
"},{dynKeys:["
|
||||
"]),{dynKeys:["
|
||||
joined(self.dyns.iter(), ",", |ctx: &Ctx, buf, (key, _, _)| {
|
||||
code!(buf, ctx; ctx.get_ir(*key));
|
||||
})
|
||||
"],dynVals:["
|
||||
joined(self.dyns.iter(), ",", |ctx: &Ctx, buf, (_, val, _)| {
|
||||
let val = ctx.get_ir(*val);
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating a dynamic attribute\"," val.span() ",()=>(" val "))"
|
||||
);
|
||||
code!(buf, ctx; val);
|
||||
})
|
||||
"],dynSpans:["
|
||||
joined(self.dyns.iter(), ",", |ctx: &Ctx, buf, (_, _, attr_span)| {
|
||||
@@ -615,21 +578,21 @@ impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
||||
);
|
||||
} else if !self.stcs.is_empty() {
|
||||
code!(buf, ctx;
|
||||
"Nix.mkAttrsWithPos({"
|
||||
"$ma(new Map(["
|
||||
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(expr, _))| {
|
||||
let key = ctx.get_sym(sym);
|
||||
let val = ctx.get_ir(expr);
|
||||
|
||||
code!(
|
||||
buf, ctx;
|
||||
key ":Nix.withContext(\"while evaluating the attribute '" escaped(&key) "'\"," val.span() ",()=>(" val "))"
|
||||
"[" key "," val "]"
|
||||
);
|
||||
})
|
||||
"},{"
|
||||
"]),new Map(["
|
||||
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(_, span))| {
|
||||
code!(buf, ctx; ctx.get_sym(sym) ":" span);
|
||||
code!(buf, ctx; "[" ctx.get_sym(sym) "," span "]");
|
||||
})
|
||||
"})"
|
||||
"]))"
|
||||
);
|
||||
} else {
|
||||
code!(buf, ctx; "new Map()");
|
||||
@@ -641,12 +604,9 @@ impl<Ctx: CodegenContext> Compile<Ctx> for List {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
code!(buf, ctx;
|
||||
"["
|
||||
joined(self.items.iter().enumerate(), ",", |ctx: &Ctx, buf, (idx, item)| {
|
||||
joined(self.items.iter(), ",", |ctx: &Ctx, buf, item| {
|
||||
let item = ctx.get_ir(*item);
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating list element " idx "\"," item.span() ",()=>(" item "))"
|
||||
);
|
||||
code!(buf, ctx; item);
|
||||
})
|
||||
"]"
|
||||
);
|
||||
@@ -656,13 +616,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for List {
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for ConcatStrings {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
code!(buf, ctx;
|
||||
"Nix.concatStringsWithContext(["
|
||||
"$cs(["
|
||||
joined(self.parts.iter(), ",", |ctx: &Ctx, buf, part| {
|
||||
let part = ctx.get_ir(*part);
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating a path segment\"," part.span() ",()=>(" part "))"
|
||||
);
|
||||
code!(buf, ctx; part);
|
||||
})
|
||||
"]," self.force_string ")"
|
||||
);
|
||||
@@ -672,7 +629,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for ConcatStrings {
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for HasAttr {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
code!(buf, ctx;
|
||||
"Nix.hasAttr("
|
||||
"$h("
|
||||
ctx.get_ir(self.lhs)
|
||||
",["
|
||||
joined(self.rhs.iter(), ",", |ctx: &Ctx, buf, attr| {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::path::Path;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
@@ -123,9 +124,14 @@ impl Context {
|
||||
let code = self.ctx.compile(source, None)?;
|
||||
self.runtime.eval(
|
||||
format!(
|
||||
"Nix.builtins.set('derivation',({}));Nix.builtins.set('storeDir','{}')",
|
||||
"Nix.builtins.set('derivation',({}));Nix.builtins.set('storeDir','{}');{}0n",
|
||||
code,
|
||||
self.get_store_dir()
|
||||
self.get_store_dir(),
|
||||
if std::env::var("NIX_JS_DEBUG_THUNKS").is_ok() {
|
||||
"Nix.DEBUG_THUNKS.enabled=true;"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
),
|
||||
&mut self.ctx,
|
||||
)?;
|
||||
@@ -182,6 +188,7 @@ pub(crate) struct Ctx {
|
||||
global: NonNull<HashMap<SymId, ExprId>>,
|
||||
sources: Vec<Source>,
|
||||
store: DaemonStore,
|
||||
spans: UnsafeCell<Vec<(usize, TextRange)>>,
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
@@ -278,6 +285,7 @@ impl Ctx {
|
||||
global: unsafe { NonNull::new_unchecked(Box::leak(Box::new(global))) },
|
||||
sources: Vec::new(),
|
||||
store,
|
||||
spans: UnsafeCell::new(Vec::new()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -363,12 +371,15 @@ impl CodegenContext for Ctx {
|
||||
.checked_sub(1)
|
||||
.expect("current_source not set")
|
||||
}
|
||||
fn get_current_source(&self) -> crate::error::Source {
|
||||
self.sources.last().expect("current_source not set").clone()
|
||||
}
|
||||
fn get_store_dir(&self) -> &str {
|
||||
self.store.get_store_dir()
|
||||
}
|
||||
fn register_span(&self, range: rnix::TextRange) -> usize {
|
||||
let spans = unsafe { &mut *self.spans.get() };
|
||||
let id = spans.len();
|
||||
spans.push((self.get_current_source_id(), range));
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeContext for Ctx {
|
||||
@@ -390,6 +401,10 @@ impl RuntimeContext for Ctx {
|
||||
fn get_store(&self) -> &DaemonStore {
|
||||
&self.store
|
||||
}
|
||||
fn get_span(&self, id: usize) -> (usize, rnix::TextRange) {
|
||||
let spans = unsafe { &*self.spans.get() };
|
||||
spans[id]
|
||||
}
|
||||
}
|
||||
|
||||
enum Scope<'ctx> {
|
||||
|
||||
@@ -292,43 +292,32 @@ fn parse_frames(stack: &str, ctx: &impl RuntimeContext) -> Vec<NixStackFrame> {
|
||||
let mut frames = Vec::new();
|
||||
|
||||
for line in stack.lines() {
|
||||
// Format: NIX_STACK_FRAME:source_id:start:end[:extra_data]
|
||||
// Format: NIX_STACK_FRAME:span_id:message
|
||||
let Some(rest) = line.strip_prefix("NIX_STACK_FRAME:") else {
|
||||
continue;
|
||||
};
|
||||
let parts: Vec<&str> = rest.splitn(4, ':').collect();
|
||||
let parts: Vec<&str> = rest.splitn(2, ':').collect();
|
||||
|
||||
if parts.len() < 3 {
|
||||
if parts.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let src = match parts[0].parse() {
|
||||
Ok(id) => ctx.get_source(id),
|
||||
Err(_) => continue,
|
||||
};
|
||||
let start: u32 = match parts[1].parse() {
|
||||
Ok(v) => v,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let end: u32 = match parts[2].parse() {
|
||||
Ok(v) => v,
|
||||
let span_id: usize = match parts[0].parse() {
|
||||
Ok(id) => id,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let (source_id, span) = ctx.get_span(span_id);
|
||||
let src = ctx.get_source(source_id);
|
||||
|
||||
let span = rnix::TextRange::new(rnix::TextSize::from(start), rnix::TextSize::from(end));
|
||||
|
||||
let message = {
|
||||
if parts.len() == 4 {
|
||||
parts[3].to_string()
|
||||
let message = if parts.len() == 2 {
|
||||
parts[1].to_string()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
|
||||
frames.push(NixStackFrame { span, message, src });
|
||||
}
|
||||
|
||||
// Deduplicate consecutive identical frames
|
||||
frames.dedup_by(|a, b| a.span == b.span && a.message == b.message);
|
||||
|
||||
frames
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use deno_core::OpState;
|
||||
use deno_core::ToV8;
|
||||
use deno_core::op2;
|
||||
use nix_compat::nixhash::HashAlgo;
|
||||
use nix_compat::nixhash::NixHash;
|
||||
use serde::Serialize;
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
use crate::runtime::OpStateExt;
|
||||
@@ -22,19 +22,19 @@ pub use metadata_cache::MetadataCache;
|
||||
use crate::nar;
|
||||
use crate::runtime::NixRuntimeError;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[derive(ToV8)]
|
||||
pub struct FetchUrlResult {
|
||||
pub store_path: String,
|
||||
pub hash: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[derive(ToV8)]
|
||||
pub struct FetchTarballResult {
|
||||
pub store_path: String,
|
||||
pub nar_hash: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[derive(ToV8)]
|
||||
pub struct FetchGitResult {
|
||||
pub out_path: String,
|
||||
pub rev: String,
|
||||
@@ -47,7 +47,6 @@ pub struct FetchGitResult {
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_fetch_url<Ctx: RuntimeContext>(
|
||||
state: &mut OpState,
|
||||
#[string] url: String,
|
||||
@@ -152,7 +151,6 @@ pub fn op_fetch_url<Ctx: RuntimeContext>(
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_fetch_tarball<Ctx: RuntimeContext>(
|
||||
state: &mut OpState,
|
||||
#[string] url: String,
|
||||
@@ -266,7 +264,6 @@ pub fn op_fetch_tarball<Ctx: RuntimeContext>(
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_fetch_git<Ctx: RuntimeContext>(
|
||||
state: &mut OpState,
|
||||
#[string] url: String,
|
||||
|
||||
@@ -26,6 +26,12 @@ struct Cli {
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Command {
|
||||
Compile {
|
||||
#[clap(flatten)]
|
||||
source: ExprSource,
|
||||
#[arg(long)]
|
||||
silent: bool
|
||||
},
|
||||
Eval {
|
||||
#[clap(flatten)]
|
||||
source: ExprSource,
|
||||
@@ -63,6 +69,30 @@ fn create_context(#[cfg(feature = "inspector")] cli: &Cli) -> Result<Context> {
|
||||
Ok(Context::new()?)
|
||||
}
|
||||
|
||||
fn run_compile(context: &mut Context, src: ExprSource, silent: bool) -> Result<()> {
|
||||
let src = if let Some(expr) = src.expr {
|
||||
Source::new_eval(expr)?
|
||||
} else if let Some(file) = src.file {
|
||||
Source::new_file(file)?
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
match context.compile(src) {
|
||||
Ok(compiled) => {
|
||||
if !silent {
|
||||
println!("{compiled}");
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("{:?}", miette::Report::new(*err));
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
#[cfg(feature = "inspector")]
|
||||
context.wait_for_inspector_disconnect();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_eval(context: &mut Context, src: ExprSource) -> Result<()> {
|
||||
let src = if let Some(expr) = src.expr {
|
||||
Source::new_eval(expr)?
|
||||
@@ -150,6 +180,7 @@ fn main() -> Result<()> {
|
||||
)?;
|
||||
|
||||
match cli.command {
|
||||
Command::Compile { source , silent } => run_compile(&mut context, source, silent),
|
||||
Command::Eval { source } => run_eval(&mut context, source),
|
||||
Command::Repl => run_repl(&mut context),
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ pub(crate) trait RuntimeContext: 'static {
|
||||
fn compile_scoped(&mut self, source: Source, scope: Vec<String>) -> Result<String>;
|
||||
fn get_source(&self, id: usize) -> Source;
|
||||
fn get_store(&self) -> &DaemonStore;
|
||||
fn get_span(&self, id: usize) -> (usize, rnix::TextRange);
|
||||
}
|
||||
|
||||
pub(crate) trait OpStateExt<Ctx: RuntimeContext> {
|
||||
@@ -115,6 +116,7 @@ pub(crate) use private::NixRuntimeError;
|
||||
|
||||
pub(crate) struct Runtime<Ctx: RuntimeContext> {
|
||||
js_runtime: JsRuntime,
|
||||
#[cfg(feature = "inspector")]
|
||||
rt: tokio::runtime::Runtime,
|
||||
#[cfg(feature = "inspector")]
|
||||
wait_for_inspector: bool,
|
||||
@@ -142,7 +144,12 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
|
||||
static INIT: Once = Once::new();
|
||||
INIT.call_once(|| {
|
||||
assert_eq!(
|
||||
deno_core::v8_set_flags(vec!["".into(), format!("--stack-size={}", 8 * 1024)]),
|
||||
deno_core::v8_set_flags(vec![
|
||||
"".into(),
|
||||
format!("--stack-size={}", 8 * 1024),
|
||||
#[cfg(feature = "prof")]
|
||||
("--prof".into())
|
||||
]),
|
||||
[""]
|
||||
);
|
||||
JsRuntime::init_platform(Some(v8::new_default_platform(0, false).make_shared()));
|
||||
@@ -172,6 +179,7 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
|
||||
|
||||
Ok(Self {
|
||||
js_runtime,
|
||||
#[cfg(feature = "inspector")]
|
||||
rt: tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
@@ -224,22 +232,6 @@ impl<Ctx: RuntimeContext> Runtime<Ctx> {
|
||||
|
||||
crate::error::parse_js_error(error, ctx)
|
||||
})?;
|
||||
let global_value = self
|
||||
.rt
|
||||
.block_on(self.js_runtime.resolve(global_value))
|
||||
.map_err(|error| {
|
||||
let op_state = self.js_runtime.op_state();
|
||||
let op_state_borrow = op_state.borrow();
|
||||
let ctx: &Ctx = op_state_borrow.get_ctx();
|
||||
|
||||
crate::error::parse_js_error(error, ctx)
|
||||
})?;
|
||||
#[cfg(feature = "inspector")]
|
||||
{
|
||||
let _ = self
|
||||
.rt
|
||||
.block_on(self.js_runtime.run_event_loop(Default::default()));
|
||||
}
|
||||
|
||||
// Retrieve scope from JsRuntime
|
||||
deno_core::scope!(scope, self.js_runtime);
|
||||
|
||||
@@ -21,10 +21,10 @@ use deno_core::url::Url;
|
||||
use fastwebsockets::Frame;
|
||||
use fastwebsockets::OpCode;
|
||||
use fastwebsockets::WebSocket;
|
||||
use hashbrown::HashMap;
|
||||
use hyper::body::Bytes;
|
||||
use hyper_util::rt::TokioIo;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::pin::pin;
|
||||
use std::process;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::Infallible;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
use hashbrown::hash_map::{Entry, HashMap};
|
||||
|
||||
use deno_core::{FromV8, OpState, v8};
|
||||
use deno_core::error::JsError;
|
||||
use deno_core::{FromV8, OpState, ToV8, v8};
|
||||
use hashbrown::{HashMap, HashSet, hash_map::Entry};
|
||||
use regex::Regex;
|
||||
use rust_embed::Embed;
|
||||
|
||||
@@ -36,16 +38,84 @@ impl RegexCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct Map<K, V>(HashMap<K, V>);
|
||||
impl<'a, K, V> ToV8<'a> for Map<K, V>
|
||||
where
|
||||
K: ToV8<'a>,
|
||||
K::Error: ToString,
|
||||
V: ToV8<'a>,
|
||||
V::Error: ToString,
|
||||
{
|
||||
type Error = NixRuntimeError;
|
||||
fn to_v8<'i>(self, scope: &mut v8::PinScope<'a, 'i>) -> Result<v8::Local<'a, v8::Value>> {
|
||||
let map = v8::Map::new(scope);
|
||||
for (k, v) in self.0 {
|
||||
let k = k.to_v8(scope).map_err(|err| err.to_string())?;
|
||||
let v = v.to_v8(scope).map_err(|err| err.to_string())?;
|
||||
map.set(scope, k, v).ok_or("Failed to set V8 Map KV")?;
|
||||
}
|
||||
Ok(map.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Embed)]
|
||||
#[folder = "src/runtime/corepkgs"]
|
||||
pub(crate) struct CorePkgs;
|
||||
|
||||
#[deno_core::op2]
|
||||
#[string]
|
||||
fn new_simple_jserror(msg: String) -> Box<JsError> {
|
||||
JsError {
|
||||
message: Some(msg.clone()),
|
||||
|
||||
name: None,
|
||||
stack: None,
|
||||
cause: None,
|
||||
exception_message: msg,
|
||||
frames: Vec::new(),
|
||||
source_line: None,
|
||||
source_line_frame_index: None,
|
||||
aggregated: None,
|
||||
additional_properties: Vec::new(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
struct Compiled(String);
|
||||
impl<'a> ToV8<'a> for Compiled {
|
||||
type Error = Box<JsError>;
|
||||
fn to_v8<'i>(
|
||||
self,
|
||||
scope: &mut v8::PinScope<'a, 'i>,
|
||||
) -> std::result::Result<v8::Local<'a, v8::Value>, Self::Error> {
|
||||
let Ok(script) = self.0.to_v8(scope);
|
||||
let Some(source) = script.to_string(scope) else {
|
||||
unsafe { std::hint::unreachable_unchecked() }
|
||||
};
|
||||
let tc = std::pin::pin!(v8::TryCatch::new(scope));
|
||||
let mut scope = tc.init();
|
||||
let Some(compiled) = v8::Script::compile(&scope, source, None) else {
|
||||
let msg = scope
|
||||
.exception()
|
||||
.map(|e| e.to_rust_string_lossy(&scope))
|
||||
.unwrap_or_else(|| "failed to compile code".into());
|
||||
return Err(new_simple_jserror(msg));
|
||||
};
|
||||
match compiled.run(&scope) {
|
||||
Some(val) => Ok(val),
|
||||
None => Err(scope
|
||||
.exception()
|
||||
.map(|e| JsError::from_v8_exception(&mut scope, e))
|
||||
.unwrap_or_else(|| {
|
||||
new_simple_jserror("script execution failed unexpectedly".into())
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[deno_core::op2(reentrant)]
|
||||
pub(super) fn op_import<Ctx: RuntimeContext>(
|
||||
state: &mut OpState,
|
||||
#[string] path: String,
|
||||
) -> Result<String> {
|
||||
) -> Result<Compiled> {
|
||||
let _span = tracing::info_span!("op_import", path = %path).entered();
|
||||
let ctx: &mut Ctx = state.get_ctx_mut();
|
||||
|
||||
@@ -61,7 +131,8 @@ pub(super) fn op_import<Ctx: RuntimeContext>(
|
||||
.into(),
|
||||
);
|
||||
ctx.add_source(source.clone());
|
||||
return Ok(ctx.compile(source).map_err(|err| err.to_string())?);
|
||||
let code = ctx.compile(source).map_err(|err| err.to_string())?;
|
||||
return Ok(Compiled(code));
|
||||
} else {
|
||||
return Err(format!("Corepkg not found: {}", corepkg_name).into());
|
||||
}
|
||||
@@ -85,7 +156,8 @@ pub(super) fn op_import<Ctx: RuntimeContext>(
|
||||
tracing::debug!("Compiling file");
|
||||
ctx.add_source(source.clone());
|
||||
|
||||
Ok(ctx.compile(source).map_err(|err| err.to_string())?)
|
||||
let code = ctx.compile(source).map_err(|err| err.to_string())?;
|
||||
Ok(Compiled(code))
|
||||
}
|
||||
|
||||
#[deno_core::op2]
|
||||
@@ -93,7 +165,7 @@ pub(super) fn op_import<Ctx: RuntimeContext>(
|
||||
pub(super) fn op_scoped_import<Ctx: RuntimeContext>(
|
||||
state: &mut OpState,
|
||||
#[string] path: String,
|
||||
#[serde] scope: Vec<String>,
|
||||
#[scoped] scope: Vec<String>,
|
||||
) -> Result<String> {
|
||||
let _span = tracing::info_span!("op_scoped_import", path = %path).entered();
|
||||
let ctx: &mut Ctx = state.get_ctx_mut();
|
||||
@@ -161,10 +233,7 @@ pub(super) fn op_read_file_type(#[string] path: String) -> Result<String> {
|
||||
}
|
||||
|
||||
#[deno_core::op2]
|
||||
#[serde]
|
||||
pub(super) fn op_read_dir(
|
||||
#[string] path: String,
|
||||
) -> Result<std::collections::HashMap<String, String>> {
|
||||
pub(super) fn op_read_dir(#[string] path: String) -> Result<Map<String, &'static str>> {
|
||||
let path = Path::new(&path);
|
||||
|
||||
if !path.is_dir() {
|
||||
@@ -174,7 +243,7 @@ pub(super) fn op_read_dir(
|
||||
let entries = std::fs::read_dir(path)
|
||||
.map_err(|e| format!("Failed to read directory {}: {}", path.display(), e))?;
|
||||
|
||||
let mut result = std::collections::HashMap::new();
|
||||
let mut result = HashMap::new();
|
||||
|
||||
for entry in entries {
|
||||
let entry = entry.map_err(|e| format!("Failed to read directory entry: {}", e))?;
|
||||
@@ -198,10 +267,10 @@ pub(super) fn op_read_dir(
|
||||
"unknown"
|
||||
};
|
||||
|
||||
result.insert(file_name, type_str.to_string());
|
||||
result.insert(file_name, type_str);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
Ok(Map(result))
|
||||
}
|
||||
|
||||
#[deno_core::op2]
|
||||
@@ -255,35 +324,40 @@ pub(super) fn op_make_placeholder(#[string] output: String) -> String {
|
||||
format!("/{}", encoded)
|
||||
}
|
||||
|
||||
enum StringOrU32 {
|
||||
String(String),
|
||||
U32(u32),
|
||||
}
|
||||
impl<'a> ToV8<'a> for StringOrU32 {
|
||||
type Error = Infallible;
|
||||
fn to_v8<'i>(
|
||||
self,
|
||||
scope: &mut v8::PinScope<'a, 'i>,
|
||||
) -> std::result::Result<v8::Local<'a, v8::Value>, Self::Error> {
|
||||
match self {
|
||||
Self::String(x) => x.to_v8(scope),
|
||||
Self::U32(x) => x.to_v8(scope),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[deno_core::op2]
|
||||
#[serde]
|
||||
pub(super) fn op_decode_span<Ctx: RuntimeContext>(
|
||||
state: &mut OpState,
|
||||
#[string] span_str: String,
|
||||
) -> Result<serde_json::Value> {
|
||||
let parts: Vec<&str> = span_str.split(':').collect();
|
||||
if parts.len() != 3 {
|
||||
return Ok(serde_json::json!({
|
||||
"file": serde_json::Value::Null,
|
||||
"line": serde_json::Value::Null,
|
||||
"column": serde_json::Value::Null
|
||||
}));
|
||||
}
|
||||
|
||||
let source_id: usize = parts[0].parse().map_err(|_| "Invalid source ID")?;
|
||||
let start: u32 = parts[1].parse().map_err(|_| "Invalid start offset")?;
|
||||
|
||||
#[smi] span_id: u32,
|
||||
) -> Map<&'static str, StringOrU32> {
|
||||
let ctx: &Ctx = state.get_ctx();
|
||||
let (source_id, range) = ctx.get_span(span_id as usize);
|
||||
let source = ctx.get_source(source_id);
|
||||
let content = &source.src;
|
||||
let start = u32::from(range.start());
|
||||
|
||||
let (line, column) = byte_offset_to_line_col(content, start as usize);
|
||||
let (line, column) = byte_offset_to_line_col(&source.src, start as usize);
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"file": source.get_name(),
|
||||
"line": line,
|
||||
"column": column
|
||||
}))
|
||||
Map(HashMap::from([
|
||||
("file", StringOrU32::String(source.get_name())),
|
||||
("line", StringOrU32::U32(line)),
|
||||
("column", StringOrU32::U32(column)),
|
||||
]))
|
||||
}
|
||||
|
||||
fn byte_offset_to_line_col(content: &str, offset: usize) -> (u32, u32) {
|
||||
@@ -305,14 +379,18 @@ fn byte_offset_to_line_col(content: &str, offset: usize) -> (u32, u32) {
|
||||
(line, col)
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub(super) struct ParsedHash {
|
||||
hex: String,
|
||||
algo: String,
|
||||
mod private {
|
||||
use deno_core::ToV8;
|
||||
|
||||
#[derive(ToV8)]
|
||||
pub(super) struct ParsedHash {
|
||||
pub(super) hex: String,
|
||||
pub(super) algo: String,
|
||||
}
|
||||
}
|
||||
use private::*;
|
||||
|
||||
#[deno_core::op2]
|
||||
#[serde]
|
||||
pub(super) fn op_parse_hash(
|
||||
#[string] hash_str: String,
|
||||
#[string] algo: Option<String>,
|
||||
@@ -444,7 +522,7 @@ pub(super) fn op_to_file<Ctx: RuntimeContext>(
|
||||
state: &mut OpState,
|
||||
#[string] name: String,
|
||||
#[string] contents: String,
|
||||
#[serde] references: Vec<String>,
|
||||
#[scoped] references: Vec<String>,
|
||||
) -> Result<String> {
|
||||
let ctx: &Ctx = state.get_ctx();
|
||||
let store = ctx.get_store();
|
||||
@@ -498,7 +576,6 @@ pub(super) fn op_get_env(#[string] key: String) -> Result<String> {
|
||||
}
|
||||
|
||||
#[deno_core::op2]
|
||||
#[serde]
|
||||
pub(super) fn op_walk_dir(#[string] path: String) -> Result<Vec<(String, String)>> {
|
||||
fn walk_recursive(
|
||||
base: &Path,
|
||||
@@ -560,7 +637,7 @@ pub(super) fn op_add_filtered_path<Ctx: RuntimeContext>(
|
||||
#[string] name: Option<String>,
|
||||
recursive: bool,
|
||||
#[string] sha256: Option<String>,
|
||||
#[serde] include_paths: Vec<String>,
|
||||
#[scoped] include_paths: Vec<String>,
|
||||
) -> Result<String> {
|
||||
use nix_compat::nixhash::{HashAlgo, NixHash};
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -659,7 +736,6 @@ pub(super) fn op_add_filtered_path<Ctx: RuntimeContext>(
|
||||
}
|
||||
|
||||
#[deno_core::op2]
|
||||
#[serde]
|
||||
pub(super) fn op_match(
|
||||
state: &mut OpState,
|
||||
#[string] regex: String,
|
||||
@@ -684,7 +760,6 @@ pub(super) fn op_match(
|
||||
}
|
||||
|
||||
#[deno_core::op2]
|
||||
#[serde]
|
||||
pub(super) fn op_split(
|
||||
state: &mut OpState,
|
||||
#[string] regex: String,
|
||||
@@ -720,13 +795,30 @@ pub(super) fn op_split(
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub(super) enum SplitResult {
|
||||
Text(String),
|
||||
Captures(Vec<Option<String>>),
|
||||
}
|
||||
|
||||
impl<'a> ToV8<'a> for SplitResult {
|
||||
type Error = Infallible;
|
||||
fn to_v8<'i>(
|
||||
self,
|
||||
scope: &mut v8::PinScope<'a, 'i>,
|
||||
) -> std::result::Result<v8::Local<'a, v8::Value>, Self::Error> {
|
||||
Ok(match self {
|
||||
Self::Text(text) => {
|
||||
let Ok(value) = text.to_v8(scope);
|
||||
value
|
||||
}
|
||||
Self::Captures(captures) => {
|
||||
let Ok(value) = captures.to_v8(scope);
|
||||
value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) enum NixJsonValue {
|
||||
Null,
|
||||
Bool(bool),
|
||||
@@ -847,34 +939,24 @@ pub(super) fn op_from_toml(#[string] toml_str: String) -> Result<NixJsonValue> {
|
||||
toml_to_nix(parsed)
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub(super) struct FixedOutputInput {
|
||||
#[serde(rename = "hashAlgo")]
|
||||
hash_algo: String,
|
||||
hash: String,
|
||||
#[serde(rename = "hashMode")]
|
||||
hash_mode: String,
|
||||
}
|
||||
mod scope {
|
||||
use deno_core::{FromV8, ToV8};
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub(super) struct FinalizeDerivationInput {
|
||||
name: String,
|
||||
builder: String,
|
||||
platform: String,
|
||||
outputs: Vec<String>,
|
||||
args: Vec<String>,
|
||||
env: Vec<(String, String)>,
|
||||
context: Vec<String>,
|
||||
#[serde(rename = "fixedOutput")]
|
||||
fixed_output: Option<FixedOutputInput>,
|
||||
}
|
||||
#[derive(FromV8)]
|
||||
pub(super) struct FixedOutputInput {
|
||||
pub(super) hash_algo: String,
|
||||
pub(super) hash: String,
|
||||
pub(super) hash_mode: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
pub(super) struct FinalizeDerivationOutput {
|
||||
#[serde(rename = "drvPath")]
|
||||
drv_path: String,
|
||||
outputs: Vec<(String, String)>,
|
||||
#[derive(ToV8)]
|
||||
pub(super) struct FinalizeDerivationOutput {
|
||||
// renamed to `drvPath` automatically
|
||||
pub(super) drv_path: String,
|
||||
pub(super) outputs: Vec<(String, String)>,
|
||||
}
|
||||
}
|
||||
use scope::*;
|
||||
|
||||
fn output_path_name(drv_name: &str, output: &str) -> String {
|
||||
if output == "out" {
|
||||
@@ -885,10 +967,16 @@ fn output_path_name(drv_name: &str, output: &str) -> String {
|
||||
}
|
||||
|
||||
#[deno_core::op2]
|
||||
#[serde]
|
||||
pub(super) fn op_finalize_derivation<Ctx: RuntimeContext>(
|
||||
state: &mut OpState,
|
||||
#[serde] input: FinalizeDerivationInput,
|
||||
#[string] name: String,
|
||||
#[string] builder: String,
|
||||
#[string] platform: String,
|
||||
#[scoped] outputs: Vec<String>,
|
||||
#[scoped] args: Vec<String>,
|
||||
#[scoped] env: Vec<(String, String)>,
|
||||
#[scoped] context: Vec<String>,
|
||||
#[scoped] fixed_output: Option<FixedOutputInput>,
|
||||
) -> Result<FinalizeDerivationOutput> {
|
||||
use crate::derivation::{DerivationData, OutputInfo};
|
||||
use crate::string_context::extract_input_drvs_and_srcs;
|
||||
@@ -898,15 +986,15 @@ pub(super) fn op_finalize_derivation<Ctx: RuntimeContext>(
|
||||
let store_dir = store.get_store_dir().to_string();
|
||||
|
||||
let (input_drvs, input_srcs) =
|
||||
extract_input_drvs_and_srcs(&input.context).map_err(NixRuntimeError::from)?;
|
||||
extract_input_drvs_and_srcs(&context).map_err(NixRuntimeError::from)?;
|
||||
|
||||
let env: std::collections::BTreeMap<String, String> = input.env.into_iter().collect();
|
||||
let env: BTreeMap<String, String> = env.into_iter().collect();
|
||||
|
||||
let drv_path;
|
||||
let output_paths: Vec<(String, String)>;
|
||||
|
||||
if let Some(fixed) = &input.fixed_output {
|
||||
let path_name = output_path_name(&input.name, "out");
|
||||
if let Some(fixed) = &fixed_output {
|
||||
let path_name = output_path_name(&name, "out");
|
||||
let out_path = crate::runtime::ops::op_make_fixed_output_path_impl(
|
||||
&store_dir,
|
||||
&fixed.hash_algo,
|
||||
@@ -921,7 +1009,7 @@ pub(super) fn op_finalize_derivation<Ctx: RuntimeContext>(
|
||||
""
|
||||
};
|
||||
|
||||
let mut final_outputs = std::collections::BTreeMap::new();
|
||||
let mut final_outputs = BTreeMap::new();
|
||||
final_outputs.insert(
|
||||
"out".to_string(),
|
||||
OutputInfo {
|
||||
@@ -935,13 +1023,13 @@ pub(super) fn op_finalize_derivation<Ctx: RuntimeContext>(
|
||||
final_env.insert("out".to_string(), out_path.clone());
|
||||
|
||||
let drv = DerivationData {
|
||||
name: input.name.clone(),
|
||||
name: name.clone(),
|
||||
outputs: final_outputs,
|
||||
input_drvs: input_drvs.clone(),
|
||||
input_srcs: input_srcs.clone(),
|
||||
platform: input.platform,
|
||||
builder: input.builder,
|
||||
args: input.args,
|
||||
platform,
|
||||
builder,
|
||||
args,
|
||||
env: final_env,
|
||||
};
|
||||
|
||||
@@ -949,7 +1037,7 @@ pub(super) fn op_finalize_derivation<Ctx: RuntimeContext>(
|
||||
let references = drv.collect_references();
|
||||
|
||||
drv_path = store
|
||||
.add_text_to_store(&format!("{}.drv", input.name), &final_aterm, references)
|
||||
.add_text_to_store(&format!("{}.drv", name), &final_aterm, references)
|
||||
.map_err(|e| NixRuntimeError::from(format!("failed to write derivation: {}", e)))?;
|
||||
|
||||
let fixed_hash_fingerprint = format!(
|
||||
@@ -963,8 +1051,7 @@ pub(super) fn op_finalize_derivation<Ctx: RuntimeContext>(
|
||||
|
||||
output_paths = vec![("out".to_string(), out_path)];
|
||||
} else {
|
||||
let masked_outputs: std::collections::BTreeMap<String, OutputInfo> = input
|
||||
.outputs
|
||||
let masked_outputs: std::collections::BTreeMap<String, OutputInfo> = outputs
|
||||
.iter()
|
||||
.map(|o| {
|
||||
(
|
||||
@@ -979,18 +1066,18 @@ pub(super) fn op_finalize_derivation<Ctx: RuntimeContext>(
|
||||
.collect();
|
||||
|
||||
let mut masked_env = env.clone();
|
||||
for output in &input.outputs {
|
||||
for output in &outputs {
|
||||
masked_env.insert(output.clone(), String::new());
|
||||
}
|
||||
|
||||
let masked_drv = DerivationData {
|
||||
name: input.name.clone(),
|
||||
name: name.clone(),
|
||||
outputs: masked_outputs,
|
||||
input_drvs: input_drvs.clone(),
|
||||
input_srcs: input_srcs.clone(),
|
||||
platform: input.platform.clone(),
|
||||
builder: input.builder.clone(),
|
||||
args: input.args.clone(),
|
||||
platform: platform.clone(),
|
||||
builder: builder.clone(),
|
||||
args: args.clone(),
|
||||
env: masked_env,
|
||||
};
|
||||
|
||||
@@ -1018,8 +1105,8 @@ pub(super) fn op_finalize_derivation<Ctx: RuntimeContext>(
|
||||
let mut final_env = env;
|
||||
let mut result_output_paths = Vec::new();
|
||||
|
||||
for output_name in &input.outputs {
|
||||
let path_name = output_path_name(&input.name, output_name);
|
||||
for output_name in &outputs {
|
||||
let path_name = output_path_name(&name, output_name);
|
||||
let out_path = crate::nix_utils::make_store_path(
|
||||
&store_dir,
|
||||
&format!("output:{}", output_name),
|
||||
@@ -1039,13 +1126,13 @@ pub(super) fn op_finalize_derivation<Ctx: RuntimeContext>(
|
||||
}
|
||||
|
||||
let final_drv = DerivationData {
|
||||
name: input.name,
|
||||
name,
|
||||
outputs: final_outputs,
|
||||
input_drvs,
|
||||
input_srcs,
|
||||
platform: input.platform,
|
||||
builder: input.builder,
|
||||
args: input.args,
|
||||
platform,
|
||||
builder,
|
||||
args,
|
||||
env: final_env,
|
||||
};
|
||||
|
||||
@@ -1172,21 +1259,21 @@ pub(super) fn op_hash_file(#[string] algo: String, #[string] path: String) -> Re
|
||||
|
||||
#[deno_core::op2]
|
||||
#[string]
|
||||
pub(super) fn op_convert_hash(#[serde] input: ConvertHashInput) -> Result<String> {
|
||||
pub(super) fn op_convert_hash(
|
||||
#[string] hash: &str,
|
||||
#[string] algo: Option<String>,
|
||||
#[string] format: &str,
|
||||
) -> Result<String> {
|
||||
use nix_compat::nixhash::{HashAlgo, NixHash};
|
||||
|
||||
let hash_algo = input
|
||||
.hash_algo
|
||||
.as_deref()
|
||||
.and_then(|a| HashAlgo::from_str(a).ok());
|
||||
let hash_algo = algo.as_deref().and_then(|a| HashAlgo::from_str(a).ok());
|
||||
|
||||
let hash = NixHash::from_str(&input.hash, hash_algo).map_err(|e| {
|
||||
NixRuntimeError::from(format!("cannot convert hash '{}': {}", input.hash, e))
|
||||
})?;
|
||||
let hash = NixHash::from_str(hash, hash_algo)
|
||||
.map_err(|e| NixRuntimeError::from(format!("cannot convert hash '{}': {}", hash, e)))?;
|
||||
|
||||
let bytes = hash.digest_as_bytes();
|
||||
|
||||
match input.to_format.as_str() {
|
||||
match format {
|
||||
"base16" => Ok(hex::encode(bytes)),
|
||||
"nix32" | "base32" => Ok(nix_compat::nixbase32::encode(bytes)),
|
||||
"base64" => {
|
||||
@@ -1199,20 +1286,11 @@ pub(super) fn op_convert_hash(#[serde] input: ConvertHashInput) -> Result<String
|
||||
})),
|
||||
_ => Err(NixRuntimeError::from(format!(
|
||||
"unknown hash format '{}'",
|
||||
input.to_format
|
||||
format
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub(super) struct ConvertHashInput {
|
||||
hash: String,
|
||||
#[serde(rename = "hashAlgo")]
|
||||
hash_algo: Option<String>,
|
||||
#[serde(rename = "toHashFormat")]
|
||||
to_format: String,
|
||||
}
|
||||
|
||||
struct XmlCtx<'s> {
|
||||
force_fn: v8::Local<'s, v8::Function>,
|
||||
is_thunk: v8::Local<'s, v8::Symbol>,
|
||||
@@ -1254,7 +1332,7 @@ impl<'s> XmlCtx<'s> {
|
||||
struct XmlWriter {
|
||||
buf: String,
|
||||
context: Vec<String>,
|
||||
drvs_seen: hashbrown::HashSet<String>,
|
||||
drvs_seen: HashSet<String>,
|
||||
}
|
||||
|
||||
impl XmlWriter {
|
||||
@@ -1262,7 +1340,7 @@ impl XmlWriter {
|
||||
Self {
|
||||
buf: String::with_capacity(4096),
|
||||
context: Vec::new(),
|
||||
drvs_seen: hashbrown::HashSet::new(),
|
||||
drvs_seen: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1753,7 +1831,6 @@ impl<'a> FromV8<'a> for ToXmlResult {
|
||||
}
|
||||
|
||||
#[deno_core::op2]
|
||||
#[serde]
|
||||
pub(super) fn op_to_xml(#[scoped] value: ToXmlResult) -> (String, Vec<String>) {
|
||||
(value.xml, value.context)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user