1 of 40

js-slang under the hood

1

what happens when you press ▶️

2 of 40

2

/source-academy/<?>

/frontend

/backend

/js-slang

/sling

/modules

/sinter

3 of 40

3

/source-academy/<?>

/frontend

/backend

/js-slang

/sling

/modules

/sinter

db

interface

program str

context

import stmts

SVML

SVML

bundle(s)

result

result

4 of 40

4

/source-academy/<?>

/frontend

/js-slang

/modules

program str

context

import stmts

bundle(s)

result

5 of 40

5

pressing the ▶️ button

/frontend

6 of 40

6

pressing the ▶️ button

/js-slang

runInContext(programString, context, options)

runInContext :: (string, Context, IOptions) -> Result

7 of 40

7

pressing the ▶️ button

/js-slang

runInContext(programString, context, options)

runInContext :: (string, Context, IOptions) -> Result

8 of 40

8

pressing the ▶️ button

/js-slang

export interface Context<T = any> {

chapter: Chapter

executionMethod: ExecutionMethod

variant: Variant

prelude: string | null

nativeStorage: NativeStorage

moduleContexts: {...}

errors: SourceError[]

runtime: {

environmentTree: EnvTree

environments: Environment[]

nodes: es.Node[]

}

}

Context

9 of 40

9

pressing the ▶️ button

/js-slang

Context

runInContext(_, context, _)

already contains builtins (e.g. pair, list)

stored in

context.nativeStorage.builtins :: Map<String, any>

10 of 40

10

pressing the ▶️ button

/js-slang

Context

function createContext(chapter, variant) {

const context = createEmptyContext(chapter, variant)

// context

// .nativeStorage

// .builtins

// .insert(<name>, <value>)

importBuiltins(context)

}

11 of 40

11

pressing the ▶️ button

/js-slang

runInContext(programString, context, options)

runInContext :: (string, Context, IOptions) -> Result

12 of 40

12

pressing the ▶️ button

/js-slang

Options

13 of 40

13

pressing the ▶️ button

/js-slang

Options

14 of 40

14

pressing the ▶️ button

/js-slang

chapter

executionMethod

variant

useSubst

determines the builtins

native / debug mode

how should it be executed

should i run stepper

15 of 40

15

pressing the ▶️ button

/js-slang

chapter

executionMethod

variant

useSubst

determines the builtins

native / debug mode

how should it be executed

should i run stepper

prelude

what magic 🪄should be added

16 of 40

16

program string

✅ the “recipe” to run it

✅ builtins (sort of)

✅ prelude (sort of)

17 of 40

17

program string

✅ the “recipe” to run it

✅ builtins (sort of)

✅ prelude (sort of)

🤔 REPL support

18 of 40

18

after pressing the ▶️ button

program evaluation

{

// e.g. const pair = (x, y) => [x,y];

...builtins

// e.g. import {show, heart} from ‘rune’;

...prelude

// e.g. show(heart);

...program string

}

🤔

19 of 40

19

after pressing the ▶️ button

program evaluation

{

const pair = (x, y) => [x,y];

import {show, heart} from ‘rune’;

show(heart);

}

🤔

curr_program_str

20 of 40

20

after pressing the ▶️ button

program evaluation

function boo(...args1) {

const a = 1;

function hoo(...args2) {

return a;

}

return hoo

}

21 of 40

21

after pressing the ▶️ button

program evaluation

evaller = (program) => eval(program)

evaller :: (String) -> any

context.nativeStorage.evaller = typeof evaller | null

22 of 40

22

after pressing the ▶️ button

program evaluation

{

// e.g. const pair = (x, y) => [x,y];

...builtins

// e.g. import {show, heart} from ‘rune’;

...prelude

nativeStorage.evaller = (program) => eval(program)

// e.g. show(heart);

...program string

}

23 of 40

23

after pressing the ▶️ button

program evaluation

24 of 40

24

after pressing the ▶️ button

program evaluation

{

const pair = (x, y) => [x,y];

import {show, heart} from ‘rune’;

nativeStorage.evaller = (program) => eval(program)

show(heart);

}

curr_program_str

25 of 40

25

after pressing the ▶️ button

program evaluation

26 of 40

26

after pressing the ▶️ button

program evaluation

function sandBoxedEval(code: string, {nativeStorage, ...ctx}){

if (nativeStorage.evaller) {

return nativeStorage.evaller(code)

}

return eval(code)

}

27 of 40

27

after pressing the ▶️ button

program evaluation

function sandBoxedEval(code: string, {nativeStorage, ...ctx}){

return eval(

{

const pair = (x, y) => [x,y];

import {show, heart} from ‘rune’;

nativeStorage.evaller = (program) => eval(program)

show(heart);

}

)

}

28 of 40

28

after pressing the ▶️ button

program evaluation

function sandBoxedEval(code: string, {nativeStorage, ...ctx}){

if (nativeStorage.evaller) {

return nativeStorage.evaller(code)

}

return eval(code)

}

29 of 40

29

after pressing the ▶️ button

program evaluation

function sandBoxedEval(code: string, {nativeStorage, ...ctx}){

// nativeStorage.evaller = (program) => eval(program)

return nativeStorage.evaller(code)

}

30 of 40

30

after pressing the ▶️ button

program evaluation

function sandBoxedEval(code: string, {nativeStorage, ...ctx}){

// Note which scope the evaller belongs to!!

return ((program) => eval(program))(code)

}

31 of 40

31

after pressing the ▶️ button

program evaluation

function sandBoxedEval(code: string, {nativeStorage, ...ctx}){

return eval(

{

const pair = (x, y) => [x,y];

import {show, heart} from ‘rune’;

nativeStorage.evaller = (program) => eval(program)

show(heart);

}

)

}

32 of 40

32

after pressing the ▶️ button

program evaluation

function sandBoxedEval(code: string, {nativeStorage, ...ctx}){

return ((program) => eval(program))(

const a = pair(heart, heart);

const b = pair(show(heart), show(heart));

)

}

33 of 40

33

after pressing the ▶️ button

program evaluation

function sandBoxedEval(code: string, {nativeStorage, ...ctx}){

return ((program) => eval(program))(

const a = pair(heart, heart);

const b = pair(show(heart), show(heart));

nativeStorage.evaller = (program) => eval(program)

)

}

34 of 40

34

program string

✅ the “recipe” to run it

✅ builtins (sort of)

✅ prelude (sort of)

✅ REPL support

35 of 40

35

{

...builtins

{

nativeStorage.evaller = (program) => eval(program)

...prelude

}

}

after pressing the ▶️ button

evaluation stage 1

36 of 40

36

{

...builtins

{

nativeStorage.evaller = (program) => eval(program)

...program_str

}

}

after pressing the ▶️ button

evaluation stage 2

37 of 40

37

const __MODULE_N__ = (...)(context) // IIFE

{

...builtins

{

nativeStorage.evaller = (program) => eval(program)

const heart = __MODULE_N__.heart;

...program_str

}

}

after pressing the ▶️ button

evaluation with imports

38 of 40

38

const __MODULE_N__ = (...)(context) // IIFE

{

...builtins

{

nativeStorage.evaller = (program) => eval(program)

const heart = __MODULE_N__.heart;

pair(heart, heart);

}

}

after pressing the ▶️ button

evaluation with imports

39 of 40

39

improvements

  • better separation and handling of debug and native modes
  • code structure
    • context and options
    • builtins and prelude
    • can we make it more declarative?
  • slang executable
  • better introspection for devs
  • dev documentation

40 of 40

40

improvements