opal 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }