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

source-academy / js-slang / 17161360280

22 Aug 2025 05:04PM UTC coverage: 77.439% (-1.1%) from 78.56%
17161360280

Pull #1813

github

web-flow
Merge 3531197b0 into 20b0a35b6
Pull Request #1813: Remove infinite loop detector

2746 of 3843 branches covered (71.45%)

Branch coverage included in aggregate %.

8193 of 10283 relevant lines covered (79.68%)

162956.53 hits per line

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

86.32
/src/utils/operators.ts
1
import type { BinaryOperator, UnaryOperator } from 'estree'
2

3
import {
72✔
4
  CallingNonFunctionValue,
5
  ExceptionError,
6
  GetInheritedPropertyError,
7
  InvalidNumberOfArguments
8
} from '../errors/errors'
9
import { RuntimeSourceError } from '../errors/runtimeSourceError'
72✔
10
import {
72✔
11
  PotentialInfiniteLoopError,
12
  PotentialInfiniteRecursionError
13
} from '../errors/timeoutErrors'
14
import { Chapter, type NativeStorage } from '../types'
15
import { callExpression, locationDummyNode } from './ast/astCreator'
72✔
16
import * as create from './ast/astCreator'
72✔
17
import { makeWrapper } from './makeWrapper'
72✔
18
import * as rttc from './rttc'
72✔
19

20
export function throwIfTimeout(
72✔
21
  nativeStorage: NativeStorage,
22
  start: number,
23
  current: number,
24
  line: number,
25
  column: number,
26
  source: string | null
27
) {
28
  if (current - start > nativeStorage.maxExecTime) {
17,044,840✔
29
    throw new PotentialInfiniteLoopError(
2✔
30
      create.locationDummyNode(line, column, source),
31
      nativeStorage.maxExecTime
32
    )
33
  }
34
}
35

36
export function callIfFuncAndRightArgs(
72✔
37
  candidate: any,
38
  line: number,
39
  column: number,
40
  source: string | null,
41
  ...args: any[]
42
) {
43
  const dummy = create.callExpression(create.locationDummyNode(line, column, source), args, {
25,188✔
44
    start: { line, column },
45
    end: { line, column }
46
  })
47

48
  if (typeof candidate === 'function') {
25,184!
49
    const originalCandidate = candidate
25,184✔
50
    if (candidate.transformedFunction !== undefined) {
25,184✔
51
      candidate = candidate.transformedFunction
16,086✔
52
    }
53
    const expectedLength = candidate.length
25,184✔
54
    const receivedLength = args.length
25,184✔
55
    const hasVarArgs = candidate.minArgsNeeded !== undefined
25,184✔
56
    if (hasVarArgs ? candidate.minArgsNeeded > receivedLength : expectedLength !== receivedLength) {
25,184✔
57
      throw new InvalidNumberOfArguments(
4✔
58
        dummy,
59
        hasVarArgs ? candidate.minArgsNeeded : expectedLength,
4✔
60
        receivedLength,
61
        hasVarArgs
62
      )
63
    }
64
    try {
25,180✔
65
      return originalCandidate(...args)
25,180✔
66
    } catch (error) {
67
      // if we already handled the error, simply pass it on
68
      if (!(error instanceof RuntimeSourceError || error instanceof ExceptionError)) {
8,893✔
69
        throw new ExceptionError(error, dummy.loc)
204✔
70
      } else {
71
        throw error
8,689✔
72
      }
73
    }
74
  } else {
75
    throw new CallingNonFunctionValue(candidate, dummy)
×
76
  }
77
}
78

79
export function boolOrErr(candidate: any, line: number, column: number, source: string | null) {
72✔
80
  const error = rttc.checkIfStatement(create.locationDummyNode(line, column, source), candidate)
17,117,314✔
81
  if (error === undefined) {
17,117,314!
82
    return candidate
17,117,314✔
83
  } else {
84
    throw error
×
85
  }
86
}
87

88
export function unaryOp(
72✔
89
  operator: UnaryOperator,
90
  argument: any,
91
  line: number,
92
  column: number,
93
  source: string | null
94
) {
95
  const error = rttc.checkUnaryExpression(
10✔
96
    create.locationDummyNode(line, column, source),
97
    operator,
98
    argument
99
  )
100
  if (error === undefined) {
10!
101
    return evaluateUnaryExpression(operator, argument)
10✔
102
  } else {
103
    throw error
×
104
  }
105
}
106

107
export function evaluateUnaryExpression(operator: UnaryOperator, value: any) {
72✔
108
  if (operator === '!') {
49✔
109
    return !value
6✔
110
  } else if (operator === '-') {
43✔
111
    return -value
42✔
112
  } else if (operator === 'typeof') {
1!
113
    return typeof value
1✔
114
  } else {
115
    return +value
×
116
  }
117
}
118

119
export function binaryOp(
72✔
120
  operator: BinaryOperator,
121
  chapter: Chapter,
122
  left: any,
123
  right: any,
124
  line: number,
125
  column: number,
126
  source: string | null
127
) {
128
  const error = rttc.checkBinaryExpression(
23,964,318✔
129
    create.locationDummyNode(line, column, source),
130
    operator,
131
    chapter,
132
    left,
133
    right
134
  )
135
  if (error === undefined) {
23,964,318✔
136
    return evaluateBinaryExpression(operator, left, right)
23,964,312✔
137
  } else {
138
    throw error
6✔
139
  }
140
}
141

