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