Skip to content

Kernel behaviour (known issues & workarounds)

Don Jayamanne edited this page Aug 23, 2021 · 3 revisions

Workarounds

  • All top level variables are converted to var
    • const & let variables are converted to var, hence read-write
  • Use of top-level awaits in the cells will result in the code being altered
    • Note, this isn't as bad as it sounds, this is precisely what node.js runtime does and this extension utilizes the exact same code
    • With the exception of maintaining changes to the source maps (to get better error reporting & debugging)
  • Value of the last promise expression displayed by awaiting on the promise
    • Unlike the traditional node.js console window, promises are displayed in the console window.
    • This extension will await on the promise and display the result instead.
  • There's no concept of interrupts (due to a limitation in node.js runtime), instead the node.js process is killed.

Worthy mentions

  • Imports are converted into commonjs require statements
  • Latest version of typeScript is bundled with the extension
  • ts-node is bundled with the extension
  • TypeScript compiler settings are harcoded
  • TypeScript compiler warnings/errors are not displayed

Runtime

  • All code is executed in a separate node process. Each notebook owns its own node process.
  • The node process is spawned with debugging enabled by default (the assumption is there isn't any perf degradation in enabling this).
  • Simple shell commands (such as pwd, ls, cp, etc) are executed in node, and more complex ones are executed in a real shell using node-pty.
  • As we own the runtime execution, we can inject and intercept all modules. Thus when users import modules, we have the ability to alter some imports or inject some code (this is enables the rich integration with danfo.js, plotly and tensorflow.js).

All top level variables are converted to var

let x = 'Hello;
const y = 'World';
var z = '1234';

All top level variables are converted into global variables. Thus variables x, y, z will be available in subsequent cells.

const & let variables are converted to var, hence read-write

As top level const is converted into a var, these variables are now read-write. The reason being one might want to run the following code multiple times (i.e. run the same cell over & over)

const y = 'World';

If it were a const, node runtime would throw an error on subsequent executions. As the notebook is deemed as a means of iterative development, all constants are converted into var during run time.

Support for top level awaits

Running code such as const result = await Promise.resolve(1324) is not possible without some special handling. For instance Nodejs too doesn't support this out of the box (at least not in all versions of node.js). In order to support top level awaits, the code is updated prior to execution. Nodejs runtime also makes simiar (almost identical) changes to support top level awaits in the Repl, see here for more info.

Value of the last expression displayed

Just like a repl, the value of the last expression will be displayed in the output. E.g. in each of the following cases, the value of the last expression is displayed, just like a regular REPL:

var a = 111;
var b = 222;
c = a + b

or

var a = 111;
var b = 222;
var c = a + b
c

Value of the last promise expression displayed

If the last expression evaluates to a promise, then the promise is awaited, and the result displayed in the cell.

const result = Promise.resolve(1234);
result

In the above case, the promise result will be awaited upon and the value displayed in the output. Unlike the node.js repl, the value promise is not displayed, instead the kernel will await on the promise and display the result.

Debugging & Source Maps

The code is updated prior to execution for two reasons:

  1. Support top level awaits As mentioned earlier, code needs to be altered prior to execution (just as node.js runtime also modifies the code under the hood)..
  2. Support breaking in exceptions (exception breakpoints) For some reason breaking on exceptions requires user code (cells) to be wrapped within a try..catch & re-throwing of the errors. Without this, the break on exception doesn't work in the debugger.

As code is updated for the above reasons, the source maps need to be re-adjusted. Though the source maps work, sometimes breakpoints might move around after the debugger starts, this is a known issue and will hopefully be addressed in a future version.

Unable to interrupt and retain kernel state

Once a cell has commenced execution, its not possible to interrupt the code and still maintain the state in the kernel. E.g. if you have a CPU bound code as follows:

var a = 1234;
var b = 999;
for (var i = 0; i < 1000000; i++){
    // Perform some CPU bound operation...
    console.log(i);
}

Hitting the stop button will not stop execution. The only way completely stop execution is to re-start the kernel using the toolbar icon. At this stage the Stop button on the cell does nothing, perhaps in the future it can be updated to just restart the kernel with a message. This is due to a limitation in the node runtime.

Imports converted into commonjs require statements

If a cell contains an unused import, the typescript compiler will not generate code for these imports. Assume we have a cells as follows:

import * as fs from 'fs';

The typescript compiler is used to compile each cell individually, hence even if fs variable is used subsequently, when using the typescript compiler, the import statement is not generated (unfortunately there's no way to preserve this in tsc). The solution is for the extension to identify all imports and generate the commonjs compliant require statement and inject them at the top of the code that will be executed.

TypeScript

The goal is to ship the latest version of TyepScript with every release of this extension.

ts-node

ts-node is shipped with the extension and enabled, so as to enable importing typescript modules without the user having to compile typescript into javascript.

TypeScript compiler settings

At this stage all TypeScript compiler settings are hardcoded. The plan is to support user defined tsconfig files.

No Compiler warnings

The compiler in the extension will not display any warnings or errors encountered during the compilation. I.e if there's invalid code in the cell, these errors will not prevent the cell from being executed. The expectation is the user will see rely on standard features of VS Code to look for errors in their JavaScript/TypeScript.