diff --git a/nix-js/runtime-ts/src/helpers.ts b/nix-js/runtime-ts/src/helpers.ts index 2e47abe..78df675 100644 --- a/nix-js/runtime-ts/src/helpers.ts +++ b/nix-js/runtime-ts/src/helpers.ts @@ -144,17 +144,20 @@ export const resolvePath = (currentDir: string, path: NixValue): NixPath => { export const select = (obj: NixValue, attrpath: NixValue[], span?: string): NixValue => { if (span) { - const pathStrings = attrpath.map((a) => forceStringValue(a)); - const path = pathStrings.join("."); - const message = path ? `while selecting attribute [${path}]` : "while selecting attribute"; - if (callStack.length >= MAX_STACK_DEPTH) { callStack.shift(); } - callStack.push({ span, message }); + const frame: StackFrame = { span, message: "while selecting attribute" }; + callStack.push(frame); try { return selectImpl(obj, attrpath); } catch (error) { + try { + const path = attrpath.map((a) => forceStringValue(a)).join("."); + if (path) frame.message = `while selecting attribute [${path}]`; + } catch { + throw enrichError(error); + } throw enrichError(error); } finally { callStack.pop(); @@ -167,8 +170,8 @@ export const select = (obj: NixValue, attrpath: NixValue[], span?: string): NixV function selectImpl(obj: NixValue, attrpath: NixValue[]): NixValue { let attrs = forceAttrs(obj); - for (const attr of attrpath.slice(0, -1)) { - const key = forceStringValue(attr); + for (let i = 0; i < attrpath.length - 1; i++) { + const key = forceStringValue(attrpath[i]); if (!attrs.has(key)) { throw new Error(`Attribute '${key}' not found`); } @@ -190,17 +193,20 @@ export const selectWithDefault = ( span?: string, ): NixValue => { if (span) { - const pathStrings = attrpath.map((a) => forceStringValue(a)); - const path = pathStrings.join("."); - const message = path ? `while selecting attribute [${path}]` : "while selecting attribute"; - if (callStack.length >= MAX_STACK_DEPTH) { callStack.shift(); } - callStack.push({ span, message }); + const frame: StackFrame = { span, message: "while selecting attribute" }; + callStack.push(frame); try { return selectWithDefaultImpl(obj, attrpath, defaultVal); } catch (error) { + try { + const path = attrpath.map((a) => forceStringValue(a)).join("."); + if (path) frame.message = `while selecting attribute [${path}]`; + } catch { + throw enrichError(error); + } throw enrichError(error); } finally { callStack.pop(); @@ -216,8 +222,8 @@ function selectWithDefaultImpl(obj: NixValue, attrpath: NixValue[], defaultVal: return defaultVal; } - for (const attr of attrpath.slice(0, -1)) { - const key = forceStringValue(attr); + for (let i = 0; i < attrpath.length - 1; i++) { + const key = forceStringValue(attrpath[i]); if (!attrs.has(key)) { return defaultVal; } @@ -242,8 +248,8 @@ export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => { } let attrs = forced; - for (const attr of attrpath.slice(0, -1)) { - const key = forceStringNoCtx(attr); + for (let i = 0; i < attrpath.length - 1; i++) { + const key = forceStringNoCtx(attrpath[i]); if (!attrs.has(key)) { return false; } diff --git a/nix-js/runtime-ts/src/string-context.ts b/nix-js/runtime-ts/src/string-context.ts index f438665..ef6d38a 100644 --- a/nix-js/runtime-ts/src/string-context.ts +++ b/nix-js/runtime-ts/src/string-context.ts @@ -49,11 +49,12 @@ export const getStringValue = (s: string | StringWithContext): string => { return s; }; +const emptyContext: NixStringContext = new Set(); export const getStringContext = (s: string | StringWithContext): NixStringContext => { if (isStringWithContext(s)) { return s.context; } - return new Set(); + return emptyContext; }; export const mergeContexts = (...contexts: NixStringContext[]): NixStringContext => { diff --git a/nix-js/runtime-ts/src/types.ts b/nix-js/runtime-ts/src/types.ts index 8b7a4bc..a333864 100644 --- a/nix-js/runtime-ts/src/types.ts +++ b/nix-js/runtime-ts/src/types.ts @@ -35,10 +35,10 @@ export class NixArgs { allowed: Set; ellipsis: boolean; positions: Map; - constructor(required: string[], optional: string[], positions: Record, ellipsis: boolean) { + constructor(required: string[], optional: string[], positions: Map, ellipsis: boolean) { this.required = required; this.optional = optional; - this.positions = new Map(Object.entries(positions)); + this.positions = positions; this.ellipsis = ellipsis; this.allowed = new Set(required.concat(optional)); } @@ -64,7 +64,7 @@ export const mkFunction = ( f: (arg: NixValue) => NixValue, required: string[], optional: string[], - positions: Record, + positions: Map, ellipsis: boolean, ): NixFunction => { const func: NixFunction = f; @@ -86,12 +86,10 @@ export const mkAttrs = (attrs: NixAttrs, keys: NixValue[], values: NixValue[]): }; export const mkAttrsWithPos = ( - obj: Record, - positions: Record, + attrs: NixAttrs, + positions: Map, dyns?: { dynKeys: NixValue[]; dynVals: NixValue[]; dynSpans: string[] }, ): NixAttrs => { - const attrs: NixAttrs = new Map(Object.entries(obj)); - if (dyns) { const len = dyns.dynKeys.length; for (let i = 0; i < len; i++) { @@ -101,12 +99,12 @@ export const mkAttrsWithPos = ( } const str = forceStringNoCtx(key); attrs.set(str, dyns.dynVals[i]); - positions[str] = dyns.dynSpans[i]; + positions.set(str, dyns.dynSpans[i]); } } - if (Object.keys(positions).length > 0) { - attrs[ATTR_POSITIONS] = new Map(Object.entries(positions)); + if (positions.size > 0) { + attrs[ATTR_POSITIONS] = positions; } return attrs; diff --git a/nix-js/src/codegen.rs b/nix-js/src/codegen.rs index 819ef79..4bc935e 100644 --- a/nix-js/src/codegen.rs +++ b/nix-js/src/codegen.rs @@ -458,11 +458,11 @@ impl Compile for Func { joined(optional.iter(), ",", |ctx: &Ctx, buf, &(sym, _)| { code!(buf, ctx; ctx.get_sym(sym)); }) - "],{" - joined(required.iter().chain(optional.iter()), ",", |ctx: &Ctx, buf, &(sym, span)| { - code!(buf, ctx; ctx.get_sym(sym) ":" span); + "],new Map()" + joined(required.iter().chain(optional.iter()), "", |ctx: &Ctx, buf, &(sym, span)| { + code!(buf, ctx; ".set(" ctx.get_sym(sym) "," span ")"); }) - "}," + "," ellipsis ")" ); @@ -574,21 +574,21 @@ impl Compile for AttrSet { fn compile(&self, ctx: &Ctx, buf: &mut CodeBuffer) { if !self.dyns.is_empty() { code!(buf, ctx; - "Nix.mkAttrsWithPos({" - joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(expr, _))| { + "Nix.mkAttrsWithPos(new Map()" + joined(self.stcs.iter(), "", |ctx: &Ctx, buf, (&sym, &(expr, _))| { let key = ctx.get_sym(sym); let val = ctx.get_ir(expr); code!( buf, ctx; - key ":Nix.withContext(\"while evaluating the attribute '" escaped(&key) "'\"," val.span() ",()=>(" val "))" + ".set(" key ",Nix.withContext(\"while evaluating the attribute '" escaped(&key) "'\"," val.span() ",()=>(" val ")))" ); }) - "},{" - joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(_, span))| { - code!(buf, ctx; ctx.get_sym(sym) ":" span); + ",new Map()" + joined(self.stcs.iter(), "", |ctx: &Ctx, buf, (&sym, &(_, span))| { + code!(buf, ctx; ".set(" ctx.get_sym(sym) "," span ")"); }) - "},{dynKeys:[" + ",{dynKeys:[" joined(self.dyns.iter(), ",", |ctx: &Ctx, buf, (key, _, _)| { code!(buf, ctx; ctx.get_ir(*key)); }) @@ -608,21 +608,21 @@ impl Compile for AttrSet { ); } else if !self.stcs.is_empty() { code!(buf, ctx; - "Nix.mkAttrsWithPos({" - joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(expr, _))| { + "Nix.mkAttrsWithPos(new Map()" + joined(self.stcs.iter(), "", |ctx: &Ctx, buf, (&sym, &(expr, _))| { let key = ctx.get_sym(sym); let val = ctx.get_ir(expr); code!( buf, ctx; - key ":Nix.withContext(\"while evaluating the attribute '" escaped(&key) "'\"," val.span() ",()=>(" val "))" + ".set(" key ",Nix.withContext(\"while evaluating the attribute '" escaped(&key) "'\"," val.span() ",()=>(" val ")))" ); }) - "},{" - joined(self.stcs.iter(), ",", |ctx: &Ctx, buf, (&sym, &(_, span))| { - code!(buf, ctx; ctx.get_sym(sym) ":" span); + ",new Map()" + joined(self.stcs.iter(), "", |ctx: &Ctx, buf, (&sym, &(_, span))| { + code!(buf, ctx; ".set(" ctx.get_sym(sym) "," span ")"); }) - "})" + ")" ); } else { code!(buf, ctx; "new Map()");