File: HACKING-js

package info (click to toggle)
nqp 2020.12%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 8,880 kB
  • sloc: java: 26,979; perl: 3,386; ansic: 450; makefile: 203; javascript: 68; sh: 1
file content (69 lines) | stat: -rw-r--r-- 2,417 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# Hacking tips for the JS backend of NQP

# Source layout.

src/vm/js/Compiler.nqp - turns QAST into JavaScript code
src/vm/js/nqp-runtime - a node module the code we generate uses
src/vm/js/bin/run_tests.pl - a list of tests we use

# Overview

The bulk of the compilation is done by QAST::CompilerJS.as_js($node, :$want).
It takes a QAST::Node and produces a Chunk.

A chunk is created by Chunk.new($type, $expr, @setup, :$node).

$expr represents a sideffect free JavaScript expression.
$type describes what the expression evaluates to.

$type can be one of $T_OBJ, $T_INT, $T_STR, $T_BOOL, $T_VOID, $T_NONVAL.

@setup is the JavaScript code that needs to be run for $expr to be valid.
@setup is an array containing either string literals with JavaScript code or other Chunks.

$node is a QAST::Node we get line positions from when emitting source maps.

Simple chunk examples:

    Chunk.new($T_STR, "'Hello World'", []);

    my $foo := Chunk.new($T_STR, "foo", ["var foo = 'Hello World';\n"]);

    Chunk.new($T_VOID, "", [$foo, "alert({$foo.expr});\n"]); # We don't ever use the $expr of $T_VOID chunks

When compiling a QAST::Block we need to keep a bunch of information about it.
We store it in a dynamic $*BLOCK variable (which contains a BlockInfo).

     my $tmp := $*BLOCK.add_tmp(); # that creates a temporary variable in js function we are emitting.
     my $foo := Chunk.new($T_STR, $tmp, ["$tmp = 'Hello World';\n"]);


# NQP op codes


QAST::OperationsJS.compile_op(QAST::CompilerJS $compiler, QAST::Node $node, :$want) handles the compilation of QAST::Op nodes

The more complex ops are defined using add_op.

    add_op($op, sub ($comp, $node, :$want) {
        # this should return a chunk
    });

The simpler ones are defined using add_simple_op($op, $return_type, @argument_types, $cb, :$sideffects).
$cb gets a bunch of .expr parts from the Chunks the op arguments are compiled to.
$cb should return a string containing a JavaScript expression.
If the op has sideffects the :$sideffects flag needs to be true.

Example:

    add_simple_op('lc', $T_STR, [$T_STR], sub ($string) {"$string.toLowerCase()"});

Looking at src/vm/jvm/runtime/org/raku/nqp/runtime/Ops.java and src/vm/jvm/QAST/Compiler.nqp can provide insight on how the various ops should work.

# Reading generated code

./nqp-js --target=js --beautify -e 'say("Hello World")'

# t/js

JS backend specific tests should placed here