optimize: generate shorter code
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -9,4 +9,4 @@ profile.json.gz
|
||||
prof.json
|
||||
*.cpuprofile
|
||||
*.cpuprofile.gz
|
||||
*v8.log
|
||||
*v8.log*
|
||||
|
||||
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -1986,7 +1986,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nix-compat"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.snix.dev/snix/snix.git#db30e92b30e18ca4d813206ac1b60d1f670adb8c"
|
||||
source = "git+https://git.snix.dev/snix/snix.git#1b37f68842a7e5e226d9dc009e9a90d400c5fb14"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bstr",
|
||||
@@ -2009,7 +2009,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "nix-compat-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.snix.dev/snix/snix.git#db30e92b30e18ca4d813206ac1b60d1f670adb8c"
|
||||
source = "git+https://git.snix.dev/snix/snix.git#1b37f68842a7e5e226d9dc009e9a90d400c5fb14"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3271,9 +3271,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn-match"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "783c4140d7ed89f37116e865b49e5a9fdd28608b9071a9dd1e158b50fc0a31fc"
|
||||
checksum = "54b8f0a9004d6aafa6a588602a1119e6cdaacec9921aa1605383e6e7d6258fd6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -154,6 +154,6 @@ export const unsafeGetAttrPos =
|
||||
return null;
|
||||
}
|
||||
|
||||
const span = positions.get(name) as string;
|
||||
const span = positions.get(name) as number;
|
||||
return mkPos(span);
|
||||
};
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { NixAttrs, NixBool, NixPath, NixString, NixValue } from "./types";
|
||||
import { CatchableError, isNixPath } from "./types";
|
||||
|
||||
interface StackFrame {
|
||||
span: string;
|
||||
span: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ function enrichError(error: unknown): Error {
|
||||
return err;
|
||||
}
|
||||
|
||||
const pushContext = (message: string, span: string): void => {
|
||||
const pushContext = (message: string, span: number): void => {
|
||||
if (callStack.length >= MAX_STACK_DEPTH) {
|
||||
callStack.shift();
|
||||
}
|
||||
@@ -43,7 +43,7 @@ const popContext = (): void => {
|
||||
callStack.pop();
|
||||
};
|
||||
|
||||
export const withContext = <T>(message: string, span: string, fn: () => T): T => {
|
||||
export const withContext = <T>(message: string, span: number, fn: () => T): T => {
|
||||
pushContext(message, span);
|
||||
try {
|
||||
return fn();
|
||||
@@ -142,8 +142,8 @@ export const resolvePath = (currentDir: string, path: NixValue): NixPath => {
|
||||
return mkPath(resolved);
|
||||
};
|
||||
|
||||
export const select = (obj: NixValue, attrpath: NixValue[], span?: string): NixValue => {
|
||||
if (span) {
|
||||
export const select = (obj: NixValue, attrpath: NixValue[], span?: number): NixValue => {
|
||||
if (span !== undefined) {
|
||||
if (callStack.length >= MAX_STACK_DEPTH) {
|
||||
callStack.shift();
|
||||
}
|
||||
@@ -190,9 +190,9 @@ export const selectWithDefault = (
|
||||
obj: NixValue,
|
||||
attrpath: NixValue[],
|
||||
defaultVal: NixValue,
|
||||
span?: string,
|
||||
span?: number,
|
||||
): NixValue => {
|
||||
if (span) {
|
||||
if (span !== undefined) {
|
||||
if (callStack.length >= MAX_STACK_DEPTH) {
|
||||
callStack.shift();
|
||||
}
|
||||
@@ -263,8 +263,8 @@ export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
|
||||
return attrs.has(forceStringValue(attrpath[attrpath.length - 1]));
|
||||
};
|
||||
|
||||
export const call = (func: NixValue, arg: NixValue, span?: string): NixValue => {
|
||||
if (span) {
|
||||
export const call = (func: NixValue, arg: NixValue, span?: number): NixValue => {
|
||||
if (span !== undefined) {
|
||||
if (callStack.length >= MAX_STACK_DEPTH) {
|
||||
callStack.shift();
|
||||
}
|
||||
@@ -282,19 +282,19 @@ export const call = (func: NixValue, arg: NixValue, span?: string): NixValue =>
|
||||
};
|
||||
|
||||
function callImpl(func: NixValue, arg: NixValue): NixValue {
|
||||
const forcedFunc = force(func);
|
||||
if (typeof forcedFunc === "function") {
|
||||
forcedFunc.args?.check(arg);
|
||||
return forcedFunc(arg);
|
||||
const forced = force(func);
|
||||
if (typeof forced === "function") {
|
||||
forced.args?.check(arg);
|
||||
return forced(arg);
|
||||
}
|
||||
if (forcedFunc instanceof Map && forcedFunc.has("__functor")) {
|
||||
const functor = forceFunction(forcedFunc.get("__functor") as NixValue);
|
||||
return call(functor(forcedFunc), arg);
|
||||
if (forced instanceof Map && forced.has("__functor")) {
|
||||
const functor = forceFunction(forced.get("__functor") as NixValue);
|
||||
return call(callImpl(functor, forced), arg);
|
||||
}
|
||||
throw new Error(`attempt to call something which is not a function but ${typeOf(forcedFunc)}`);
|
||||
throw new Error(`attempt to call something which is not a function but ${typeOf(forced)}`);
|
||||
}
|
||||
|
||||
export const assert = (assertion: NixValue, expr: NixValue, assertionRaw: string, span: string): NixValue => {
|
||||
export const assert = (assertion: NixValue, expr: NixValue, assertionRaw: string, span: number): NixValue => {
|
||||
if (forceBool(assertion)) {
|
||||
return expr;
|
||||
}
|
||||
@@ -304,14 +304,7 @@ export const assert = (assertion: NixValue, expr: NixValue, assertionRaw: string
|
||||
throw "unreachable";
|
||||
};
|
||||
|
||||
export const ifFunc = (cond: NixValue, consq: NixValue, alter: NixValue) => {
|
||||
if (forceBool(cond)) {
|
||||
return consq;
|
||||
}
|
||||
return alter;
|
||||
};
|
||||
|
||||
export const mkPos = (span: string): NixAttrs => {
|
||||
export const mkPos = (span: number): NixAttrs => {
|
||||
return new Map(Object.entries(Deno.core.ops.op_decode_span(span)));
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import { builtins, PRIMOP_METADATA } from "./builtins";
|
||||
import { coerceToString, StringCoercionMode } from "./builtins/conversion";
|
||||
import {
|
||||
assert,
|
||||
call,
|
||||
@@ -16,40 +15,31 @@ import {
|
||||
resolvePath,
|
||||
select,
|
||||
selectWithDefault,
|
||||
withContext,
|
||||
} from "./helpers";
|
||||
import { op } from "./operators";
|
||||
import { HAS_CONTEXT } from "./string-context";
|
||||
import {
|
||||
createThunk,
|
||||
DEBUG_THUNKS,
|
||||
force,
|
||||
forceDeep,
|
||||
forceShallow,
|
||||
IS_CYCLE,
|
||||
IS_THUNK,
|
||||
isThunk,
|
||||
} from "./thunk";
|
||||
import { createThunk, DEBUG_THUNKS, force, forceDeep, forceShallow, IS_CYCLE, IS_THUNK } from "./thunk";
|
||||
import { forceBool } from "./type-assert";
|
||||
import { ATTR_POSITIONS, IS_PATH, mkAttrs, mkAttrsWithPos, mkFunction, type NixValue } from "./types";
|
||||
import { IS_PATH, mkAttrs, mkFunction, type NixValue } from "./types";
|
||||
|
||||
export type NixRuntime = typeof Nix;
|
||||
|
||||
const replBindings: Map<string, NixValue> = new Map();
|
||||
|
||||
export const Nix = {
|
||||
createThunk,
|
||||
force,
|
||||
forceShallow,
|
||||
forceDeep,
|
||||
forceBool,
|
||||
isThunk,
|
||||
IS_THUNK,
|
||||
IS_CYCLE,
|
||||
HAS_CONTEXT,
|
||||
IS_PATH,
|
||||
PRIMOP_METADATA,
|
||||
DEBUG_THUNKS,
|
||||
|
||||
createThunk,
|
||||
force,
|
||||
forceBool,
|
||||
forceShallow,
|
||||
forceDeep,
|
||||
|
||||
assert,
|
||||
call,
|
||||
hasAttr,
|
||||
@@ -57,20 +47,13 @@ export const Nix = {
|
||||
selectWithDefault,
|
||||
lookupWith,
|
||||
resolvePath,
|
||||
coerceToString,
|
||||
concatStringsWithContext,
|
||||
StringCoercionMode,
|
||||
mkAttrs,
|
||||
mkAttrsWithPos,
|
||||
mkFunction,
|
||||
mkPos,
|
||||
ATTR_POSITIONS,
|
||||
|
||||
withContext,
|
||||
|
||||
op,
|
||||
builtins,
|
||||
PRIMOP_METADATA,
|
||||
|
||||
replBindings,
|
||||
setReplBinding: (name: string, value: NixValue) => {
|
||||
@@ -80,3 +63,29 @@ export const Nix = {
|
||||
};
|
||||
|
||||
globalThis.Nix = Nix;
|
||||
globalThis.$t = createThunk;
|
||||
globalThis.$f = force;
|
||||
globalThis.$fb = forceBool;
|
||||
globalThis.$a = assert;
|
||||
globalThis.$c = call;
|
||||
globalThis.$h = hasAttr;
|
||||
globalThis.$s = select;
|
||||
globalThis.$sd = selectWithDefault;
|
||||
globalThis.$l = lookupWith;
|
||||
globalThis.$r = resolvePath;
|
||||
globalThis.$cs = concatStringsWithContext;
|
||||
globalThis.$ma = mkAttrs;
|
||||
globalThis.$mf = mkFunction;
|
||||
globalThis.$mp = mkPos;
|
||||
globalThis.$gb = Nix.getReplBinding;
|
||||
|
||||
globalThis.$oa = op.add;
|
||||
globalThis.$os = op.sub;
|
||||
globalThis.$om = op.mul;
|
||||
globalThis.$od = op.div;
|
||||
globalThis.$oe = op.eq;
|
||||
globalThis.$ol = op.lt;
|
||||
globalThis.$og = op.gt;
|
||||
globalThis.$oc = op.concat;
|
||||
globalThis.$ou = op.update;
|
||||
globalThis.$b = builtins;
|
||||
|
||||
@@ -27,15 +27,15 @@ export type NixNull = null;
|
||||
|
||||
export const ATTR_POSITIONS = Symbol("attrPositions");
|
||||
export type NixList = NixValue[];
|
||||
export type NixAttrs = Map<string, NixValue> & { [ATTR_POSITIONS]?: Map<string, string> };
|
||||
export type NixAttrs = Map<string, NixValue> & { [ATTR_POSITIONS]?: Map<string, number> };
|
||||
export type NixFunction = ((arg: NixValue) => NixValue) & { args?: NixArgs };
|
||||
export class NixArgs {
|
||||
required: string[];
|
||||
optional: string[];
|
||||
allowed: Set<string>;
|
||||
ellipsis: boolean;
|
||||
positions: Map<string, string>;
|
||||
constructor(required: string[], optional: string[], positions: Map<string, string>, ellipsis: boolean) {
|
||||
positions: Map<string, number>;
|
||||
constructor(required: string[], optional: string[], positions: Map<string, number>, ellipsis: boolean) {
|
||||
this.required = required;
|
||||
this.optional = optional;
|
||||
this.positions = positions;
|
||||
@@ -64,7 +64,7 @@ export const mkFunction = (
|
||||
f: (arg: NixValue) => NixValue,
|
||||
required: string[],
|
||||
optional: string[],
|
||||
positions: Map<string, string>,
|
||||
positions: Map<string, number>,
|
||||
ellipsis: boolean,
|
||||
): NixFunction => {
|
||||
const func: NixFunction = f;
|
||||
@@ -72,23 +72,10 @@ export const mkFunction = (
|
||||
return func;
|
||||
};
|
||||
|
||||
export const mkAttrs = (attrs: NixAttrs, keys: NixValue[], values: NixValue[]): NixAttrs => {
|
||||
const len = keys.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
const key = force(keys[i]);
|
||||
if (key === null) {
|
||||
continue;
|
||||
}
|
||||
const str = forceStringNoCtx(key);
|
||||
attrs.set(str, values[i]);
|
||||
}
|
||||
return attrs;
|
||||
};
|
||||
|
||||
export const mkAttrsWithPos = (
|
||||
export const mkAttrs = (
|
||||
attrs: NixAttrs,
|
||||
positions: Map<string, string>,
|
||||
dyns?: { dynKeys: NixValue[]; dynVals: NixValue[]; dynSpans: string[] },
|
||||
positions: Map<string, number>,
|
||||
dyns?: { dynKeys: NixValue[]; dynVals: NixValue[]; dynSpans: number[] },
|
||||
): NixAttrs => {
|
||||
if (dyns) {
|
||||
const len = dyns.dynKeys.length;
|
||||
|
||||
50
nix-js/runtime-ts/src/types/global.d.ts
vendored
50
nix-js/runtime-ts/src/types/global.d.ts
vendored
@@ -1,8 +1,50 @@
|
||||
import type { NixRuntime } from "..";
|
||||
import type { builtins } from "../builtins";
|
||||
import type { FetchGitResult, FetchTarballResult, FetchUrlResult } from "../builtins/io";
|
||||
import type {
|
||||
assert,
|
||||
call,
|
||||
concatStringsWithContext,
|
||||
hasAttr,
|
||||
lookupWith,
|
||||
mkPos,
|
||||
resolvePath,
|
||||
select,
|
||||
selectWithDefault,
|
||||
} from "../helpers";
|
||||
import type { op } from "../operators";
|
||||
import type { createThunk, force } from "../thunk";
|
||||
import type { forceBool } from "../type-assert";
|
||||
import type { mkAttrs, mkFunction } from "../types";
|
||||
|
||||
declare global {
|
||||
var Nix: NixRuntime;
|
||||
var $t: typeof createThunk;
|
||||
var $f: typeof force;
|
||||
var $fb: typeof forceBool;
|
||||
var $a: typeof assert;
|
||||
var $c: typeof call;
|
||||
var $h: typeof hasAttr;
|
||||
var $s: typeof select;
|
||||
var $sd: typeof selectWithDefault;
|
||||
var $l: typeof lookupWith;
|
||||
var $r: typeof resolvePath;
|
||||
var $cs: typeof concatStringsWithContext;
|
||||
var $ma: typeof mkAttrs;
|
||||
var $mf: typeof mkFunction;
|
||||
var $mp: typeof mkPos;
|
||||
var $oa: typeof op.add;
|
||||
var $os: typeof op.sub;
|
||||
var $om: typeof op.mul;
|
||||
var $od: typeof op.div;
|
||||
var $oe: typeof op.eq;
|
||||
var $ol: typeof op.lt;
|
||||
var $og: typeof op.gt;
|
||||
var $oc: typeof op.concat;
|
||||
var $ou: typeof op.update;
|
||||
var $b: typeof builtins;
|
||||
var $gb: typeof Nix.getReplBinding;
|
||||
|
||||
namespace Deno {
|
||||
namespace core {
|
||||
namespace ops {
|
||||
@@ -20,11 +62,7 @@ declare global {
|
||||
function op_make_placeholder(output: string): string;
|
||||
function op_store_path(path: string): string;
|
||||
|
||||
function op_convert_hash(input: {
|
||||
hash: string;
|
||||
hashAlgo: string | null;
|
||||
toHashFormat: string;
|
||||
}): string;
|
||||
function op_convert_hash(hash: string, hashAlgo: string | null, toHashFormat: string): string;
|
||||
function op_hash_string(algo: string, data: string): string;
|
||||
function op_hash_file(algo: string, path: string): string;
|
||||
function op_parse_hash(hashStr: string, algo: string | null): { hex: string; algo: string };
|
||||
@@ -43,7 +81,7 @@ declare global {
|
||||
includePaths: string[],
|
||||
): string;
|
||||
|
||||
function op_decode_span(span: string): {
|
||||
function op_decode_span(span: number): {
|
||||
file: string | null;
|
||||
line: number | null;
|
||||
column: number | null;
|
||||
|
||||
@@ -31,14 +31,10 @@ pub(crate) fn compile(expr: &Ir, ctx: &impl CodegenContext) -> String {
|
||||
|
||||
code!(&mut buf, ctx; "(()=>{");
|
||||
|
||||
if std::env::var("NIX_JS_DEBUG_THUNKS").is_ok() {
|
||||
code!(&mut buf, ctx; "Nix.DEBUG_THUNKS.enabled=true;");
|
||||
}
|
||||
|
||||
code!(&mut buf, ctx;
|
||||
"const __currentDir="
|
||||
"const _d="
|
||||
quoted(&ctx.get_current_dir().display().to_string())
|
||||
";const __with=null;return "
|
||||
",_w=null;return "
|
||||
expr
|
||||
"})()");
|
||||
|
||||
@@ -48,16 +44,12 @@ pub(crate) fn compile(expr: &Ir, ctx: &impl CodegenContext) -> String {
|
||||
pub(crate) fn compile_scoped(expr: &Ir, ctx: &impl CodegenContext) -> String {
|
||||
let mut buf = CodeBuffer::with_capacity(8192);
|
||||
|
||||
code!(&mut buf, ctx; "((__scope)=>{");
|
||||
|
||||
if std::env::var("NIX_JS_DEBUG_THUNKS").is_ok() {
|
||||
code!(&mut buf, ctx; "Nix.DEBUG_THUNKS.enabled=true;");
|
||||
}
|
||||
code!(&mut buf, ctx; "((_s)=>{");
|
||||
|
||||
code!(&mut buf, ctx;
|
||||
"const __currentDir="
|
||||
"const _d="
|
||||
quoted(&ctx.get_current_dir().display().to_string())
|
||||
";return "
|
||||
",_w=null;return "
|
||||
expr
|
||||
"})"
|
||||
);
|
||||
@@ -191,13 +183,7 @@ where
|
||||
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for rnix::TextRange {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
code!(
|
||||
buf,
|
||||
"\"{}:{}:{}\"",
|
||||
ctx.get_current_source_id(),
|
||||
usize::from(self.start()),
|
||||
usize::from(self.end())
|
||||
);
|
||||
code!(buf, "{}", ctx.register_span(*self));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,6 +193,7 @@ pub(crate) trait CodegenContext {
|
||||
fn get_current_dir(&self) -> &Path;
|
||||
fn get_store_dir(&self) -> &str;
|
||||
fn get_current_source_id(&self) -> usize;
|
||||
fn register_span(&self, range: rnix::TextRange) -> usize;
|
||||
}
|
||||
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for ExprId {
|
||||
@@ -240,7 +227,8 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||
code!(buf, ctx; quoted(&s.val));
|
||||
}
|
||||
Ir::Path(p) => {
|
||||
code!(buf, ctx; "Nix.resolvePath(__currentDir," ctx.get_ir(p.expr) ")");
|
||||
// Nix.resolvePath
|
||||
code!(buf, ctx; "$r(_d," ctx.get_ir(p.expr) ")");
|
||||
}
|
||||
Ir::If(x) => x.compile(ctx, buf),
|
||||
Ir::BinOp(x) => x.compile(ctx, buf),
|
||||
@@ -258,11 +246,13 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||
code!(buf, "expr{}", expr_id.0);
|
||||
}
|
||||
Ir::Builtins(_) => {
|
||||
code!(buf, ctx; "Nix.builtins");
|
||||
// Nix.builtins
|
||||
code!(buf, ctx; "$b");
|
||||
}
|
||||
&Ir::Builtin(Builtin { inner: name, .. }) => {
|
||||
// Nix.builtins
|
||||
code!(buf, ctx;
|
||||
"Nix.builtins.get("
|
||||
"$b.get("
|
||||
ctx.get_sym(name)
|
||||
")"
|
||||
);
|
||||
@@ -275,50 +265,51 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||
ref assertion_raw,
|
||||
span: assert_span,
|
||||
}) => {
|
||||
let assertion_ir = ctx.get_ir(assertion);
|
||||
let assertion_span = assertion_ir.span();
|
||||
let assertion = ctx.get_ir(assertion);
|
||||
|
||||
// Nix.assert
|
||||
code!(buf, ctx;
|
||||
"Nix.assert(Nix.withContext(\"while evaluating the condition of the assert statement\","
|
||||
assertion_span
|
||||
",()=>("
|
||||
assertion_ir
|
||||
")),"
|
||||
ctx.get_ir(expr)
|
||||
","
|
||||
quoted(assertion_raw)
|
||||
","
|
||||
assert_span
|
||||
"$a("
|
||||
assertion
|
||||
","
|
||||
ctx.get_ir(expr)
|
||||
","
|
||||
quoted(assertion_raw)
|
||||
","
|
||||
assert_span
|
||||
")"
|
||||
);
|
||||
}
|
||||
Ir::CurPos(cur_pos) => {
|
||||
// Nix.mkPos
|
||||
code!(buf, ctx;
|
||||
"Nix.mkPos("
|
||||
cur_pos.span
|
||||
"$mp("
|
||||
cur_pos.span
|
||||
")"
|
||||
);
|
||||
}
|
||||
&Ir::ReplBinding(ReplBinding { inner: name, .. }) => {
|
||||
// Nix.getReplBinding
|
||||
code!(buf, ctx;
|
||||
"Nix.getReplBinding("
|
||||
"$gb("
|
||||
ctx.get_sym(name)
|
||||
")"
|
||||
);
|
||||
}
|
||||
&Ir::ScopedImportBinding(ScopedImportBinding { inner: name, .. }) => {
|
||||
code!(buf, ctx;
|
||||
"__scope.get("
|
||||
"_s.get("
|
||||
ctx.get_sym(name)
|
||||
")"
|
||||
);
|
||||
}
|
||||
Ir::WithExpr(x) => x.compile(ctx, buf),
|
||||
&Ir::WithLookup(WithLookup { inner: name, .. }) => {
|
||||
// Nix.lookupWith
|
||||
code!(buf, ctx;
|
||||
"Nix.lookupWith("
|
||||
"$l("
|
||||
ctx.get_sym(name)
|
||||
",__with)"
|
||||
",_w)"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -333,13 +324,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for If {
|
||||
alter,
|
||||
span: _,
|
||||
} = self;
|
||||
let cond_ir = ctx.get_ir(cond);
|
||||
let cond_span = cond_ir.span();
|
||||
let cond = ctx.get_ir(cond);
|
||||
|
||||
code!(buf, ctx;
|
||||
"(Nix.withContext(\"while evaluating a branch condition\"," cond_span ",()=>Nix.forceBool(" cond_ir ")))"
|
||||
"?(" consq "):(" alter ")"
|
||||
);
|
||||
// Nix.forceBool
|
||||
code!(buf, ctx; "$fb(" cond ")?(" consq "):(" alter ")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,65 +340,50 @@ impl<Ctx: CodegenContext> Compile<Ctx> for BinOp {
|
||||
|
||||
match self.kind {
|
||||
Add | Sub | Mul | Div | Eq | Neq | Lt | Gt | Leq | Geq | Con | Upd => {
|
||||
let op_name = match self.kind {
|
||||
Add => "+",
|
||||
Sub => "-",
|
||||
Mul => "*",
|
||||
Div => "/",
|
||||
Eq => "==",
|
||||
Neq => "!=",
|
||||
Lt => "<",
|
||||
Gt => ">",
|
||||
Leq => "<=",
|
||||
Geq => ">=",
|
||||
Con => "++",
|
||||
Upd => "//",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let op_func = match self.kind {
|
||||
Add => "Nix.op.add",
|
||||
Sub => "Nix.op.sub",
|
||||
Mul => "Nix.op.mul",
|
||||
Div => "Nix.op.div",
|
||||
Eq => "Nix.op.eq",
|
||||
Neq => "Nix.op.neq",
|
||||
Lt => "Nix.op.lt",
|
||||
Gt => "Nix.op.gt",
|
||||
Leq => "Nix.op.lte",
|
||||
Geq => "Nix.op.gte",
|
||||
Con => "Nix.op.concat",
|
||||
Upd => "Nix.op.update",
|
||||
Add => "$oa",
|
||||
Sub => "$os",
|
||||
Mul => "$om",
|
||||
Div => "$od",
|
||||
Eq => "$oe",
|
||||
Neq => "!$oe",
|
||||
Lt => "$ol",
|
||||
Gt => "$og",
|
||||
Leq => "!$og",
|
||||
Geq => "!$ol",
|
||||
Con => "$oc",
|
||||
Upd => "$ou",
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating the " op_name " operator\"," self.span ",()=>(" op_func "(" lhs "," rhs ")))"
|
||||
op_func "(" lhs "," rhs ")"
|
||||
);
|
||||
}
|
||||
And => {
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating the && operator\"," self.span ",()=>(Nix.forceBool(" lhs ")&&Nix.forceBool(" rhs ")))"
|
||||
"$fb(" lhs ")" "&&" "$fb(" rhs ")"
|
||||
);
|
||||
}
|
||||
Or => {
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating the || operator\"," self.span ",()=>(Nix.forceBool(" lhs ")||Nix.forceBool(" rhs ")))"
|
||||
"$fb(" lhs ")" "||" "$fb(" rhs ")"
|
||||
);
|
||||
}
|
||||
Impl => {
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating the -> operator\"," self.span ",()=>(!Nix.forceBool(" lhs ")||Nix.forceBool(" rhs ")))"
|
||||
"!$fb(" lhs ")" "||" "$fb(" rhs ")"
|
||||
);
|
||||
}
|
||||
PipeL => {
|
||||
code!(buf, ctx; "Nix.call(" rhs "," lhs ")");
|
||||
code!(buf, ctx; "$c(" rhs "," lhs ")");
|
||||
}
|
||||
PipeR => {
|
||||
code!(buf, ctx; "Nix.call(" lhs "," rhs ")");
|
||||
code!(buf, ctx; "$c(" lhs "," rhs ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -422,10 +395,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for UnOp {
|
||||
let rhs = ctx.get_ir(self.rhs);
|
||||
match self.kind {
|
||||
Neg => {
|
||||
code!(buf, ctx; "Nix.op.sub(0n," rhs ")");
|
||||
code!(buf, ctx; "$os(0n," rhs ")");
|
||||
}
|
||||
Not => {
|
||||
code!(buf, ctx; "Nix.op.bnot(" ctx.get_ir(self.rhs) ")");
|
||||
code!(buf, ctx; "!$fb(" ctx.get_ir(self.rhs) ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -443,7 +416,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Func {
|
||||
ellipsis,
|
||||
}) = &self.param
|
||||
{
|
||||
code!(buf, "Nix.mkFunction(arg{}=>", id);
|
||||
code!(buf, "$mf(arg{}=>", id);
|
||||
if has_thunks {
|
||||
code!(buf, ctx; "{" self.thunks "return " self.body "}");
|
||||
} else {
|
||||
@@ -458,11 +431,11 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Func {
|
||||
joined(optional.iter(), ",", |ctx: &Ctx, buf, &(sym, _)| {
|
||||
code!(buf, ctx; ctx.get_sym(sym));
|
||||
})
|
||||
"],new Map()"
|
||||
joined(required.iter().chain(optional.iter()), "", |ctx: &Ctx, buf, &(sym, span)| {
|
||||
code!(buf, ctx; ".set(" ctx.get_sym(sym) "," span ")");
|
||||
"],new Map(["
|
||||
joined(required.iter().chain(optional.iter()), ",", |ctx: &Ctx, buf, &(sym, span)| {
|
||||
code!(buf, ctx; "[" ctx.get_sym(sym) "," span "]");
|
||||
})
|
||||
","
|
||||
"]),"
|
||||
ellipsis
|
||||
")"
|
||||
);
|
||||
@@ -480,7 +453,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Func {
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for Call {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
code!(buf, ctx;
|
||||
"Nix.call("
|
||||
"$c("
|
||||
ctx.get_ir(self.func)
|
||||
","
|
||||
ctx.get_ir(self.arg)
|
||||
@@ -501,7 +474,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for [(ExprId, ExprId)] {
|
||||
let inner_ir = ctx.get_ir(inner);
|
||||
code!(
|
||||
buf, ctx;
|
||||
"let expr" slot.0 "=Nix.createThunk(()=>(" inner_ir "),"
|
||||
"let expr" slot.0 "=$t(()=>(" inner_ir "),"
|
||||
"\"expr" slot.0 "\");"
|
||||
);
|
||||
}
|
||||
@@ -525,9 +498,9 @@ impl<Ctx: CodegenContext> Compile<Ctx> for WithExpr {
|
||||
let body = ctx.get_ir(self.body);
|
||||
let has_thunks = !self.thunks.is_empty();
|
||||
if has_thunks {
|
||||
code!(buf, ctx; "((__with)=>{" self.thunks "return " body "})({env:" namespace ",last:__with})");
|
||||
code!(buf, ctx; "((_w)=>{" self.thunks "return " body "})({env:" namespace ",last:_w})");
|
||||
} else {
|
||||
code!(buf, ctx; "((__with)=>(" body "))({env:" namespace ",last:__with})");
|
||||
code!(buf, ctx; "((_w)=>(" body "))({env:" namespace ",last:_w})");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -536,7 +509,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Select {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
if let Some(default) = self.default {
|
||||
code!(buf, ctx;
|
||||
"Nix.selectWithDefault("
|
||||
"$sd("
|
||||
ctx.get_ir(self.expr)
|
||||
",["
|
||||
joined(self.attrpath.iter(), ",", |ctx: &Ctx, buf, attr| {
|
||||
@@ -553,7 +526,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Select {
|
||||
);
|
||||
} else {
|
||||
code!(buf, ctx;
|
||||
"Nix.select("
|
||||
"$s("
|
||||
ctx.get_ir(self.expr)
|
||||
",["
|
||||
joined(self.attrpath.iter(), ",", |ctx: &Ctx, buf, attr| {
|
||||
@@ -574,31 +547,28 @@ impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
if !self.dyns.is_empty() {
|
||||
code!(buf, ctx;
|
||||
"Nix.mkAttrsWithPos(new Map()"
|
||||
joined(self.stcs.iter(), "", |ctx: &Ctx, buf, (&sym, &(expr, _))| {
|
||||
"$ma(new Map(["
|
||||
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(expr, _))| {
|
||||
let key = ctx.get_sym(sym);
|
||||
let val = ctx.get_ir(expr);
|
||||
|
||||
code!(
|
||||
buf, ctx;
|
||||
".set(" key ",Nix.withContext(\"while evaluating the attribute '" escaped(&key) "'\"," val.span() ",()=>(" val ")))"
|
||||
"[" key "," val "]"
|
||||
);
|
||||
})
|
||||
",new Map()"
|
||||
joined(self.stcs.iter(), "", |ctx: &Ctx, buf, (&sym, &(_, span))| {
|
||||
code!(buf, ctx; ".set(" ctx.get_sym(sym) "," span ")");
|
||||
"]),new Map(["
|
||||
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(_, span))| {
|
||||
code!(buf, ctx; "[" ctx.get_sym(sym) "," span "]");
|
||||
})
|
||||
",{dynKeys:["
|
||||
"]),{dynKeys:["
|
||||
joined(self.dyns.iter(), ",", |ctx: &Ctx, buf, (key, _, _)| {
|
||||
code!(buf, ctx; ctx.get_ir(*key));
|
||||
})
|
||||
"],dynVals:["
|
||||
joined(self.dyns.iter(), ",", |ctx: &Ctx, buf, (_, val, _)| {
|
||||
let val = ctx.get_ir(*val);
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating a dynamic attribute\"," val.span() ",()=>(" val "))"
|
||||
);
|
||||
code!(buf, ctx; val);
|
||||
})
|
||||
"],dynSpans:["
|
||||
joined(self.dyns.iter(), ",", |ctx: &Ctx, buf, (_, _, attr_span)| {
|
||||
@@ -608,21 +578,21 @@ impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
||||
);
|
||||
} else if !self.stcs.is_empty() {
|
||||
code!(buf, ctx;
|
||||
"Nix.mkAttrsWithPos(new Map()"
|
||||
joined(self.stcs.iter(), "", |ctx: &Ctx, buf, (&sym, &(expr, _))| {
|
||||
"$ma(new Map(["
|
||||
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(expr, _))| {
|
||||
let key = ctx.get_sym(sym);
|
||||
let val = ctx.get_ir(expr);
|
||||
|
||||
code!(
|
||||
buf, ctx;
|
||||
".set(" key ",Nix.withContext(\"while evaluating the attribute '" escaped(&key) "'\"," val.span() ",()=>(" val ")))"
|
||||
"[" key "," val "]"
|
||||
);
|
||||
})
|
||||
",new Map()"
|
||||
joined(self.stcs.iter(), "", |ctx: &Ctx, buf, (&sym, &(_, span))| {
|
||||
code!(buf, ctx; ".set(" ctx.get_sym(sym) "," span ")");
|
||||
"]),new Map(["
|
||||
joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(_, span))| {
|
||||
code!(buf, ctx; "[" ctx.get_sym(sym) "," span "]");
|
||||
})
|
||||
")"
|
||||
"]))"
|
||||
);
|
||||
} else {
|
||||
code!(buf, ctx; "new Map()");
|
||||
@@ -634,12 +604,9 @@ impl<Ctx: CodegenContext> Compile<Ctx> for List {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
code!(buf, ctx;
|
||||
"["
|
||||
joined(self.items.iter().enumerate(), ",", |ctx: &Ctx, buf, (idx, item)| {
|
||||
joined(self.items.iter(), ",", |ctx: &Ctx, buf, item| {
|
||||
let item = ctx.get_ir(*item);
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating list element " idx "\"," item.span() ",()=>(" item "))"
|
||||
);
|
||||
code!(buf, ctx; item);
|
||||
})
|
||||
"]"
|
||||
);
|
||||
@@ -649,13 +616,10 @@ impl<Ctx: CodegenContext> Compile<Ctx> for List {
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for ConcatStrings {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
code!(buf, ctx;
|
||||
"Nix.concatStringsWithContext(["
|
||||
"$cs(["
|
||||
joined(self.parts.iter(), ",", |ctx: &Ctx, buf, part| {
|
||||
let part = ctx.get_ir(*part);
|
||||
code!(
|
||||
buf, ctx;
|
||||
"Nix.withContext(\"while evaluating a path segment\"," part.span() ",()=>(" part "))"
|
||||
);
|
||||
code!(buf, ctx; part);
|
||||
})
|
||||
"]," self.force_string ")"
|
||||
);
|
||||
@@ -665,7 +629,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for ConcatStrings {
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for HasAttr {
|
||||
fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) {
|
||||
code!(buf, ctx;
|
||||
"Nix.hasAttr("
|
||||
"$h("
|
||||
ctx.get_ir(self.lhs)
|
||||
",["
|
||||
joined(self.rhs.iter(), ",", |ctx: &Ctx, buf, attr| {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::path::Path;
|
||||
use std::ptr::NonNull;
|
||||
|
||||
@@ -123,9 +124,14 @@ impl Context {
|
||||
let code = self.ctx.compile(source, None)?;
|
||||
self.runtime.eval(
|
||||
format!(
|
||||
"Nix.builtins.set('derivation',({}));Nix.builtins.set('storeDir','{}')",
|
||||
"Nix.builtins.set('derivation',({}));Nix.builtins.set('storeDir','{}');{}0n",
|
||||
code,
|
||||
self.get_store_dir()
|
||||
self.get_store_dir(),
|
||||
if std::env::var("NIX_JS_DEBUG_THUNKS").is_ok() {
|
||||
"Nix.DEBUG_THUNKS.enabled=true;"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
),
|
||||
&mut self.ctx,
|
||||
)?;
|
||||
@@ -182,6 +188,7 @@ pub(crate) struct Ctx {
|
||||
global: NonNull<HashMap<SymId, ExprId>>,
|
||||
sources: Vec<Source>,
|
||||
store: DaemonStore,
|
||||
spans: UnsafeCell<Vec<(usize, TextRange)>>,
|
||||
}
|
||||
|
||||
impl Ctx {
|
||||
@@ -278,6 +285,7 @@ impl Ctx {
|
||||
global: unsafe { NonNull::new_unchecked(Box::leak(Box::new(global))) },
|
||||
sources: Vec::new(),
|
||||
store,
|
||||
spans: UnsafeCell::new(Vec::new()),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -366,6 +374,12 @@ impl CodegenContext for Ctx {
|
||||
fn get_store_dir(&self) -> &str {
|
||||
self.store.get_store_dir()
|
||||
}
|
||||
fn register_span(&self, range: rnix::TextRange) -> usize {
|
||||
let spans = unsafe { &mut *self.spans.get() };
|
||||
let id = spans.len();
|
||||
spans.push((self.get_current_source_id(), range));
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeContext for Ctx {
|
||||
@@ -387,6 +401,10 @@ impl RuntimeContext for Ctx {
|
||||
fn get_store(&self) -> &DaemonStore {
|
||||
&self.store
|
||||
}
|
||||
fn get_span(&self, id: usize) -> (usize, rnix::TextRange) {
|
||||
let spans = unsafe { &*self.spans.get() };
|
||||
spans[id]
|
||||
}
|
||||
}
|
||||
|
||||
enum Scope<'ctx> {
|
||||
|
||||
@@ -292,43 +292,32 @@ fn parse_frames(stack: &str, ctx: &impl RuntimeContext) -> Vec<NixStackFrame> {
|
||||
let mut frames = Vec::new();
|
||||
|
||||
for line in stack.lines() {
|
||||
// Format: NIX_STACK_FRAME:source_id:start:end[:extra_data]
|
||||
// Format: NIX_STACK_FRAME:span_id:message
|
||||
let Some(rest) = line.strip_prefix("NIX_STACK_FRAME:") else {
|
||||
continue;
|
||||
};
|
||||
let parts: Vec<&str> = rest.splitn(4, ':').collect();
|
||||
let parts: Vec<&str> = rest.splitn(2, ':').collect();
|
||||
|
||||
if parts.len() < 3 {
|
||||
if parts.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let src = match parts[0].parse() {
|
||||
Ok(id) => ctx.get_source(id),
|
||||
Err(_) => continue,
|
||||
};
|
||||
let start: u32 = match parts[1].parse() {
|
||||
Ok(v) => v,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let end: u32 = match parts[2].parse() {
|
||||
Ok(v) => v,
|
||||
let span_id: usize = match parts[0].parse() {
|
||||
Ok(id) => id,
|
||||
Err(_) => continue,
|
||||
};
|
||||
let (source_id, span) = ctx.get_span(span_id);
|
||||
let src = ctx.get_source(source_id);
|
||||
|
||||
let span = rnix::TextRange::new(rnix::TextSize::from(start), rnix::TextSize::from(end));
|
||||
|
||||
let message = {
|
||||
if parts.len() == 4 {
|
||||
parts[3].to_string()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
let message = if parts.len() == 2 {
|
||||
parts[1].to_string()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
frames.push(NixStackFrame { span, message, src });
|
||||
}
|
||||
|
||||
// Deduplicate consecutive identical frames
|
||||
frames.dedup_by(|a, b| a.span == b.span && a.message == b.message);
|
||||
|
||||
frames
|
||||
|
||||
@@ -26,6 +26,7 @@ pub(crate) trait RuntimeContext: 'static {
|
||||
fn compile_scoped(&mut self, source: Source, scope: Vec<String>) -> Result<String>;
|
||||
fn get_source(&self, id: usize) -> Source;
|
||||
fn get_store(&self) -> &DaemonStore;
|
||||
fn get_span(&self, id: usize) -> (usize, rnix::TextRange);
|
||||
}
|
||||
|
||||
pub(crate) trait OpStateExt<Ctx: RuntimeContext> {
|
||||
|
||||
@@ -21,10 +21,10 @@ use deno_core::url::Url;
|
||||
use fastwebsockets::Frame;
|
||||
use fastwebsockets::OpCode;
|
||||
use fastwebsockets::WebSocket;
|
||||
use hashbrown::HashMap;
|
||||
use hyper::body::Bytes;
|
||||
use hyper_util::rt::TokioIo;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::pin::pin;
|
||||
use std::process;
|
||||
|
||||
@@ -259,25 +259,14 @@ pub(super) fn op_make_placeholder(#[string] output: String) -> String {
|
||||
#[serde]
|
||||
pub(super) fn op_decode_span<Ctx: RuntimeContext>(
|
||||
state: &mut OpState,
|
||||
#[string] span_str: String,
|
||||
#[smi] span_id: u32,
|
||||
) -> Result<serde_json::Value> {
|
||||
let parts: Vec<&str> = span_str.split(':').collect();
|
||||
if parts.len() != 3 {
|
||||
return Ok(serde_json::json!({
|
||||
"file": serde_json::Value::Null,
|
||||
"line": serde_json::Value::Null,
|
||||
"column": serde_json::Value::Null
|
||||
}));
|
||||
}
|
||||
|
||||
let source_id: usize = parts[0].parse().map_err(|_| "Invalid source ID")?;
|
||||
let start: u32 = parts[1].parse().map_err(|_| "Invalid start offset")?;
|
||||
|
||||
let ctx: &Ctx = state.get_ctx();
|
||||
let (source_id, range) = ctx.get_span(span_id as usize);
|
||||
let source = ctx.get_source(source_id);
|
||||
let content = &source.src;
|
||||
let start = u32::from(range.start());
|
||||
|
||||
let (line, column) = byte_offset_to_line_col(content, start as usize);
|
||||
let (line, column) = byte_offset_to_line_col(&source.src, start as usize);
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"file": source.get_name(),
|
||||
|
||||
Reference in New Issue
Block a user