-
Notifications
You must be signed in to change notification settings - Fork 1
/
stdlib.notwasm
347 lines (304 loc) · 12.6 KB
/
stdlib.notwasm
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
import ht_new : () -> HT;
import ht_get: (HT, str) -> any;
import ht_set: (HT, str, any) -> any;
import array_new: () -> Array;
import array_index: (Array, i32) -> any;
import array_set: (Array, i32, any) -> any;
import array_length: (Array) -> i32;
import string_length: (str) -> i32;
import any_from_ptr: (i32) -> any;
import any_to_ptr: (any) -> i32;
import get_undefined: () -> any;
import get_null: () -> any;
import object_empty: () -> DynObject;
import object_create: (env, any, any) -> any;
import object_set: (DynObject, str, any, ptr) -> any;
import object_get: (DynObject, str, ptr) -> any;
import ref_new_non_ptr_32: (i32) -> Ref(i32);
import ref_new_f64: (f64) -> Ref(f64);
import ref_new_any: (any) -> Ref(any);
import ref_new_ptr: (ptr) -> Ref(ptr);
// i don't believe the fact that there's no generic closure type is a problem
// here, but it may be
import ref_new_closure: (clos(env, any) -> any) -> Ref(clos(env, any) -> any);
import init: () -> void;
import gc_enter_fn: (i32) -> void;
import gc_exit_fn: () -> void;
// NOTE(arjun): The type below is not accurate. The first argument is
// a *mut Tag, but we don't have a type for that.
import set_in_current_shadow_frame_slot: (i32, i32) -> void;
import set_any_in_current_shadow_frame_slot : (any, i32) -> void;
import set_closure_in_current_shadow_frame_slot : (clos () -> void, i32) -> void;
// The type below is not accurate. The first argument is
// a *mut Tag, but we don't have a type for that.
import set_in_globals_frame: (i32, i32) -> void;
import set_any_in_globals_frame: (any, i32) -> void;
import set_closure_in_globals_frame : (clos () -> void, i32) -> void;
import any_to_f64: (any) -> f64;
import f64_to_any: (f64) -> any;
// length -> Env
import env_alloc: (i32, DynObject) -> i32;
// TODO(luna): this could be a single wasm instruction too
// (env: Env, index, item) -> Env
import env_init_at: (i32, i32, any) -> i32;
// this could be 2 wasm instructions
import closure_new: (i32, i32) -> clos () -> void;
// i tried writing these 2 in wasm too but it got more complicated than i'd
// like; i don't think i'd write it smarter than the rust compiler inlining
// aside; and i hope we can inline the runtime automatically at some point
// -> Env
import closure_env: (clos () -> void) -> i32;
import closure_func: (clos () -> void) -> () -> void;
// here's some standard library stuff!!
// most of these take Env, Any which is _env, _this (usually ignored)
// note this is normalized to always accept the radix by
// javascript::normalize_std_lib_calls
import parse_int : (env, any, any, any) -> any;
// returns 5 for now because void messiness remains
import console_log : (env, any, any) -> any;
// math
import math_sqrt : (env, any, any) -> any;
import math_sin : (env, any, any) -> any;
import math_abs : (env, any, any) -> any;
import math_min : (env, any, any, any) -> any;
import math_max : (env, any, any, any) -> any;
// __JNKS
import heap_dump : (env, any) -> any;
import log_any_raw : (any, any) -> any;
import dbg_log : (any) -> any;
import janky_primitive_plus : (any, any) -> any;
import any_is_object : (any) -> bool;
import run_gc : (env, any) -> any;
import mem_info : (env, any) -> any;
// this is the minimal runtime which doesn't initialize the standard library,
// and only does the bare minimum to allow {} to work
// this plus std_lib.notwasm comprises the entire notwasm runtime
// this file can be merged without std_lib.notwasm for a minimal working
// notwasm
var undefined: any;
// abusing lazy globals to create a null DynObject for jnks_new_object
var jnks_null_dynobject: DynObject;
// Cached Object properties to be used in jnks_new_object.
// ASSUMPTION: Object.prototype is never modified by user code.
var jnks_objectCreate: clos(env, any, any) -> any;
// this is needed for an awful check in jnks_new_object, explained there
// the type is bool, but notwasm sucks and can't compare bools, so here we are
var jnks_objectCreateExists: i32 = 0;
var jnks_objectPrototype: any;
// Lazily-initialized global variables. DO NOT READ FROM THESE UNTIL THEY HAVE
// BEEN WRITTEN TO.
var Object: DynObject;
var Math: DynObject;
var global: DynObject;
var console: DynObject;
var __JNKS: DynObject;
var log_any: clos(env, any, any) -> any;
// (_, _this, what, radix) -> i32 or f64(NaN)
var parseInt: clos(env, any, any, any) -> any;
// temporary Error ground to see other compile-time errors (unrelated to this
// being called Error)
var Error: clos(env, any, any) -> any;
// REAL one:
// var Error: clos(env, any, str) -> DynObject;
// Other cached runtime functions
var jnks_any_is_object: (any) -> bool;
var jnks_primitive_plus: (any, any) -> any;
// Initialize JankScripten runtime.
function jnks_init() {
// load the Object.create runtime function
var objectCreateF = rt(object_create);
// comma is obligatory to indicate an empty environment
var objectCreate = clos(objectCreateF, );
jnks_objectCreate = objectCreate;
jnks_objectCreateExists = 1;
var getUndefined = rt(get_undefined);
undefined = getUndefined();
jnks_init_std_lib();
}
// Runtime function to create a regular empty object like `{}` or
// `new Object()`.
function jnks_new_object(): DynObject {
var objectCreate = jnks_objectCreate;
var newObject = objectCreate!(undefined, jnks_objectPrototype);
return newObject as DynObject;
}
// Create an empty object to be used as a function object.
// The only current difference between this function and jnks_new_object
// is that a `prototype` field is automatically initialized here.
function jnks_new_fn_obj(): DynObject {
// Create new object that inherits from Object.prototype,
// AND has a `prototype` field automatically initialized.
// a new fn object is created to store in the closure of... you guessed it,
// objectCreate. so we need to give a fake object for that closure
// only. this hopefully won't be necessary after we optimize non-object
// functions.
if (jnks_objectCreateExists == 1) {
var newObject = jnks_new_object();
var newObjectProto = jnks_new_object();
newObject.prototype = any(newObjectProto);
return newObject;
} else {}
// not written as an if-else because wasm typechecking strictness
// this thing may or may not even be null. it doesn't really matter
return jnks_null_dynobject;
}
// Initialize JankScripten runtime.
function jnks_init_std_lib(): i32 {
// Create the Object class. This means creating a (really) empty object,
// giving it a prototype, and assigning its static methods, such as
// Object.create.
//
// Once the default Object class is set up, object literals like `{}` can
// be used.
// create the actual object for Object
var nullVar = null;
var objectClass = jnks_objectCreate!(undefined, nullVar);
Object = objectClass as DynObject;
// set a completely empty prototype.
// it's called objectPrototypeAny because its type is any.
var objectPrototypeAny = jnks_objectCreate!(undefined, nullVar);
Object.prototype = objectPrototypeAny;
// Object.create is very special because we need it to create
// closures! This needs to happen now
Object.create = any(jnks_objectCreate);
// Cache Object properties for jnks_new_object
jnks_objectPrototype = objectPrototypeAny;
// Now we can add other static methods on Object
var object_freeze = clos(jnks_id, );
Object.freeze = any(object_freeze);
// assign methods to Object.prototype.
// first, unwrap objectPrototypeAny into a DynObject
var objectPrototype = objectPrototypeAny as DynObject;
var objectTostrF = jnks_Object_prototype_tostr;
var objectTostr = clos(objectTostrF, );
objectPrototype.toString = any(objectTostr);
Math = {};
var mathSqrtF = rt(math_sqrt);
var mathSqrt = clos(mathSqrtF, );
Math.sqrt = any(mathSqrt);
var mathSinF = rt(math_sin);
var mathSin = clos(mathSinF, );
Math.sin = any(mathSin);
var mathAbsF = rt(math_abs);
var mathAbs = clos(mathAbsF, );
Math.abs = any(mathAbs);
var mathMinF = rt(math_min);
var mathMin = clos(mathMinF, );
Math.min = any(mathMin);
var mathMaxF = rt(math_max);
var mathMax = clos(mathMaxF, );
Math.max = any(mathMax);
// source: firefox console -> Math.PI
Math.PI = any(3.141592653589793f);
// __JNKS
__JNKS = {};
var heapDumpF = rt(heap_dump);
var heapDump = clos(heapDumpF, );
__JNKS.heap_dump = any(heapDump);
var run_gc_f = rt(run_gc);
var run_gc = clos(run_gc_f, );
__JNKS.run_gc = any(run_gc);
var mem_info_f = rt(mem_info);
var mem_info = clos(mem_info_f, );
__JNKS.mem_info = any(mem_info);
log_any = clos(log_any_raw_env, );
// Other cached runtime functions
var primitive_plusF = rt(janky_primitive_plus);
// var primitive_plus = clos(primitive_plusF, );
jnks_primitive_plus = primitive_plusF;
var any_is_objectF = rt(any_is_object);
// var any_is_object = clos(any_is_objectF, );
jnks_any_is_object = any_is_objectF;
console = {};
var consoleLogF = rt(console_log);
var consoleLog = clos(consoleLogF, );
console.log = any(consoleLog);
console.info = any(consoleLog);
// === free-floating stuff in the global namespace ===
var parseIntF = rt(parse_int);
parseInt = clos(parseIntF, );
Error = clos(error_ground, );
// Now that the default Object class has been set up, create the global
// object.
global = {};
// notwasm limitation
return 0;
}
function error_raw(_: env, _this: any, message: str): DynObject {
// `Error("err")` has same functionality as `new Error("err")`
// so Error i guess doesn't use `this`
var e_proto = {};
e_proto.name = any("Error");
e_proto.message = any(message);
var e_proto_any = any(e_proto);
var e = jnks_objectCreate!(undefined, e_proto_any);
return e as DynObject;
}
function log_any_raw_env(_: env, this: any, message: any): any {
var call_to = rt(log_any_raw);
call_to(this, message);
return undefined;
}
// there's no reason Error has to be ground. it's top-level, not stored
// in an object, so it's never boxed into any. however, coercion_insertion
// wants to convert it to an object because i'm still waiting to add Mark's
// function-objects, so it needs to be any'd to create a bad coercion so i
// can see other compile-time errors
function error_ground(dummyEnv: env, this: any, message: any): any {
var messageStr = message as str;
var result = error_raw(dummyEnv, this, messageStr);
return any(result);
}
// The default implementation of `Object.prototype.toString`.
// All objects that don't specifically override `toString` will
// have this implementation.
function jnks_Object_prototype_tostr(_: env, this: any): any {
return any("[object Object]");
}
// Converts the given value into a primitive value.
// This really just amounts to calling `tostr` if the value
// is an object.
// This isn't implemented to spec but should have the same behavior
// unless you're doing some funky stuff with objects:
// https://www.ecma-international.org/ecma-262/5.1/#sec-9.1
function to_primitive(val: any): any {
// load cached function into a local variable because notwasm doesn't
// support directly calling functions stored in global variables.
var is_object = jnks_any_is_object;
var is_val_obj = is_object(val);
if (is_val_obj) {
var val_obj = val as DynObject;
var val_obj_tostr = val_obj.toString as clos(env, any) -> any;
var val_res = val_obj_tostr!(val);
return val_res;
} else { }
return val;
}
/// The JavaScript `+` operator. This isn't implemented to spec:
/// https://www.ecma-international.org/ecma-262/5.1/#sec-11.6.1
/// because it doesn't first try to coerce its arguments to primitives.
/// It instead uses a simple trick to emulate most of the behavior in the spec:
/// if either of the arguments are NotWasm ptrs, both will be coerced into
/// strings, and string concatenation will happen instead. If both of the
/// arguments are NOT pointers, then they're both primitives and
/// the user is expecting mathematical plus.
function jnks_plus(left: any, right: any): any {
// load cached function into a local variable because notwasm doesn't
// support directly calling functions stored in global variables.
var plus = jnks_primitive_plus;
// Convert non-primitive values to primitives
left = to_primitive(left);
right = to_primitive(right);
var result = plus(left, right);
return result;
}
function print_any(x: any): i32 {
var f = rt(console_log);
var g = clos(f,);
g!(undefined, x);
return 0;
}
/// Useful for stubbing features
function jnks_id(_: env, this: any, x: any): any {
return x;
}