divine 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,512 @@
1
+ # Some notes on Javascript and numbers:
2
+ # . The bitwise operators and shift operators operate on 32-bit ints.
3
+ # . Note that all the positive and negative integers whose magnitude is no greater than 253 are representable in the Number type (indeed, the integer 0 has two representations, +0 and −0).
4
+ # . They are 64-bit floating point values, the largest exact integral value is 2^53, or 9007199254740992 (-9007199254740992 to 9007199254740992)
5
+
6
+
7
+ require 'pp'
8
+
9
+ module Divine
10
+ $debug_javascript = false
11
+
12
+ class JavascriptHelperMethods < BabelHelperMethods
13
+ def javascript_base_class_template_str
14
+ <<EOS
15
+ // ------------------------------------------------------------ BabelDataReader
16
+ function BabelDataReader(data) {
17
+ this.data = data;
18
+ this.index = 0;
19
+ }
20
+
21
+ BabelDataReader.prototype.getbyte = function () {
22
+ return this.data[this.index++];
23
+ };
24
+
25
+ BabelDataReader.prototype.read = function (items) {
26
+ var from = this.index;
27
+ this.index += items
28
+ return this.data.subarray(from, this.index)
29
+ };
30
+
31
+
32
+ // ------------------------------------------------------------ BabelDataWriter
33
+ function BabelDataWriter(data) {
34
+ this.data = data;
35
+ this.index = 0;
36
+ this.data = new Uint8Array(4096);
37
+ }
38
+
39
+ BabelDataWriter.prototype._realloc = function (size) {
40
+ size = size || 4096;
41
+ var old_data = this.data;
42
+ this.data = new Uint8Array(Math.max(size, 4096) + this.data.length);
43
+ this.data.set(old_data, 0);
44
+ };
45
+
46
+ BabelDataWriter.prototype.writeByte = function (a_byte) {
47
+ if (this.index + 1 >= this.data.length) this._realloc();
48
+ this.data[this.index++] = a_byte;
49
+ };
50
+
51
+ BabelDataWriter.prototype.write = function (bytes) {
52
+ if (this.index + bytes.length >= this.data.length) this._realloc(bytes.length);
53
+ this.data.set(bytes, this.index);
54
+ this.index += bytes.length;
55
+ };
56
+
57
+ BabelDataWriter.prototype.get_data = function () {
58
+ return this.data.subarray(0, this.index);
59
+ };
60
+
61
+
62
+ // ------------------------------------------------------------ BabelHelper
63
+ function BabelHelper() {}
64
+
65
+ BabelHelper.prototype.serialize = function () {
66
+ var out = new BabelDataWriter();
67
+ this.serialize_internal(out);
68
+ return out.get_data();
69
+ }
70
+
71
+ BabelHelper.prototype.read_int8 = function (data) {
72
+ return data.getbyte();
73
+ };
74
+
75
+ BabelHelper.prototype.read_int16 = function (data) {
76
+ return (data.getbyte() << 8) | this.read_int8(data);
77
+ };
78
+
79
+ BabelHelper.prototype.read_int24 = function (data) {
80
+ return (data.getbyte() << 16) | this.read_int16(data);
81
+ };
82
+
83
+ BabelHelper.prototype.read_int32 = function (data) {
84
+ // return (data.getbyte() << 24) | this.read_int24(data); // See notes about numbers above
85
+ return (data.getbyte() * (256*256*256)) + this.read_int24(data);
86
+ };
87
+
88
+ BabelHelper.prototype.read_binary = function (data) {
89
+ return data.read(this.read_int32(data));
90
+ };
91
+
92
+ BabelHelper.prototype.read_short_binary = function (data) {
93
+ return data.read(this.read_int8(data));
94
+ };
95
+
96
+ BabelHelper.prototype.read_ip_number = function (data) {
97
+ var ip_array = this.read_short_binary(data);
98
+ ip = "";
99
+ for (i = 0, len = ip_array.length; i < len; i++) {
100
+ b = ip_array[i];
101
+ if (ip.length > 0) {
102
+ ip += ".";
103
+ }
104
+ ip += b.toString();
105
+ }
106
+ return ip;
107
+ };
108
+
109
+ BabelHelper.prototype.read_string = function (data) {
110
+ return this.decode_utf8(data.read(this.read_int16(data)))
111
+ };
112
+
113
+ BabelHelper.prototype.write_int8 = function (v, out) {
114
+ if (v > 0xFF) // Max 255
115
+ this.raise_error("Too large int8 number: " + v);
116
+ out.writeByte(v);
117
+ }
118
+
119
+ BabelHelper.prototype.write_int16 = function (v, out) {
120
+ if (v > 0xFFFF) // Max 65.535
121
+ this.raise_error("Too large int16 number: " + v);
122
+ this.write_int8(v >> 8 & 0xFF, out);
123
+ this.write_int8(v & 0xFF, out);
124
+ }
125
+
126
+ BabelHelper.prototype.write_int24 = function (v, out) {
127
+ if (v > 0xFFFFFF) // Max 16.777.215
128
+ this.raise_error("Too large int24 number: " + v);
129
+ this.write_int8(v >> 16 & 0xFF, out);
130
+ this.write_int16(v & 0xFFFF, out);
131
+ }
132
+
133
+ BabelHelper.prototype.write_int32 = function (v, out) {
134
+ if (v > 0xFFFFFFFF) // Max 4.294.967.295
135
+ this.raise_error("Too large int32 number: " + v);
136
+ this.write_int8(v >> 24 & 0xFF, out);
137
+ this.write_int24(v & 0xFFFFFF, out);
138
+ }
139
+
140
+ BabelHelper.prototype.write_bool = function (v, out) {
141
+ this.write_int8(v ? 1 : 0, out)
142
+ }
143
+
144
+ BabelHelper.prototype.write_bool = function (v, out) {
145
+ this.write_int8(v ? 1 : 0, out)
146
+ }
147
+
148
+ BabelHelper.prototype.write_string = function (v, out) {
149
+ var s = this.encode_utf8(v);
150
+ if (s.length > 0xFFFF) this.raise_error("Too large string: " + s.length + " bytes");
151
+ this.write_int16(s.length, out);
152
+ out.write(s);
153
+ }
154
+
155
+ BabelHelper.prototype.write_binary = function (v, out) {
156
+ if ((v instanceof Array) || (v instanceof Uint8Array)) {
157
+ if (v.length > 0xFFFFFFFF) this.raise_error("Too large binary: " + v.length + " (" + v.constructor.name + ")");
158
+ this.write_int32(v.length, out)
159
+ out.write(v);
160
+ } else if (v.constructor == String) {
161
+ if (v.length > 0xFFFFFFFF) this.raise_error("Too large binary: " + v.length + " (" + v.constructor.name + ")");
162
+ this.write_int32(v.length, out)
163
+ out.write(v);
164
+ } else if (v == null) {
165
+ this.raise_error("Unsupported binary 'null'");
166
+ } else {
167
+ this.raise_error("Unsupported binary of type '" + v.constructor.name + "'");
168
+ }
169
+ }
170
+
171
+ BabelHelper.prototype.write_short_binary = function (v, out) {
172
+ if ((v instanceof Array) || (v instanceof Uint8Array)) {
173
+ if (v.length > 0xFF) this.raise_error("Too large binary: " + v.length + " (" + v.constructor.name + ")");
174
+ this.write_int8(v.length, out)
175
+ out.write(v);
176
+ } else if (v.constructor == String) {
177
+ if (v.length > 0xFF) this.raise_error("Too large binary: " + v.length + " (" + v.constructor.name + ")");
178
+ this.write_int8(v.length, out)
179
+ out.write(v);
180
+ } else if (v == null) {
181
+ this.raise_error("Unsupported binary 'null'");
182
+ } else {
183
+ this.raise_error("Unsupported binary of type '" + v.constructor.name + "'");
184
+ }
185
+ }
186
+
187
+ BabelHelper.prototype.write_ip_number = function (v, out) {
188
+ if ((v instanceof Array) || (v instanceof Uint8Array)) {
189
+ if (v.length != 4 && v.length != 0) this.raise_error("Unknown IP v4 number " + v);
190
+ this.write_short_binary(v, out)
191
+ } else if (v.constructor == String) {
192
+ var ss = [];
193
+ if (v.length > 0) {
194
+ ss = v.split(".").map(Number);
195
+ }
196
+ this.write_ip_number(ss, out);
197
+ } else {
198
+ this.raise_error("Unknown IP number '" + v + "'");
199
+ }
200
+ }
201
+
202
+ BabelHelper.prototype.encode_utf8 = function (str) {
203
+ var utf8 = [];
204
+ var chr, next_chr;
205
+ var x, y, z;
206
+ for (var i = 0; i < str.length; i++) {
207
+ chr = str.charCodeAt(i);
208
+ if ((chr & 0xFF80) == 0) {
209
+ utf8.push(chr);
210
+ } else {
211
+ if ((chr & 0xFC00) == 0xD800) {
212
+ next_chr = str.charCodeAt(i + 1);
213
+ if ((next_chr & 0xFC00) == 0xDC00) {
214
+ // UTF-16 surrogate pair
215
+ chr = (((chr & 0x03FF) << 10) | (next_chr & 0X3FF)) + 0x10000;
216
+ i++;
217
+ } else {
218
+ this.raise_error("Error decoding surrogate pair: " + chr + ", " + next_chr);
219
+ }
220
+ }
221
+ x = chr & 0xFF;
222
+ y = chr & 0xFF00;
223
+ z = chr & 0xFF0000;
224
+
225
+ if (chr <= 0x0007FF) {
226
+ utf8.push(0xC0 | (y >> 6) | (x >> 6));
227
+ utf8.push(0x80 | (x & 63));
228
+ } else if (chr <= 0x00FFFF) {
229
+ utf8.push(0xe0 | (y >> 12));
230
+ utf8.push(0x80 | ((y >> 6) & 63) | (x >> 6));
231
+ utf8.push(0x80 | (x & 63));
232
+ } else if (chr <= 0x10FFFF) {
233
+ utf8.push(0xF0 | (z >> 18));
234
+ utf8.push(0x80 | ((z >> 12) & 63) | (y >> 12));
235
+ utf8.push(0x80 | ((y >> 6) & 63) | (x >> 6));
236
+ utf8.push(0x80 | (x & 63));
237
+ } else {
238
+ this.raise_error("Error encoding to UTF8: " + chr + " is greater than U+10FFFF");
239
+ }
240
+ }
241
+ }
242
+ return utf8;
243
+ }
244
+
245
+ BabelHelper.prototype.decode_utf8 = function (utf8_data) {
246
+ var str = "";
247
+ var chr, b2, b3, b4;
248
+ for (var i = 0; i < utf8_data.length; i++) {
249
+ chr = utf8_data[i];
250
+ if ((chr & 0x80) == 0x00) {}
251
+ else if ((chr & 0xF8) == 0xF0) {
252
+ // 4 bytes: U+10000 - U+10FFFF
253
+ b2 = utf8_data[i + 1];
254
+ b3 = utf8_data[i + 2];
255
+ b4 = utf8_data[i + 3];
256
+ if ((b2 & 0xc0) == 0x80 && (b3 & 0xC0) == 0x80 && (b4 & 0xC0) == 0x80) {
257
+ chr = (chr & 7) << 18 | (b2 & 63) << 12 | (b3 & 63) << 6 | (b4 & 63);
258
+ i += 3;
259
+ } else {
260
+ this.raise_error("Error decoding from UTF8: " + chr + "," + b2 + "," + b3 + "," + b4);
261
+ }
262
+ } else if ((chr & 0xF0) == 0xE0) {
263
+ // 3 bytes: U+0800 - U+FFFF
264
+ b2 = utf8_data[i + 1];
265
+ b3 = utf8_data[i + 2];
266
+ if ((b2 & 0xC0) == 0x80 && (b3 & 0xC0) == 0x80) {
267
+ chr = (chr & 15) << 12 | (b2 & 63) << 6 | (b3 & 63);
268
+ i += 2;
269
+ } else {
270
+ this.raise_error("Error decoding from UTF8: " + chr + "," + b2 + "," + b3);
271
+ }
272
+ } else if ((chr & 0xE0) == 0xC0) {
273
+ // 2 bytes: U+0080 - U+07FF
274
+ b2 = utf8_data[i + 1];
275
+ if ((b2 & 0xC0) == 0x80) {
276
+ chr = (chr & 31) << 6 | (b2 & 63);
277
+ i += 1;
278
+ } else {
279
+ this.raise_error("Error decoding from UTF8: " + chr + "," + b2);
280
+ }
281
+ } else {
282
+ // 80-BF: Second, third, or fourth byte of a multi-byte sequence
283
+ // F5-FF: Start of 4, 5, or 6 byte sequence
284
+ this.raise_error("Error decoding from UTF8: " + chr + " encountered not in multi-byte sequence");
285
+ }
286
+ if (chr <= 0xFFFF) {
287
+ str += String.fromCharCode(chr);
288
+ } else if (chr > 0xFFFF && chr <= 0x10FFFF) {
289
+ // Must be encoded into UTF-16 surrogate pair.
290
+ chr -= 0x10000;
291
+ str += (String.fromCharCode(0xD800 | (chr >> 10)) + String.fromCharCode(0xDC00 | (chr & 1023)));
292
+ } else {
293
+ this.raise_error("Error encoding surrogate pair: " + chr + " is greater than U+10ffff");
294
+ }
295
+ }
296
+ return str;
297
+ }
298
+
299
+ BabelHelper.prototype.raise_error = function (msg) {
300
+ throw "[" + this.constructor.name + "] " + msg;
301
+ }
302
+ EOS
303
+ end
304
+
305
+ def javascript_class_template_str
306
+ <<EOS2
307
+ // ------------------------------------------------------------ <%= c.name %>
308
+ function <%= c.name %>() {
309
+ BabelHelper.call(this);
310
+ <% c.fields.each do |f| %>
311
+ this.<%= f.name %> = <%= this.javascript_get_empty_declaration(f) %>;
312
+ <% end %>
313
+ }
314
+
315
+ // Inherit BabelHelper
316
+ <%= c.name %>.prototype = new BabelHelper();
317
+
318
+ // Correct the constructor pointer because it points to BabelHelper
319
+ <%= c.name %>.prototype.constructor = <%= c.name %>;
320
+
321
+ // Define the methods of <%= c.name %>
322
+ <%= c.name %>.prototype.deserialize = function (data) {
323
+ <% c.simple_fields.each do |f| %>
324
+ this.<%= f.name %> = this.read_<%= f.type %>(data);
325
+ <% end %>
326
+ <% c.complex_fields.each do |f| %>
327
+ <%= this.javascript_deserialize_complex f %>
328
+ <% end %>
329
+ }
330
+
331
+ <%= c.name %>.prototype.serialize_internal = function(out) {
332
+ <% c.simple_fields.each do |f| %>
333
+ this.write_<%= f.type %>(this.<%= f.name %>, out);
334
+ <% end %>
335
+ <% c.complex_fields.each do |f| %>
336
+ <%= this.javascript_serialize_complex f %>
337
+ <% end %>
338
+ }
339
+ EOS2
340
+ end
341
+
342
+ def javascript_get_empty_declaration(field)
343
+ case field.type
344
+ when :list, :binary, :short_binary
345
+ "[]"
346
+ when :map
347
+ "{}"
348
+ when :int8, :int16, :int32
349
+ "0"
350
+ when :string, :ip_number
351
+ "\"\""
352
+ else
353
+ raise "Unkown field type #{field.type}"
354
+ end
355
+ end
356
+
357
+ def javascript_serialize_complex(field)
358
+ types = field.referenced_types
359
+ as = [
360
+ "// Serialize #{field.type} '#{field.name}'",
361
+ $debug_javascript ? "console.log(\"Serialize '#{field.name}'\");" : nil,
362
+ javascript_serialize_internal("this.#{field.name}", types)
363
+ ]
364
+ format_src(4, 4, as)
365
+ end
366
+
367
+ def javascript_serialize_internal(var, types)
368
+ if types.respond_to? :first
369
+ case types.first
370
+ when :list
371
+ nv = get_fresh_variable_name
372
+ idx = get_fresh_variable_name
373
+ return [
374
+ "this.write_int32(#{var}.length, out);",
375
+ "for(var #{idx}=0; #{idx}<#{var}.length; #{idx}++) {",
376
+ :indent,
377
+ "var #{nv} = #{var}[#{idx}];",
378
+ javascript_serialize_internal(nv, types[1]),
379
+ :deindent,
380
+ "}"
381
+ ]
382
+ when :map
383
+ len = get_fresh_variable_name
384
+ nv1 = get_fresh_variable_name
385
+ nv2 = get_fresh_variable_name
386
+ key = get_fresh_variable_name
387
+ return [
388
+ "var #{len} = Object.keys(#{var}).length;",
389
+ "this.write_int32(#{len}, out);",
390
+ "for(var #{nv1} in #{var}) {",
391
+ :indent,
392
+ "var #{nv2} = #{var}[#{nv1}];",
393
+ javascript_serialize_internal(nv1, types[1]),
394
+ javascript_serialize_internal(nv2, types[2]),
395
+ :deindent,
396
+ "}"
397
+ ]
398
+ else
399
+ raise "Missing serialization for #{var}"
400
+ end
401
+ else
402
+ if $all_structs[types]
403
+ "#{var}.serialize_internal(out)"
404
+
405
+ elsif $available_types[types] && $available_types[types].ancestors.include?(SimpleDefinition)
406
+ "this.write_#{types}(#{var}, out)"
407
+
408
+ else
409
+ raise "Missing code generation case #{types}"
410
+ end
411
+ end
412
+ end
413
+
414
+ def javascript_deserialize_complex(field)
415
+ types = field.referenced_types
416
+ as = [
417
+ "// Deserialize #{field.type} '#{field.name}'",
418
+ $debug_javascript ? "console.log(\"Deserialize '#{field.name}'\");" : nil,
419
+ javascript_deserialize_internal("this.#{field.name}", types)
420
+ ]
421
+ format_src(4, 4, as)
422
+ end
423
+
424
+ def javascript_deserialize_internal(var, types)
425
+ if types.respond_to? :first
426
+ case types.first
427
+ when :list
428
+ count = get_fresh_variable_name
429
+ nv = get_fresh_variable_name
430
+ iter = get_fresh_variable_name
431
+ return [
432
+ "#{"var " unless var.include? "this."}#{var} = [];",
433
+ "var #{count} = this.read_int32(data);",
434
+ "for(var #{iter}=0; #{iter}<#{count}; #{iter}++) {",
435
+ :indent,
436
+ javascript_deserialize_internal(nv, types[1]),
437
+ "#{var}.push(#{nv});",
438
+ :deindent,
439
+ "}"
440
+ ]
441
+ when :map
442
+ count = get_fresh_variable_name
443
+ nv1 = get_fresh_variable_name
444
+ nv2 = get_fresh_variable_name
445
+ iter = get_fresh_variable_name
446
+ return ["#{"var " unless var.include? "this."}#{var} = {};",
447
+ "var #{count} = this.read_int32(data);",
448
+ "for(var #{iter}=0; #{iter}<#{count}; #{iter}++) {",
449
+ :indent,
450
+ javascript_deserialize_internal(nv1, types[1]),
451
+ javascript_deserialize_internal(nv2, types[2]),
452
+ "#{var}[#{nv1}] = #{nv2};",
453
+ :deindent,
454
+ "}"
455
+ ]
456
+ else
457
+ raise "Missing serialization for #{var}"
458
+ end
459
+ else
460
+ case types
461
+ when :map
462
+ "#{"var " unless var.include? "this."}#{var} = {}"
463
+ when :list
464
+ "#{"var " unless var.include? "this."}#{var} = []"
465
+ else
466
+ if $all_structs.key? types
467
+ [
468
+ "#{"var " unless var.include? "this."}#{var} = new #{types}();",
469
+ "#{var}.deserialize(data);"
470
+ ]
471
+ else
472
+ "#{"var " unless var.include? "this."}#{var} = this.read_#{types}(data);"
473
+ end
474
+ end
475
+ end
476
+ end
477
+ end
478
+
479
+
480
+
481
+ class JavascriptGenerator < JavascriptHelperMethods
482
+ def generate_code(structs, opts)
483
+ pp opts
484
+ $debug_javascript = true if opts[:debug]
485
+ base_template = Erubis::Eruby.new(javascript_base_class_template_str)
486
+ class_template = Erubis::Eruby.new(javascript_class_template_str)
487
+ keys = structs.keys.sort
488
+ src = keys.map do |k|
489
+ ss = structs[k]
490
+ # TODO: Should we merge different versions and deduce deprecated methods, warn for incompatible changes, etc?
491
+ raise "Duplicate definitions of struct #{k}" if ss.size > 1
492
+ class_template.result( c: ss.first, this: self )
493
+ end
494
+
495
+ # User defined super class?
496
+ toplevel = opts[:parent_class] || nil
497
+ toplevel = " < #{toplevel}" if toplevel
498
+ return "#{javascript_get_begin_module(opts)}#{base_template.result({ toplevel_class: toplevel })}\n\n#{src.join("\n\n")}#{javascript_get_end_module(opts)}"
499
+ end
500
+
501
+ def javascript_get_begin_module(opts)
502
+ nil
503
+ end
504
+
505
+ def javascript_get_end_module(opts)
506
+ nil
507
+ end
508
+ end
509
+
510
+
511
+ $language_generators[:javascript] = JavascriptGenerator.new
512
+ end