-
Notifications
You must be signed in to change notification settings - Fork 2
/
bytecode-table.js
169 lines (155 loc) · 5.88 KB
/
bytecode-table.js
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// # bytecode_table.js
//
// Part of the bytecode compiler for parsed Simplified JavaScript, written in
// Simplified JavaScript.
//
// This module just defines the bytecode operations.
// C. Scott Ananian
// 2011-05-10
define(['text!bytecode-table.js'],function make_bytecode_table(bytecode_table_source) {
// Table of bytecodes
var bytecodes_by_num = [];
var bytecodes_by_name = {};
var bc = function(name, args, stackpop, stackpush, printargs) {
var nbc = {
id: bytecodes_by_num.length,
name: name,
args: args,
stackpop: stackpop,
stackpush: stackpush,
printargs: printargs
};
if (typeof(nbc.stackpop) !== "function") {
nbc.stackpop = function() { return stackpop; };
}
if (typeof(nbc.stackpush) !== "function") {
nbc.stackpush = function() { return stackpush; };
}
if (!nbc.printargs) {
nbc.printargs = function(state, bytecode, pc) {
var result = "";
var i = 0;
while (i < this.args) {
result += " ";
result += bytecode[pc+i+1];
i+=1;
}
return result;
};
}
bytecodes_by_num[nbc.id] = nbc;
bytecodes_by_name[nbc.name] = nbc;
};
var print_literal = function(state, bytecode, pc) {
var idx = bytecode[pc+1];
return " "+idx+" /* "+state.literals[idx]+" */";
};
var print_label = function(state, bytecode, pc) {
var result = "";
var i = 0;
while (i < this.args) {
result += " ";
var lbl = bytecode[pc+1+i];
if (typeof(lbl) !== "number") {
lbl = lbl.label;
}
result += lbl;
i += 1;
}
return result;
};
// define the bytecodes for the js virtual machine
// name, args, stackpop, stackpush
// Push the address of the function activation record on the stack
// (If this instruction never occurs in the bytecode for a function,
// the function can safely skip allocation of a frame object.)
bc("push_frame", 0, 0, 1);
// Push the address of the "local scope" record on the stack
// In a naive implementation, this can be identical to push_frame,
// but it allows a clever runtime to register allocate anything
// stored in the local frame.
bc("push_local_frame", 0, 0, 1);
// Push a (numeric or string) literal on the stack.
// Argument #0 is the index into the literal table.
bc("push_literal", 1, 0, 1, print_literal);
// New Object
bc("new_object", 0, 0, 1);
// New Array
bc("new_array", 0, 0, 1);
// New Function
// argument #0 is an index into the function list identifying the bytecode
// for this function.
bc("new_function", 1, 0, 1);
// Fetch a slot (direct)
// argument #0 is name of the slot (as literal table index)
// pops object address. pushes slot value.
bc("get_slot_direct", 1, 1, 1, print_literal);
// Fetch a slot (indirect)
// pops slot name object, then pops object address. pushes slot value.
bc("get_slot_indirect", 0, 2, 1);
// Fetch slot (direct) and verify that it's a function (debugging)
// this is identical to get_slot_direct when debugging's turned off
bc("get_slot_direct_check", 1, 1, 1, print_literal);
// Store to a slot (direct)
// argument #0 is name of the slot (as literal table index)
// pops value, then pops object address.
bc("set_slot_direct", 1, 2, 0, print_literal);
// Fetch a slot (indirect)
// pops value, the pops slot name object, then pops object address.
bc("set_slot_indirect", 0, 3, 0);
// Method dispatch.
// argument #0 is number of (real) arguments to the function
// stack is: <top> argN .. arg3 arg2 arg1 arg0 this function
// on return, pops args, this, and function, and pushes ret value
// (possibly undefined)
bc("invoke", 1, function(opname, arg0) { return arg0+2; }, 1);
// Method return
bc("return", 0, 1, 0);
// branches
// unconditional branch
// argument #0 is the jump target
bc("jmp", 1, 0, 0, print_label);
// Identical to `jmp`, but hints that we're jumping into a loop body
// argument #0 is the jump target, a phi marking the top of the loop
// argument #1 hints the label of the phi marking the loop exit
bc("jmp_into_loop", 2, 0, 0, print_label);
// conditional branch
// argument #0 is the label to jump to if the top of the stack is true
// argument #1 hints the label of the phi marking the eventual merge point
// (hint allows you to compile to balanced control flow structures)
bc("jmp_unless", 2, 1, 0, print_label);
// This is a no-op, but it marks a control-flow merge after a branch/loop
bc("phi", 0, 0, 0);
// stack manipulation TOP <-stack grows <- BOTTOM
bc("pop", 0, 1, 0); // ab.. -> b..
bc("dup", 0, 1, 2); // a.. -> aa..
bc("2dup", 0, 2, 4); // ab.. -> abab..
bc("over", 0, 2, 3); // ab.. -> aba..
bc("over2", 0, 3, 4); // abc.. -> abca..
bc("swap", 0, 2, 2); // ab.. -> ba..
// Unary operators.
bc("un_not", 0, 1, 1);
bc("un_minus", 0, 1, 1);
bc("un_typeof", 0, 1, 1);
// Binary operators
bc("bi_eq", 0, 2, 1);
bc("bi_gt", 0, 2, 1);
bc("bi_gte", 0, 2, 1);
bc("bi_add", 0, 2, 1);
bc("bi_sub", 0, 2, 1);
bc("bi_mul", 0, 2, 1);
bc("bi_div", 0, 2, 1);
// OK, return an object wrapping all this stuff.
return {
__module_name__: "bytecode-table",
__module_init__: make_bytecode_table,
__module_deps__: [],
__module_source__: bytecode_table_source,
for_num: function(n) {
return bytecodes_by_num[n];
},
for_name: function(name) {
return bytecodes_by_name[name];
}
};
});