opal 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.
data/runtime/vm.js ADDED
@@ -0,0 +1,1150 @@
1
+ /*
2
+ * vm.js
3
+ * opal
4
+ *
5
+ * Created by Adam Beynon.
6
+ * Copyright 2010 Adam Beynon.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ * THE SOFTWARE.
25
+ */
26
+
27
+
28
+ /**
29
+ Jarv - (Javascript/Just) another ruby vm
30
+ Influenced by yarv, but opcodes are (slightly) different.
31
+ Main difference are the stack frames and stacks themselves.
32
+ */
33
+
34
+ // temp so we dont have to change code later;
35
+ var nil = null;
36
+
37
+ /**
38
+ Instruction table (opcodes)
39
+ */
40
+ var iNOP = 0, iGETLOCAL = 1,
41
+ iSETLOCAL = 2, iGETSPECIAL = 3,
42
+ iSETSPECIAL = 4, iGETDYNAMIC = 5,
43
+ iSETDYNAMIC = 6, iGETINSTANCEVARIABLE = 7,
44
+ iSETINSTANCEVARIABLE = 8, iGETCLASSVARIABLE = 9,
45
+ iSETCLASSVARIABLE = 10, iGETCONSTANT = 11,
46
+ iSETCONSTANT = 12, iGETGLOBAL = 13,
47
+ iSETGLOBAL = 14, iPUTNIL = 15,
48
+ iPUTSELF = 16, iPUTOBJECT = 17,
49
+ iPUTSTRING = 18, iCONCATSTRINGS = 19,
50
+ iTOSTRING = 20, iTOREGEXP = 21,
51
+ iNEWARRAY = 22, iDUPARRAY = 23,
52
+ iEXPANDARRAY = 24, iCONCATARRAY = 25,
53
+ iSPLATARRAY = 26, iCHECKINCLUDEARRAY = 27,
54
+ iNEWHASH = 28, iNEWRANGE = 29,
55
+ iPOP = 30, iDUP = 31,
56
+ iDUPN = 32, iSWAP = 33,
57
+ iREPUT = 34, iTOPN = 35,
58
+ iSETN = 36, iADJUSTSTACK = 37,
59
+ iDEFINEMETHOD = 38, iALIAS = 39,
60
+ iUNDEF = 40, iDEFINED = 41,
61
+ iPOSTEXE = 42, iTRACE = 43,
62
+ iDEFINECLASS = 44, iSEND = 45,
63
+ iINVOKESUPER = 46, iINVOKEBLOCK = 47,
64
+ iLEAVE = 48, iFINISH = 49,
65
+ iTHROW = 50, iJUMP = 51,
66
+ iBRANCHIF = 52, iBRANCHUNLESS = 53,
67
+ iGETINLINECACHE = 54, iONCEINLINECACHE = 55,
68
+ iSETINLINECACHE = 56, iOPT_CASE_DISPATCH = 57,
69
+ iOPT_CHECKENV = 58, iOPT_PLUS = 59,
70
+ iOPT_MINUS = 60, iOPT_MULT = 61,
71
+ iOPT_DIV = 62, iOPT_MOD = 63,
72
+ iOPT_EQ = 64, iOPT_NEQ = 65,
73
+ iOPT_LT = 66, iOPT_LE = 67,
74
+ iOPT_GT = 68, iOPT_GE = 69,
75
+ iOPT_LTLT = 70, iOPT_AREF = 71,
76
+ iOPT_ASET = 72, iOPT_LENGTH = 73,
77
+ iOPT_SUCC = 74, iOPT_NOT = 75,
78
+ iOPT_REGEXPMATCH1 = 76, iOPT_REGEXPMATCH2 = 77,
79
+ iOPT_CALL_C_FUNCTION = 78, iBITBLT = 79,
80
+ iANSWER = 80,
81
+ // JARV additions to YARV
82
+ iPUTSYMBOL = 81;
83
+
84
+ /**
85
+ iseq types
86
+ */
87
+ var ISEQ_TYPE_TOP = 1,
88
+ ISEQ_TYPE_METHOD = 2,
89
+ ISEQ_TYPE_BLOCK = 3,
90
+ ISEQ_TYPE_CLASS = 4,
91
+ ISEQ_TYPE_RESCUE = 5,
92
+ ISEQ_TYPE_ENSURE = 6,
93
+ ISEQ_TYPE_EVAL = 7,
94
+ ISEQ_TYPE_MAIN = 8;
95
+
96
+ /**
97
+ DEFINECLASS types
98
+ */
99
+ var DEFINECLASS_CLASS = 0,
100
+ DEFINECLASS_SINGLETON = 1,
101
+ DEFINECLASS_MODULE = 2;
102
+
103
+ /**
104
+ == Depreceated?
105
+
106
+
107
+ call args
108
+ */
109
+ var VM_CALL_ARGS_SPLAT_BIT = 2,
110
+ VM_CALL_ARGS_BLOCKARG_BIT = 4,
111
+ VM_CALL_FCALL_BIT = 8,
112
+ VM_CALL_VCALL_BIT = 16,
113
+ VM_CALL_TAILCALL_BIT = 32,
114
+ VM_CALL_TAILRECURSION_BIT = 64,
115
+ VM_CALL_SUPER_BIT = 128,
116
+ VM_CALL_SEND_BIT = 256;
117
+
118
+
119
+ /**
120
+ Handles some functionality found in thread until thread is added to main repo.
121
+ At the moment, using threads makes to much of a performance impact, so they
122
+ will be added if/when performance can be improved. (Need to determine how much
123
+ performance impact is justified.). See 'threads' branch on vienna.adambeynon.com
124
+ for threads code. Not currently in github branch (or branch for gem building.)
125
+ */
126
+ function rb_vm() {
127
+ this.self = null;
128
+
129
+ this.running = 0;
130
+
131
+ // current frame pointer - rb_control_frame
132
+ this.cfp = null;
133
+ // control frame stack
134
+ this.cfs = []
135
+
136
+ // for iterations
137
+ this.passed_block = null;
138
+
139
+ this.top_self = null;
140
+ this.top_wrapper = null;
141
+
142
+ // eval env
143
+ this.base_block = null;
144
+
145
+ // search style.. search for local .rb or .vm files
146
+ this.search_style = ".rb";
147
+ }
148
+
149
+ function rb_control_frame() {
150
+ // stack. every control frame manages its own stack
151
+ this.stack = []
152
+ // stack pointer
153
+ this.sp = 0;
154
+ // program counter
155
+ this.pc = 0;
156
+ // prev env
157
+ this.prev = null;
158
+ // instruction sequence (array we got from json, for now)
159
+ this.iseq = null;
160
+ // local self
161
+ this.self = null;
162
+ // local frame pointer
163
+ this.lfp = null;
164
+ // dynamic frame pointer
165
+ this.dfp = null;
166
+ // block_t ptr
167
+ this.block_t = null;
168
+ // proc - always 0/false for methods..
169
+ this.proc = 0;
170
+
171
+ // locals
172
+ this.locals = null;
173
+
174
+ // this.insn_info_table = {
175
+ // position: 0, line_no: 0, sp: 0
176
+ // };
177
+ this.line_no = 0;
178
+
179
+ //
180
+ this.method_id = null;
181
+ this.method_class = null;
182
+ };
183
+
184
+ function rb_block_t() {
185
+ this.self = nil;
186
+ this.lfp = nil;
187
+ this.dfp = nil;
188
+ this.iseq = nil;
189
+ this.proc = nil;
190
+ };
191
+
192
+ // currently the only vm. thread support is currently disabled
193
+ var rb_top_vm = null;
194
+
195
+
196
+
197
+ // Seq is an array, [:misc, :name etc....]
198
+ function rb_iseq_eval(iseq) {
199
+ var val, vm = rb_top_vm;
200
+ vm_set_top_stack(vm, iseq);
201
+ val = vm_run_mode_running(rb_top_vm);
202
+ // must pop frame when we are done with it.
203
+ vm_pop_frame(vm);
204
+ return val;
205
+ }
206
+
207
+ function vm_set_top_stack(vm, iseq) {
208
+ // console.log(iseq);
209
+ if (iseq[4] != ISEQ_TYPE_TOP) {
210
+ // rb_raise(rb_eTypeError, "Not a toplevel InstructionSequence");
211
+ throw 'rb_eTypeError: ' + 'Not a top level InstructionSequence'
212
+ }
213
+
214
+
215
+ // vm_push_frame(vm, iseq, ISEQ_TYPE_TOP, vm.top_self, 0, 0, -1, 0, iseq[0][1])
216
+ vm_push_frame(vm, iseq, vm.top_self);
217
+ }
218
+
219
+ function vm_run_mode_sleep(vm) {
220
+ vm.running = 0;
221
+ }
222
+
223
+ function vm_run_mode_running(vm) {
224
+ vm.running = 1;
225
+ return vm_exec(vm);
226
+ }
227
+
228
+ /**
229
+ Execute iseq, for current frame pointer, which will be a function.
230
+ */
231
+ function vm_exec(vm) {
232
+ var jumps = vm.cfp.iseq[8], iseq = vm.cfp.iseq[9], op, sf = vm.cfp;
233
+ // console.log("executing iseq: " + iseq);
234
+ for (; sf.pc < iseq.length; sf.pc++) {
235
+ op = iseq[sf.pc];
236
+ // console.log(sf.pc);
237
+
238
+ if (typeof op === 'number') {
239
+ // console.log("at line number " + op);
240
+ continue;
241
+ }
242
+
243
+ switch (op[0]) {
244
+ /**
245
+ putobject
246
+
247
+ put an object onto the stack
248
+
249
+ == operands
250
+
251
+ VALUE val
252
+
253
+ == stack
254
+
255
+ before: after:
256
+
257
+ ----------
258
+ ========== => val
259
+ ----------
260
+ */
261
+ case iPUTOBJECT:
262
+ // console.log("putobject" + sf.sp);
263
+ sf.stack[sf.sp++] = op[1];
264
+ break;
265
+
266
+ /**
267
+ putstring
268
+ */
269
+ case iPUTSTRING:
270
+ sf.stack[sf.sp++] = op[1];
271
+ break;
272
+
273
+ /**
274
+ putsymbol
275
+
276
+ == operands
277
+
278
+ ptr - string value for symbol
279
+
280
+ == stack
281
+
282
+ before: after:
283
+
284
+ ----------
285
+ ========== => val
286
+ ----------
287
+ */
288
+ case iPUTSYMBOL:
289
+ sf.stack[sf.sp++] = ID2SYM(op[1]);
290
+ break;
291
+
292
+ /**
293
+ getlocal
294
+
295
+ get local from locals array, and put onto stack
296
+
297
+ == operands
298
+
299
+ idx
300
+
301
+ == stack
302
+
303
+ before: after:
304
+
305
+ ----------
306
+ ========== => val
307
+ ----------
308
+ */
309
+ case iGETLOCAL:
310
+ // console.log("getlocal" + sf.sp);
311
+ sf.stack[sf.sp++] = sf.locals[op[1]];
312
+ break;
313
+
314
+ /**
315
+ setlocal
316
+
317
+ == operands
318
+
319
+ idx - index of local. Args, then locals.
320
+
321
+ == stack
322
+
323
+ before: after:
324
+
325
+ ----------
326
+ val => ==========
327
+ ----------
328
+
329
+ == Discussion
330
+
331
+ Sets local var value. Note, not local ivar.
332
+ */
333
+ case iSETLOCAL:
334
+ // console.log("setlocal" + sf.sp);
335
+ sf.locals[op[1]] = sf.stack[--sf.sp];
336
+ // console.log("after: " + sf.sp);
337
+ break;
338
+
339
+ /**
340
+ getdynamic
341
+
342
+ == operands
343
+
344
+ idx - idx in locals
345
+ level - level to search, 0 = current frame
346
+
347
+ */
348
+ case iGETDYNAMIC:
349
+ // cheat, only works for level [0] at the moment, i.e. local frame and use
350
+ // the first index..basically, just so we can get things working.
351
+ sf.stack[sf.sp++] = sf.locals[0];
352
+ // throw ""
353
+ break;
354
+
355
+ /**
356
+ setinstancevariable
357
+
358
+ == operands
359
+
360
+ id
361
+ */
362
+ case iSETINSTANCEVARIABLE:
363
+ rb_ivar_set(sf.self, op[1], sf.stack[--sf.sp]);
364
+ break;
365
+
366
+ /**
367
+ getinstancevariable
368
+
369
+ == operands
370
+
371
+ id
372
+
373
+ == stack
374
+
375
+ val put onto stack
376
+ */
377
+ case iGETINSTANCEVARIABLE:
378
+ sf.stack[sf.sp++] = rb_ivar_get(sf.self, op[1]);
379
+ break;
380
+
381
+ /**
382
+ getconstant
383
+
384
+ == operands
385
+
386
+ id - constant id
387
+
388
+ == stack
389
+
390
+ before: after:
391
+
392
+ ---------- ----------
393
+ klass => val
394
+ ---------- ----------
395
+
396
+ == discussion
397
+
398
+ klass is the base klass to look on.
399
+ val is the constant itself
400
+ */
401
+ case iGETCONSTANT:
402
+ var const_klass = sf.stack[--sf.sp], const_id = op[1];
403
+
404
+ if (const_klass == nil) {
405
+ if (sf.self.flags & T_OBJECT) {
406
+ const_klass = rb_class_real(sf.self.klass);
407
+ }
408
+ else {
409
+ const_klass = sf.self;
410
+ }
411
+ }
412
+
413
+ sf.stack[sf.sp++] = rb_const_get(const_klass, const_id);
414
+ break;
415
+
416
+ /**
417
+ setconstant
418
+
419
+ == operands
420
+
421
+ id - constant id
422
+
423
+ == stack
424
+
425
+ before: after:
426
+
427
+ ----------
428
+ klass
429
+ ---------- => ==========
430
+ val
431
+ ----------
432
+ */
433
+ case iSETCONSTANT:
434
+ var cons_klass = sf.stack[--sf.sp];
435
+ var cons_val = sf.stack[--sf.sp];
436
+ var cons_id = op[1];
437
+
438
+
439
+ if (cons_klass === nil) {
440
+ if (sf.self.flags & T_OBJECT) {
441
+ cons_klass = rb_class_real(sf.self.klass);
442
+ }
443
+ else {
444
+ cons_klass = sf.self;
445
+ }
446
+ }
447
+
448
+ rb_const_set(cons_klass, cons_id, cons_val);
449
+ break;
450
+
451
+ /**
452
+ putnil
453
+
454
+ == operands
455
+
456
+ none
457
+
458
+ == stack
459
+
460
+ before: after:
461
+
462
+ ----------
463
+ ========== => nil
464
+ ----------
465
+ */
466
+ case iPUTNIL:
467
+ // console.log("nil" + sf.sp);
468
+ sf.stack[sf.sp++] = nil;
469
+ break;
470
+
471
+ /**
472
+ putnil
473
+
474
+ == operands
475
+
476
+ none
477
+
478
+ == stack
479
+
480
+ before: after:
481
+
482
+ ----------
483
+ ========== => self
484
+ ----------
485
+ */
486
+ case iPUTSELF:
487
+ sf.stack[sf.sp++] = sf.self;
488
+ break;
489
+
490
+ /**
491
+ pop
492
+
493
+ == operands
494
+
495
+ none
496
+
497
+ == stack
498
+
499
+ before: after:
500
+
501
+ ----------
502
+ val => ==========
503
+ ----------
504
+ */
505
+ case iPOP:
506
+ // console.log("pop" + sf.sp);
507
+ sf.sp--;
508
+ break;
509
+
510
+ /**
511
+ branchunless
512
+
513
+ == operands
514
+
515
+ lbl - jump label to jump to if val is false or nil (RTEST)
516
+
517
+ == stack
518
+
519
+ before: after:
520
+
521
+ ----------
522
+ val => ==========
523
+ ----------
524
+ */
525
+ case iBRANCHUNLESS:
526
+ var jmp_label = op[1], jmp_test = sf.stack[--sf.sp];
527
+ if (jmp_test === nil || jmp_test === false) {
528
+ // actually do jump
529
+ // console.log(jumps[op[1]] + " ..branch unless jump: " + jmp_test);
530
+ // the loop does pc++, so we go to one less than what we actually need
531
+ sf.pc = jumps[op[1]] - 1;
532
+ }
533
+ else {
534
+ // console.log("branch unless do not jump: " + jmp_test);
535
+ }
536
+
537
+ // if RTEST was okay, do nothing..
538
+ break;
539
+
540
+ /**
541
+ jump
542
+
543
+ == operands
544
+
545
+ ==stack
546
+
547
+ */
548
+ case iJUMP:
549
+ // pc ++ in loop makes us go to actual - 1
550
+ sf.pc = jumps[op[1]] - 1;
551
+ break;
552
+
553
+ /**
554
+ leave
555
+
556
+ == discussion
557
+
558
+ on iLEAVE, return the value on top of the stack
559
+ */
560
+ case iLEAVE:
561
+ // console.log("leaving with");
562
+ // console.log(sf.stack[sf.sp - 1]);
563
+ return sf.stack[--sf.sp];
564
+ break;
565
+
566
+ /**
567
+ newhash
568
+
569
+ == operands
570
+
571
+ num - number of hash arguments to take from stack
572
+
573
+ == stack
574
+
575
+ before: after:
576
+
577
+ ---------- ----------
578
+ n args.. => hash
579
+ ---------- ----------
580
+ */
581
+ case iNEWHASH:
582
+ var i, k, v, res = rb_hash_new(), num = op[1];
583
+ var ary = sf.stack.slice(sf.sp - num, sf.sp);
584
+ sf.sp -= num;
585
+
586
+ for (i = 0; i < num; i += 2) {
587
+ rb_hash_aset(res, ary[i], ary[i + 1])
588
+ }
589
+ sf.stack[sf.sp++] = res;
590
+ break;
591
+
592
+ /**
593
+ definemethod
594
+
595
+ == operands
596
+
597
+ method_id - method id
598
+ method_iseq - body of method
599
+ is_singleton - whether the method is a singleton or not
600
+
601
+ == stack
602
+
603
+ before: after:
604
+
605
+ ----------
606
+ val => ==========
607
+ ----------
608
+ */
609
+ case iDEFINEMETHOD:
610
+ var def_obj = sf.stack[--sf.sp];
611
+ var method_id = op[1], method_iseq = op[2], is_singleton = op[3];
612
+
613
+ if (def_obj === nil) {
614
+ if (sf.self.flags & T_OBJECT)
615
+ def_obj = rb_class_real(sf.self.klass);
616
+ else
617
+ def_obj = sf.self;
618
+ }
619
+
620
+ if (is_singleton) {
621
+ rb_define_method(rb_singleton_class(def_obj), method_id, method_iseq, 0);
622
+ }
623
+ else {
624
+ rb_define_method(def_obj, method_id, method_iseq, 0);
625
+ }
626
+
627
+ // console.log(def_obj);
628
+ // throw "dmethod"
629
+ break;
630
+
631
+ /**
632
+ defineclass
633
+
634
+ == operands
635
+
636
+ class_id - class name
637
+ class_iseq - class body/sequences
638
+ define_type - 0, 1 or 2.
639
+
640
+ == stack
641
+
642
+ before: after:
643
+
644
+ ----------
645
+ super ----------
646
+ ---------- => val
647
+ base ----------
648
+ ----------
649
+
650
+ == discussion
651
+
652
+ val, the return value, is the class object itself.
653
+ */
654
+ case iDEFINECLASS:
655
+ var klass, class_id = op[1], class_iseq = op[2], define_type = op[3];
656
+
657
+ var class_super = sf.stack[--sf.sp];
658
+ var class_base = sf.stack[--sf.sp];
659
+
660
+ switch (define_type) {
661
+ case DEFINECLASS_CLASS:
662
+
663
+ if (class_super == nil) {
664
+ class_super = rb_cObject;
665
+ }
666
+
667
+ if (class_base == nil) {
668
+ // this isn't right.
669
+ class_base = sf.self;
670
+ // if class_base is an object, then we need to get its klass.
671
+ if (class_base.flags & T_OBJECT) {
672
+ class_base = rb_class_real(class_base.klass);
673
+ }
674
+ }
675
+
676
+ // find klass, if it already exists
677
+ if (rb_const_defined_at(class_base, class_id)) {
678
+ klass = rb_const_get_at(class_base, class_id);
679
+ }
680
+ else {
681
+ // define new class
682
+ klass = rb_define_class_id(class_id, class_super);
683
+ rb_name_class(klass, class_id);
684
+ // rb_class_set_path(klass, class_base, id)
685
+ rb_const_set(class_base, class_id, klass);
686
+ klass.parent = class_base;
687
+ // rb_class_inherited(class_super, klass);
688
+ }
689
+ break;
690
+
691
+ case DEFINECLASS_SINGLETON:
692
+ // singleton reference class << self; end
693
+ klass = rb_singleton_class(class_base);
694
+ break;
695
+
696
+ case DEFINECLASS_MODULE:
697
+
698
+ if (class_base == nil) {
699
+ class_base = sf.self;
700
+ if (class_base.flags & T_OBJECT) {
701
+ class_base = rb_class_real(class_base.klass);
702
+ }
703
+ }
704
+
705
+ if (rb_const_defined_at(class_base, class_id)) {
706
+ klass = rb_const_get_at(class_base, class_id);
707
+ if (!(klass.flags & T_MODULE)) {
708
+ throw class_id + " is not a module"
709
+ // rb_raise
710
+ }
711
+ }
712
+ else {
713
+ // new module
714
+ klass = rb_define_module_id(class_id);
715
+ rb_name_class(klass, class_id);
716
+ // rb_set_class_path(klass, class_base, class_id);
717
+ rb_const_set(class_base, class_id, klass);
718
+ klass.parent = class_base;
719
+ }
720
+ break;
721
+
722
+ default:
723
+ throw "error, unknown iDEFINECLASS definetype"
724
+ }
725
+
726
+ // do body..
727
+ var pcf = vm.cfp;
728
+ var cfp = vm_push_frame(vm, class_iseq, klass);
729
+ cfp.block_iseq = nil;
730
+
731
+ var val = vm_exec(vm);
732
+ vm_pop_frame(vm);
733
+ // return val;
734
+ // puts val on stack
735
+ sf.stack[sf.sp++] = val;
736
+ break;
737
+
738
+ /**
739
+ send
740
+
741
+ == operands
742
+
743
+ == stack
744
+
745
+ */
746
+ case iSEND:
747
+ // get off arguments first, then get off recv.
748
+ var argc = op[2], mid = op[1];
749
+ var argv = sf.stack.slice(sf.sp - argc, sf.sp);
750
+ sf.sp -= argc;
751
+ var recv = sf.stack[--sf.sp];
752
+
753
+ // if this is set, then we did not give an explicit receiver, so assume
754
+ // self (we can use this info for PRIVATE/PUBLIC calls)
755
+ if (op[4] & VM_CALL_FCALL_BIT) {
756
+ // console.log("changing receiver: " + argv.join(", "));
757
+ recv = sf.self;
758
+ }
759
+ // console.log("recv is - " + mid);
760
+ // console.log(sf.self);
761
+ // console.log("a");
762
+ // console.log(mid);
763
+ // pop result of call onto stack
764
+ sf.stack[sf.sp++] = rb_call(recv.klass, recv, mid, argc, argv, op[3]);
765
+
766
+ break;
767
+
768
+ /**
769
+ invokesuper
770
+
771
+ == operands
772
+
773
+ argc
774
+ blockiseq
775
+ op_flag
776
+
777
+ == stack
778
+
779
+ before: after:
780
+
781
+
782
+ ---------- ----------
783
+ args.... => val
784
+ ---------- ----------
785
+ */
786
+ case iINVOKESUPER:
787
+ var op_argc = op[1], op_blockiseq = op[2], op_flag = op[3];
788
+ var recv = sf.self;
789
+ var argv = sf.stack.slice(sf.sp - op_argc, sf.sp);
790
+ sf.stack[sf.sp++] = rb_super_call(recv.klass, recv, sf.method_id, op_argc, argv, op_blockiseq);
791
+ break;
792
+
793
+ /**
794
+ invokeblock
795
+
796
+ == operands
797
+
798
+ argc
799
+ flag
800
+
801
+ == stack
802
+
803
+ before: after:
804
+
805
+
806
+ ---------- ----------
807
+ args.... => val
808
+ ---------- ----------
809
+ */
810
+ case iINVOKEBLOCK:
811
+ console.log(vm);
812
+ throw "a"
813
+ break;
814
+
815
+ /**
816
+ opt_plus
817
+
818
+ == operands
819
+
820
+ none
821
+
822
+ == stack
823
+
824
+ before: after:
825
+
826
+ ----------
827
+ obj ----------
828
+ ---------- => val
829
+ recv ----------
830
+ ----------
831
+
832
+ == discussion
833
+
834
+ Optomised way to send '+' between numbers. If both are numbers, then
835
+ we can just do a plus and pop value onto stack. If not, then we need to
836
+ set up a normal iSEND.
837
+ */
838
+ case iOPT_PLUS:
839
+ var opt_obj = sf.stack[--sf.sp];
840
+ var opt_recv = sf.stack[--sf.sp];
841
+ // if both numbers, do fast track, otherwise we need to send it, as per
842
+ // normal
843
+ if ((opt_obj.flags & T_NUMBER) && (opt_recv.flags & T_NUMBER)) {
844
+ sf.stack[sf.sp++] = opt_obj + opt_recv;
845
+ }
846
+ else {
847
+ throw "opt_plus: need to send as normal iSEND"
848
+ }
849
+ break;
850
+
851
+ /**
852
+ newarray
853
+
854
+ */
855
+ case iNEWARRAY:
856
+ var argc = op[1];
857
+ var ary = sf.stack.slice(sf.ap - argc, sf.sp);
858
+ sf.sp -= argc;
859
+ sf.stack[sf.sp++] = ary;
860
+ break;
861
+
862
+ default:
863
+ throw "unknown opcode " + op.toString()
864
+ }
865
+ }
866
+ return nil;
867
+ // return iseq();
868
+ }
869
+
870
+
871
+ function vm_push_frame(vm, iseq, self) {
872
+ var cfp = new rb_control_frame();
873
+ cfp.iseq = iseq;
874
+ cfp.self = self;
875
+ cfp.pc = 0;
876
+ cfp.stack = [];
877
+ cfp.sp = 0;
878
+
879
+ cfp.locals = new Array(iseq[0] + iseq[1]);
880
+ cfp.method_id = iseq[2];
881
+
882
+ vm.cfp = cfp;
883
+ vm.cfs.push(cfp);
884
+ return cfp;
885
+ }
886
+
887
+ function vm_pop_frame(vm) {
888
+ // console.log(vm);
889
+ // throw "."
890
+ vm.cfs.pop();
891
+ vm.cfp = vm.cfs[vm.cfs.length - 1];
892
+ }
893
+
894
+ function rb_funcall(recv, mid, argc) {
895
+ var argv = Array.prototype.slice.call(arguments, 3, argc + 3);
896
+
897
+ return rb_call(recv.klass, recv, mid, argc, argv, nil);
898
+ }
899
+
900
+ function rb_call(klass, recv, mid, argc, argv, blockptr) {
901
+ var body = rb_search_method(klass, mid);
902
+ if (!body) {
903
+ console.log("calling method missing with: " + mid);
904
+ console.log(recv);
905
+ return rb_call(klass, recv, "method_missing", 2, [mid, argv], blockptr);
906
+ }
907
+
908
+ return rb_vm_call(rb_top_vm, klass, recv, mid, mid, argc, argv, body, 0, blockptr);
909
+ };
910
+
911
+ function rb_search_method(klass, id) {
912
+
913
+ var f, k = klass;
914
+ while (!(f = k.m_tbl[id])) {
915
+ k = k.sup;
916
+ if (!k) return undefined;
917
+ }
918
+ return f;
919
+ };
920
+
921
+ function rb_vm_call(vm, klass, recv, id, oid, argc, argv, body, nosuper, blockiseq) {
922
+ // console.log(id);
923
+ // console.log(blockptr);
924
+ if (typeof body === 'function') {
925
+
926
+ var pcf = vm.cfp;
927
+
928
+ var cfp = vm_push_frame(vm, [0,0], recv);
929
+
930
+ var block_t = new rb_block_t();
931
+ cfp.block_t = block_t;
932
+ block_t.iseq = blockiseq;
933
+ block_t.dfp = cfp;
934
+ // block_t.self =
935
+
936
+ var val = call_cfunc(body, recv, body.rb_argc, argc, argv);
937
+
938
+ vm_pop_frame(vm);
939
+ // console.log("popping from " + id);
940
+ return val;
941
+ }
942
+ else {
943
+
944
+ var pcf = vm.cfp;
945
+ var cfp = vm_push_frame(vm, body, recv);
946
+
947
+ var block_t = new rb_block_t();
948
+ cfp.block_t = block_t;
949
+ block_t.iseq = blockiseq;
950
+ block_t.dfp = cfp;
951
+
952
+ for (var i = 0; i < argc; i++) {
953
+ cfp.locals[i] = argv[i];
954
+ }
955
+
956
+ var val = vm_exec(vm);
957
+ vm_pop_frame(vm);
958
+ // console.log("popping from " + id);
959
+ return val;
960
+ }
961
+ }
962
+
963
+ function call_cfunc(func, recv, len, argc, argv) {
964
+
965
+ if (len >= 0 && argc != len) {
966
+ // rb_raise(rb_eArgError, "wrong number of arguments(" + argc + " for " + len + ")");
967
+ throw "rb_eArgError: wrong number of arguments(" + argc + " for " + len + ")"
968
+ }
969
+
970
+ // even though 15 is acceptable, we should determine a cut off point. 5 seems reasonable, then after
971
+ // 5, we just push recv to start of argv, and apply().
972
+ switch (len) {
973
+ case -2:
974
+ throw "call_cfunc: unimplemeneted: -2 arg length"
975
+ case -1:
976
+ return func(argc, argv, recv);
977
+ case 0:
978
+ return func(recv);
979
+ case 1:
980
+ return func(recv, argv[0]);
981
+ case 2:
982
+ return func(recv, argv[0], argv[1]);
983
+ case 3:
984
+ return func(recv, argv[0], argv[1], argv[2]);
985
+ case 4:
986
+ return func(recv, argv[0], argv[1], argv[2], argv[3]);
987
+ case 5:
988
+ return func(recv, argv[0], argv[1], argv[2], argv[3], argv[4]);
989
+ case 6:
990
+ return func(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
991
+ case 7:
992
+ return func(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]);
993
+ case 8:
994
+ return func(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7]);
995
+ case 9:
996
+ return func(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8]);
997
+ case 10:
998
+ return func(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9]);
999
+ case 11:
1000
+ return func(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]);
1001
+ case 12:
1002
+ return func(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11]);
1003
+ case 13:
1004
+ return func(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12]);
1005
+ case 14:
1006
+ return func(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13]);
1007
+ case 15:
1008
+ return func(recv, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6], argv[7], argv[8], argv[9], argv[10], argv[11], argv[12], argv[13], argv[14]);
1009
+ default:
1010
+ // rb_raise(rb_eArgError, "too many arguments(" + len + ")");
1011
+ throw "rb_eArgError: too many arguments(" + len + ")"
1012
+ }
1013
+ throw "should never be reached"
1014
+ };
1015
+
1016
+ /**
1017
+ Call super
1018
+ */
1019
+ function rb_super_call(klass, recv, mid, argc, argv, blockptr) {
1020
+ var vm = rb_top_vm;
1021
+ // console.log("current iseq/body");
1022
+ // find current iseq
1023
+ var k = klass, f, cur = vm.cfp.iseq;
1024
+ while (!(f = k.m_tbl[mid]) || !(f == vm.cfp.iseq)) {
1025
+ k = k.sup;
1026
+ // if (!k) return undefined;
1027
+ if (!k) throw "cannot find method in super_call"
1028
+ }
1029
+ // console.log(f);
1030
+ // f is now our current, so look for our super()
1031
+ k = k.sup;
1032
+ while (!(f = k.m_tbl[mid])) {
1033
+ k = k.sup;
1034
+ // if (!k) return undefined;
1035
+ if (!k) throw "cannot find method in super_call"
1036
+ }
1037
+ // console.log(f);
1038
+
1039
+ if (!f) {
1040
+ throw "cannot find super.."
1041
+ }
1042
+ console.log(recv);
1043
+ return rb_vm_call(rb_top_vm, klass, recv, mid, mid, argc, argv, f, 0, blockptr);
1044
+ };
1045
+
1046
+ /**
1047
+ Is block given. This is used from c functions that check the current frame
1048
+ pointer to see if it has a block.
1049
+ */
1050
+ function rb_block_given_p() {
1051
+
1052
+ }
1053
+
1054
+ /**
1055
+ Is block given. This method, accessible from ruby, checks the previous stack
1056
+ frame for a block. Calling this method pushes a new stack frame onto the stack,
1057
+ so the previous one must be checked.
1058
+ */
1059
+ function rb_f_block_given_p(recv) {
1060
+
1061
+ }
1062
+
1063
+ function rb_yield(val) {
1064
+ if (val === nil) return rb_yield_0(0, []);
1065
+ return rb_yield_0(1, [val]);
1066
+ }
1067
+
1068
+ function rb_yield_0(argc, argv) {
1069
+ var vm = rb_top_vm;
1070
+ // console.log(vm.cfp);
1071
+ if (vm.cfp.block_t !== nil) {
1072
+ var recv = 0; // FIXME: get the correct receiver..from dfp in block_t
1073
+
1074
+ var pcf = vm.cfp;
1075
+ var cfp = vm_push_frame(vm, vm.cfp.block_t.iseq, recv);
1076
+ // cfp.dfp = vm.cfp.block_t.iseq;
1077
+ // cfp.self = vm.cfp.block_t.self;
1078
+
1079
+ for (var i = 0; i < argc; i++) {
1080
+ cfp.locals[i] = argv[i];
1081
+ }
1082
+
1083
+ var val = vm_exec(vm);
1084
+ vm_pop_frame(vm);
1085
+ return val;
1086
+ }
1087
+ throw "rb_yield_0: no block given";
1088
+ return nil;
1089
+ }
1090
+
1091
+
1092
+ // Initializie VM - this will run the main VM
1093
+ function Init_VM() {
1094
+ rb_top_vm = new rb_vm();
1095
+ rb_top_vm.top_self = rb_top_self;
1096
+ }
1097
+
1098
+ function main_to_s() {
1099
+ return "main";
1100
+ }
1101
+
1102
+ rb_top_self = null;
1103
+
1104
+ function rb_vm_top_self() {
1105
+ return rb_top_vm.top_self;
1106
+ }
1107
+
1108
+
1109
+ // Initialize top self
1110
+ function Init_top_self() {
1111
+ /**
1112
+ Hack. When we run this, our VM isnt actually running.... so we cant use methods.. hmmm
1113
+ */
1114
+ rb_top_self = new RObject();
1115
+ rb_top_self.klass = rb_cObject;
1116
+ FL_SET(rb_top_self, T_OBJECT);
1117
+ rb_define_singleton_method(rb_top_self, 'to_s', main_to_s, 0);
1118
+ }
1119
+
1120
+ function rb_method_missing(argc, argv, recv) {
1121
+ throw "method missing: " + argv.join(",")
1122
+ }
1123
+
1124
+ function Init_vm_eval() {
1125
+
1126
+ // rb_define_method(rb_mKernel, "eval", rb_f_eval, -1);
1127
+ // rb_define_method(rb_mKernel, "local_variables", rb_f_local_variables, 0);
1128
+ // rb_define_method(rb_mKernel, "iterator?", rb_f_block_given_p, 0);
1129
+ // rb_define_method(rb_mKernel, "block_given?", rb_f_block_given_p, 0);
1130
+ //
1131
+ // rb_define_method(rb_mKernel, "catch", rb_f_catch, -1);
1132
+ // rb_define_method(rb_mKernel, "throw", rb_f_throw, -1);
1133
+ //
1134
+ // rb_define_method(rb_mKernel, "loop", rb_f_loop, 0);
1135
+ //
1136
+ // rb_define_method(rb_cBasicObject, "instance_eval", rb_obj_instance_eval, -1);
1137
+ // rb_define_method(rb_cBasicObject, "instance_exec", rb_obj_instance_exec, -1);
1138
+ rb_define_private_method(rb_cBasicObject, "method_missing", rb_method_missing, -1);
1139
+
1140
+ // rb_define_method(rb_cBasicObject, "__send__", rb_f_send, -1);
1141
+ // rb_define_method(rb_mKernel, "send", rb_f_send, -1);
1142
+ // rb_define_method(rb_mKernel, "public_send", rb_f_public_send, -1);
1143
+ //
1144
+ // rb_define_method(rb_cModule, "module_exec", rb_mod_module_exec, -1);
1145
+ // rb_define_method(rb_cModule, "class_exec", rb_mod_module_exec, -1);
1146
+ // rb_define_method(rb_cModule, "module_eval", rb_mod_module_eval, -1);
1147
+ // rb_define_method(rb_cModule, "class_eval", rb_mod_module_eval, -1);
1148
+ //
1149
+ // rb_define_method(rb_mKernel, "caller", rb_f_caller, -1);
1150
+ }