142
export function evaluateBinaryExpression(operator: BinaryOperator, left: any, right: any) {
72✔
143
  switch (operator) {
24,813,132!
144
    case '+':
145
      return left + right
23,907,157✔
146
    case '-':
147
      return left - right
441,734✔
148
    case '*':
149
      return left * right
45✔
150
    case '/':
151
      return left / right
1✔
152
    case '%':
153
      return left % right
×
154
    case '===':
155
      return left === right
250,392✔
156
    case '!==':
157
      return left !== right
×
158
    case '<=':
159
      return left <= right
201,228✔
160
    case '<':
161
      return left < right
2,305✔
162
    case '>':
163
      return left > right
10,191✔
164
    case '>=':
165
      return left >= right
79✔
166
    default:
167
      return undefined
×
168
  }
169
}
170

171
/**
172
 * Limitations for current properTailCalls implementation:
173
 * Obviously, if objects ({}) are reintroduced,
174
 * we have to change this for a more stringent check,
175
 * as isTail and transformedFunctions are properties
176
 * and may be added by Source code.
177
 */
178
export const callIteratively = (f: any, nativeStorage: NativeStorage, ...args: any[]) => {
72✔
179
  let line = -1
16,086✔
180
  let column = -1
16,086✔
181
  let source: string | null = null
16,086✔
182
  const startTime = Date.now()
16,086✔
183
  const pastCalls: [string, any[]][] = []
16,086✔
184
  while (true) {
16,086✔
185
    const dummy = locationDummyNode(line, column, source)
13,439,788✔
186
    if (typeof f === 'function') {
13,439,788!
187
      if (f.transformedFunction !== undefined) {
13,439,788✔
188
        f = f.transformedFunction
13,422,098✔
189
      }
190
      const expectedLength = f.length
13,439,788✔
191
      const receivedLength = args.length
13,439,788✔
192
      const hasVarArgs = f.minArgsNeeded !== undefined
13,439,788✔
193
      if (hasVarArgs ? f.minArgsNeeded > receivedLength : expectedLength !== receivedLength) {
13,439,788!
194
        throw new InvalidNumberOfArguments(
×
195
          callExpression(dummy, args, {
196
            start: { line, column },
197
            end: { line, column },
198
            source
199
          }),
200
          hasVarArgs ? f.minArgsNeeded : expectedLength,
×
201
          receivedLength,
202
          hasVarArgs
203
        )
204
      }
205
    } else {
206
      throw new CallingNonFunctionValue(f, dummy)
×
207
    }
208
    let res
209
    try {
13,439,788✔
210
      res = f(...args)
13,439,788✔
211
      if (Date.now() - startTime > nativeStorage.maxExecTime) {
13,430,935✔
212
        throw new PotentialInfiniteRecursionError(dummy, pastCalls, nativeStorage.maxExecTime)
5✔
213
      }
214
    } catch (error) {
215
      // if we already handled the error, simply pass it on
216
      if (!(error instanceof RuntimeSourceError || error instanceof ExceptionError)) {
8,858✔
217
        throw new ExceptionError(error, dummy.loc)
176✔
218
      } else {
219
        throw error
8,682✔
220
      }
221
    }
222
    if (res === null || res === undefined) {
13,430,930✔
223
      return res
28✔
224
    } else if (res.isTail === true) {
13,430,902✔
225
      f = res.function
13,423,702✔
226
      args = res.arguments
13,423,702✔
227
      line = res.line
13,423,702✔
228
      column = res.column
13,423,702✔
229
      source = res.source
13,423,702✔
230
      pastCalls.push([res.functionName, args])
13,423,702✔
231
    } else if (res.isTail === false) {
7,200✔
232
      return res.value
5,606✔
233
    } else {
234
      return res
1,594✔
235
    }
236
  }
237
}
238

239
export const wrap = (
72✔
240
  f: (...args: any[]) => any,
241
  stringified: string,
242
  hasVarArgs: boolean,
243
  nativeStorage: NativeStorage
244
) => {
245
  if (hasVarArgs) {
14,207✔
246
    // @ts-ignore
247
    f.minArgsNeeded = f.length
2✔
248
  }
249
  const wrapped = (...args: any[]) => callIteratively(f, nativeStorage, ...args)
16,086✔
250
  makeWrapper(f, wrapped)
14,207✔
251
  wrapped.transformedFunction = f
14,207✔
252
  wrapped[Symbol.toStringTag] = () => stringified
14,207✔
253
  wrapped.toString = () => stringified
14,207✔
254
  return wrapped
14,207✔
255
}
256

257
export const setProp = (
72✔
258
  obj: any,
259
  prop: any,
260
  value: any,
261
  line: number,
262
  column: number,
263
  source: string | null
264
) => {
265
  const dummy = locationDummyNode(line, column, source)
126✔
266
  const error = rttc.checkMemberAccess(dummy, obj, prop)
126✔
267
  if (error === undefined) {
126!
268
    return (obj[prop] = value)
126✔
269
  } else {
270
    throw error
×
271
  }
272
}
273

274
export const getProp = (
72✔
275
  obj: any,
276
  prop: any,
277
  line: number,
278
  column: number,
279
  source: string | null
280
) => {
281
  const dummy = locationDummyNode(line, column, source)
18✔
282
  const error = rttc.checkMemberAccess(dummy, obj, prop)
18✔
283
  if (error === undefined) {
18!
284
    if (obj[prop] !== undefined && !obj.hasOwnProperty(prop)) {
18!
285
      throw new GetInheritedPropertyError(dummy, obj, prop)
×
286
    } else {
287
      return obj[prop]
18✔
288
    }
289
  } else {
290
    throw error
×
291
  }
292
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc