Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
e387b8b
Add tests for getVariantName
leeyi45 May 29, 2026
de096fa
Fix callIfFuncAndRightArgs not being exported
leeyi45 Jun 12, 2026
a434597
Remove the unknown overload from callWithoutMetadata
leeyi45 Jun 12, 2026
1464dee
Add type guard checking to isTupleOfLength
leeyi45 Jun 13, 2026
455a2c5
Fix TypeOfConstantsToType type
leeyi45 Jun 13, 2026
50d9de8
Fix format
leeyi45 Jun 15, 2026
724d266
Move ChapterStrings type to langs file
leeyi45 Jun 15, 2026
c172644
Add some missing tests
leeyi45 Jun 15, 2026
0f4f207
Merge branch 'master' into operators-fix
leeyi45 Jun 16, 2026
fdf73ac
Make sure that library functions also use callWithoutMetadata
leeyi45 Jun 18, 2026
677221c
Fix potential transpiler case where an object's methods might not get…
leeyi45 Jun 21, 2026
9e381c0
Fix incorrect error message when using InvalidNumberParameterError
leeyi45 Jun 22, 2026
9310179
Change toThrowError, Fix incorrect array access, Use unicode for numb…
leeyi45 Jun 22, 2026
91a1098
Revert change to memberexpressions
leeyi45 Jun 22, 2026
bf8fdcf
Merge branch 'master' into operators-fix
leeyi45 Jun 25, 2026
e622123
Add @internal specifier for ReplResult
leeyi45 Jun 26, 2026
1026dca
Merge remote-tracking branch 'refs/remotes/origin/operators-fix' into…
leeyi45 Jun 26, 2026
3758d6c
Test max args
leeyi45 Jun 27, 2026
88cd315
Make wrap work
leeyi45 Jun 27, 2026
65de4f7
General cleanup
leeyi45 Jun 28, 2026
2b7b55a
Remove fs
leeyi45 Jun 28, 2026
01712ca
Add wrapUnsafe
leeyi45 Jun 28, 2026
90576f0
Add tests for handling default arguments
leeyi45 Jun 28, 2026
23e573c
Run format
leeyi45 Jun 28, 2026
497acd1
Use original definition of FunctionOfLength
leeyi45 Jun 28, 2026
25eba1c
Add docstring to wrap
leeyi45 Jun 28, 2026
a87b2d9
Allow js-slang to handle rest and optional parameters
leeyi45 Jun 29, 2026
d3588dd
Export parameter specifier
leeyi45 Jun 29, 2026
e0d781c
Add tests for docsToHtml
leeyi45 Jun 29, 2026
89f8db2
Update docstrings
leeyi45 Jun 29, 2026
e58df40
Add utility for validating function arguments
leeyi45 Jun 29, 2026
9538208
Fix stepper and cse-machine potentially not treating function arity c…
leeyi45 Jun 29, 2026
26e9ddf
Fix stepper call to builtins not checking arity correctly
leeyi45 Jun 29, 2026
35b2935
Run format
leeyi45 Jun 29, 2026
0d438f8
Add function names to errors
leeyi45 Jun 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/modules/loader/requireProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { Context, Node } from '../../types';
import * as types from '../../types';
import * as assert from '../../utils/assert';
import * as stringify from '../../utils/stringify';
import * as operators from '../../utils/operators';
import * as errorBase from '../../errors/base';
import * as rttcErrors from '../../errors/rttcErrors';
import * as rttc from '../../utils/rttc';
Expand Down Expand Up @@ -39,8 +40,9 @@ export function getRequireProvider(context: Context) {
types,
utils: {
assert,
stringify,
operators,
rttc,
stringify,
},
},
context,
Expand Down
1 change: 1 addition & 0 deletions src/name-extractor/__tests__/modules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ describe('test name extractor functionality on imports', () => {
const mockImportDecl: ImportDeclaration = {
type: 'ImportDeclaration',
specifiers: [],
attributes: [],
source: {
type: 'Literal',
value: 'nothing',
Expand Down
14 changes: 12 additions & 2 deletions src/utils/__tests__/misc.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, test } from 'vitest';
import { Chapter } from '../../langs';
import { getChapterName, PromiseTimeoutError, timeoutPromise } from '../misc';
import { Chapter, Variant } from '../../langs';
import { getChapterName, getVariantName, PromiseTimeoutError, timeoutPromise } from '../misc';

describe(timeoutPromise, () => {
const timedResolvedPromise = (duration: number) =>
Expand Down Expand Up @@ -38,3 +38,13 @@ describe(getChapterName, () =>
['FULL_JS', Chapter.FULL_JS],
])('%s', (expected, input) => expect(getChapterName(input)).toEqual(expected)),
);

describe(getVariantName, () => {
test.each([
['DEFAULT', Variant.DEFAULT],
['TYPED', Variant.TYPED],
['NATIVE', Variant.NATIVE],
['WASM', Variant.WASM],
['EXPLICIT_CONTROL', Variant.EXPLICIT_CONTROL],
])('%s', (expected, input) => expect(getVariantName(input)).toEqual(expected));
});
31 changes: 31 additions & 0 deletions src/utils/__tests__/rttc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ describe('rttc Type Guards', () => {
expect(rttc.typeOf(obj)).toEqual('object');
});
});

test('TypeofConstantsToType', () => {
expectTypeOf<rttc.TypeOfConstantToType<'array'>>().toEqualTypeOf<unknown[]>();
expectTypeOf<rttc.TypeOfConstantToType<'bigint'>>().toEqualTypeOf<bigint>();
expectTypeOf<rttc.TypeOfConstantToType<'boolean'>>().toEqualTypeOf<boolean>();
expectTypeOf<rttc.TypeOfConstantToType<'function'>>().toEqualTypeOf<(...args: any[]) => any>();
expectTypeOf<rttc.TypeOfConstantToType<'regexp'>>().toEqualTypeOf<RegExp>();
expectTypeOf<rttc.TypeOfConstantToType<'null'>>().toEqualTypeOf<null>();
expectTypeOf<rttc.TypeOfConstantToType<'number'>>().toEqualTypeOf<number>();
expectTypeOf<rttc.TypeOfConstantToType<'object'>>().toEqualTypeOf<object>();
expectTypeOf<rttc.TypeOfConstantToType<'string'>>().toEqualTypeOf<string>();
expectTypeOf<rttc.TypeOfConstantToType<'undefined'>>().toEqualTypeOf<undefined>();
})
});

interface Fixtures {
Expand Down Expand Up @@ -422,6 +435,24 @@ describe(rttc.isTupleOfLength, () => {
const tup: unknown = [0, 0];
expect(rttc.isTupleOfLength(tup, 1)).toEqual(false);
});

test('with predicate type guard', () => {
const tup: unknown = [0, 0];
if (rttc.isTupleOfLength(tup, 2, x => typeof x === 'number')) {
expectTypeOf(tup).toEqualTypeOf<[number, number]>();
} else {
expect.fail();
}
});

test('with string type guard', () => {
const tup: unknown = [0, 0];
if (rttc.isTupleOfLength(tup, 2, 'number')) {
expectTypeOf(tup).toEqualTypeOf<[number, number]>();
} else {
expect.fail();
}
});
});

describe(rttc.isNumberWithinRange, () => {
Expand Down
5 changes: 4 additions & 1 deletion src/utils/misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ export function objectKeys<T extends string | number | symbol>(obj: Record<T, an
return Object.keys(obj) as T[];
}

type EnumKeys<T> = T extends Record<infer K, any> ? K : never;
type ChapterStrings = EnumKeys<typeof Chapter>;
Comment thread
leeyi45 marked this conversation as resolved.
Outdated

/**
* Given the chapter value, return the string name of that chapter
*/
export function getChapterName(chapter: Chapter) {
return objectKeys(Chapter).find(name => Chapter[name] === chapter)!;
return Chapter[chapter] as ChapterStrings;
}

/**
Expand Down
11 changes: 11 additions & 0 deletions src/utils/operators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,17 @@ export function callIfFuncAndRightArgs(
}
}

/**
* Convenience wrapper for {@link callIfFuncAndRightArgs} that doesn't require any
* extra metadata to be passed into the function.
*/
export function callWithoutMetadata<T extends (...args: any[]) => any>(
f: T,
...args: Parameters<T>
): ReturnType<T> {
return callIfFuncAndRightArgs(f, -1, -1, null, undefined, ...args);
}

/**
* Augment the given function with the necessary information for it to be called
* properly by {@link callIfFuncAndRightArgs}. It won't redefine any existing details
Expand Down
54 changes: 48 additions & 6 deletions src/utils/rttc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,21 +37,43 @@ export class RuntimeTypeError extends RuntimeSourceError<Node> {

type TypeOfConstants =
| 'array'
| 'boolean'
| 'bigint'
| 'boolean'
| 'function'
| 'number'
| 'null'
| 'number'
| 'object'
| 'regexp'
| 'string'
| 'undefined';

export type TypeOfConstantToType<T extends TypeOfConstants> = T extends 'array'
? unknown[]
: T extends 'bigint'
? bigint
: T extends 'boolean'
? boolean
: T extends 'function'
? (...args: any[]) => any
: T extends 'null'
? null
: T extends 'number'
? number
: T extends 'object'
? object
: T extends 'regexp'
? RegExp
: T extends 'string'
? string
: T extends 'undefined'
? undefined
: never;

/**
* A wrapper around the typeof operator to account for `null` and arrays.
*/
export function typeOf(v: boolean): 'boolean';
export function typeOf(v: bigint): 'bigint';
export function typeOf(v: boolean): 'boolean';
export function typeOf(v: number): 'number';
export function typeOf(v: RegExp): 'regexp';
export function typeOf(v: string): 'string';
Expand Down Expand Up @@ -344,13 +366,33 @@ export function assertFunctionOfLength(
}

/**
* Function for checking if the given `obj` is a tuple of the given length.
* Function for checking if the given `obj` is a tuple of the given length. Optionally, providing a type guard
* function or string can type check the entire tuple.
*/
export function isTupleOfLength<T extends number, U>(obj: U[], l: T): obj is TupleOfLength<T, U>;
export function isTupleOfLength<T extends number, U>(
obj: unknown,
l: T,
guard: (arg: unknown) => arg is U,
): obj is TupleOfLength<T, U>;
export function isTupleOfLength<T extends number, U extends TypeOfConstants>(
obj: unknown,
l: T,
guard: U,
): obj is TupleOfLength<T, TypeOfConstantToType<U>>;
export function isTupleOfLength<T extends number>(obj: unknown, l: T): obj is TupleOfLength<T>;
export function isTupleOfLength<T extends number>(obj: unknown, l: T): obj is TupleOfLength<T> {
export function isTupleOfLength<T extends number>(
obj: unknown,
l: T,
guard?: ((arg: unknown) => boolean) | TypeOfConstants,
): boolean {
if (!Array.isArray(obj)) return false;
return obj.length === l;
if (obj.length !== l) return false;

if (typeof guard === 'string') return obj.every(each => typeOf(each) === guard);
else if (guard !== undefined) return obj.every(guard);

return true;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/utils/stringify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { MAX_LIST_DISPLAY_LENGTH } from '../constants';
import Closure from '../cse-machine/closure';
import { InternalRuntimeError } from '../errors/base';
import type { Type, Value } from '../types';
import { callIfFuncAndRightArgs } from './operators';
import { callWithoutMetadata } from './operators';

export interface ArrayLike {
replPrefix: string;
Expand Down Expand Up @@ -437,7 +437,7 @@ export function valueToStringDag(value: Value): StringDag {
// callIfFuncAndRight args is necessary because if we implement toReplString as a function
// in Source, it gets wrapped by the transpiler
// this allows object literals to implement toReplString
const reprValue = callIfFuncAndRightArgs(v.toReplString.bind(v), -1, -1, null, undefined);
const reprValue = callWithoutMetadata(v.toReplString.bind(v));
return convertRepr(reprValue);
} else if (typeof v !== 'object') {
return convertRepr(v.toString());
Expand Down
1 change: 1 addition & 0 deletions src/utils/testing/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function mockImportDeclaration(): es.ImportDeclaration {
},
},
],
attributes: [],
source: {
type: 'Literal',
value: 'mock-path',
Expand Down
Loading