• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

source-academy / js-slang / 24868044425

24 Apr 2026 01:47AM UTC coverage: 78.522% (+0.1%) from 78.391%
24868044425

push

github

web-flow
Error Handling and Stringify Changes (#1893)

* Modify stringify to prioritize toReplString

* Make the extract declarations helper actually work

* Add ability to change loader for source modules

* Add a new option for controlling how Source modules are loaded

* Improve typing for CSE machine

* Add ability to check if modules are loaded with the wrong Source chapter

* Refactor errors to extend from Error class

* Refactor modules errors

* Refactor parser errors

* Refactor cse machine errors

* Mostly fix error handling in the tracer

* Tidy up generator and explainer implementations for tracer

* Remove unnecessary imports and type guards

* Adjust rttc checks to be type guards instead of returning errors

* Adjust miscellanous error changes

* Add eslint rule for useless constructor

* Fix incorrect ordering for checking exceptionerrors

* Minor changes

* Run format

* Run linting

* Override the message property, but also enable noImplicitOverride to prevent accidental overriding

* Add some documentation to some errors

* Add errors to possible imports for modules

* Update getIds helper

* Minor fix to test case

* Run format

* Allow modules to try and load the context of other modules without crashing

* Fix incorrect stdlib name

* Add some more functions to the stdlib list library

* Change to use GeneralRuntimeError for list

* Revert "Change to use GeneralRuntimeError for list"

This reverts commit 642bd99e6.

* Update src/errors/errors.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Add ability to change manifest and docs importers

* Change how external builtins are defined

* Fix typings and list lib overloads

* Miscellanous changes

* Add the Source equality function to stdlib/misc

* Merge from main branch for tracer

* Add handling for the new importers

* Change errors and made redex a local variable

* Improve tracer typing

* Relocate... (continued)

3125 of 4193 branches covered (74.53%)

Branch coverage included in aggregate %.

899 of 1089 new or added lines in 96 files covered. (82.55%)

21 existing lines in 12 files now uncovered.

7031 of 8741 relevant lines covered (80.44%)

185057.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.74
/src/stepper/nodes/Expression/ArrowFunctionExpression.ts
1
import type { ArrowFunctionExpression, Comment, Expression, SourceLocation } from 'estree';
2
import type { StepperExpression, StepperPattern } from '..';
3
import { convert } from '../../generator';
4
import { StepperBaseNode } from '../../interface';
5
import { getFreshName } from '../../utils';
6
import type { StepperBlockStatement } from '../Statement/BlockStatement';
7
import { InternalRuntimeError } from '../../../errors/base';
8
import type { RedexInfo } from '../..';
9
import { mapAndFilter } from '../../../utils/misc';
10
import { isIdentifier } from '../../../utils/ast/typeGuards';
11

12
export class StepperArrowFunctionExpression
13
  extends StepperBaseNode<ArrowFunctionExpression>
14
  implements ArrowFunctionExpression
15
{
16
  constructor(
17
    public readonly params: StepperPattern[],
1,577✔
18
    public readonly body: StepperExpression,
1,577✔
19
    public readonly name?: string,
1,577✔
20
    public readonly expression: boolean = true,
1,577✔
21
    public readonly generator: boolean = false,
1,577✔
22
    public readonly async: boolean = false,
1,577✔
23
    leadingComments?: Comment[],
24
    trailingComments?: Comment[],
25
    loc?: SourceLocation | null,
26
    range?: [number, number],
27
  ) {
28
    super('ArrowFunctionExpression', leadingComments, trailingComments, loc, range);
1,577✔
29
  }
30

31
  static create(node: ArrowFunctionExpression) {
32
    return new StepperArrowFunctionExpression(
302✔
33
      node.params.map(param => convert(param)),
406✔
34
      convert(node.body as Expression),
35
      undefined,
36
      node.expression,
37
      node.generator,
38
      node.async,
39
      node.leadingComments,
40
      node.trailingComments,
41
      node.loc,
42
      node.range,
43
    );
44
  }
45

46
  public override isContractible(): boolean {
47
    return false;
×
48
  }
49

50
  public override isOneStepPossible(): boolean {
51
    return false;
3,917✔
52
  }
53

54
  public override contract(): StepperExpression {
NEW
55
    throw new InternalRuntimeError('Cannot contract an arrow function expression', this);
×
56
  }
57

58
  public override oneStep(): StepperExpression {
NEW
59
    throw new InternalRuntimeError('Cannot step an arrow function expression', this);
×
60
  }
61

62
  assignName(name: string): StepperArrowFunctionExpression {
63
    return new StepperArrowFunctionExpression(
41✔
64
      this.params,
65
      this.body,
66
      name,
67
      this.expression,
68
      this.generator,
69
      this.async,
70
      this.leadingComments,
71
      this.trailingComments,
72
      this.loc,
73
      this.range,
74
    );
75
  }
76

77
  scanAllDeclarationNames(): string[] {
78
    const paramNames = this.params.map(param => param.name);
3,447✔
79

80
    let bodyDeclarations: string[] = [];
3,447✔
81
    // @ts-expect-error gracefully handle block statement as block expression
82
    if (this.body.type === 'BlockStatement') {
3,447✔
83
      const body = this.body as StepperBlockStatement;
1,545✔
84
      bodyDeclarations = body.body
1,545✔
85
        .filter(stmt => stmt.type === 'VariableDeclaration')
1,814✔
86
        .flatMap(decl => (decl as any).declarations.map((d: any) => d.id.name));
108✔
87
    }
88

89
    return [...paramNames, ...bodyDeclarations];
3,447✔
90
  }
91

92
  // TODO: Fix name handling for lambda
93
  public override substitute(
94
    id: StepperPattern,
95
    value: StepperExpression,
96
    redex: RedexInfo,
97
    upperBoundName?: string[],
98
  ): StepperExpression {
99
    const valueFreeNames = value.freeNames();
1,350✔
100
    const scopeNames = this.scanAllDeclarationNames();
1,350✔
101
    const repeatedNames = valueFreeNames.filter(name => scopeNames.includes(name));
1,350✔
102

103
    let protectedNamesSet = new Set([this.allNames(), upperBoundName ?? []].flat());
1,350✔
104
    repeatedNames.forEach(name => protectedNamesSet.delete(name));
1,350✔
105
    const protectedNames = Array.from(protectedNamesSet);
1,350✔
106
    const newNames = getFreshName(repeatedNames, protectedNames);
1,350✔
107
    const currentArrowFunction = newNames.reduce(
1,350✔
108
      (current: StepperArrowFunctionExpression, name: string, index: number) =>
109
        current.rename(repeatedNames[index], name) as StepperArrowFunctionExpression,
26✔
110
      this,
111
    );
112
    if (currentArrowFunction.scanAllDeclarationNames().includes(id.name)) {
1,350✔
113
      return currentArrowFunction;
233✔
114
    }
115

116
    return new StepperArrowFunctionExpression(
1,117✔
117
      currentArrowFunction.params,
118
      currentArrowFunction.body.substitute(
119
        id,
120
        value,
121
        redex,
122
        currentArrowFunction.params.flatMap(p => p.allNames()),
995✔
123
      ),
124
      currentArrowFunction.name,
125
      currentArrowFunction.expression,
126
      currentArrowFunction.generator,
127
      currentArrowFunction.async,
128
      currentArrowFunction.leadingComments,
129
      currentArrowFunction.trailingComments,
130
      currentArrowFunction.loc,
131
      currentArrowFunction.range,
132
    );
133
  }
134

135
  public override freeNames(): string[] {
136
    const paramNames = mapAndFilter(this.params, param =>
2,207!
137
      isIdentifier(param) ? param.name : undefined,
138
    );
139
    return this.body.freeNames().filter(name => !paramNames.includes(name));
2,714✔
140
  }
141

142
  public override allNames(): string[] {
143
    const paramNames = mapAndFilter(this.params, param =>
2,480!
144
      isIdentifier(param) ? param.name : undefined,
145
    );
146
    return Array.from(new Set([paramNames, this.body.allNames()].flat()));
2,439✔
147
  }
148

149
  public override rename(before: string, after: string): StepperExpression {
150
    return new StepperArrowFunctionExpression(
34✔
151
      this.params.map(param => param.rename(before, after)),
40✔
152
      this.body.rename(before, after),
153
      this.name,
154
      this.expression,
155
      this.generator,
156
      this.async,
157
      this.leadingComments,
158
      this.trailingComments,
159
      this.loc,
160
      this.range,
161
    );
162
  }
163
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc