forked from rubinius/rubinius
-
Notifications
You must be signed in to change notification settings - Fork 0
/
instructions.def
2286 lines (1923 loc) · 64.2 KB
/
instructions.def
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
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# vim: filetype=instructions
# Definitions of the Rubinius VM instruction set.
#
# [Description]
# The classic no-op operator. It performs no actions and does not modify the
# stack.
# [See Also]
# pop
instruction noop() [ -- ]
end
section "Push primitive values"
# [Description]
# The special object `nil` is pushed onto the stack.
instruction push_nil() [ -- nil ]
stack_push(Qnil);
end
# [Description]
# The special value `true` is pushed onto the stack.
instruction push_true() [ -- true ]
stack_push(Qtrue);
end
# [Description]
# The special object `false` is pushed onto the stack.
instruction push_false() [ -- false ]
stack_push(Qfalse);
end
# [Description]
# Pushes the value of the integer literal onto the stack.
# [See Also]
# meta_push_0
# meta_push_1
# meta_push_2
# meta_push_neg_1
# [Notes]
# Certain common cases (i.e. -1, 0, 1, and 2) are optimised to avoid the
# decoding of the argument.
instruction push_int(number) [ -- number ]
stack_push(Fixnum::from(number));
end
# [Description]
# The current `self` object is pushed onto the stack.
instruction push_self() [ -- self ]
stack_push(call_frame->self());
end
section "Manipulate literals"
# [Description]
# Used to set the value of a literal. The stack top is set to the literal
# indicated by the operand.
# [Notes]
# Unlike other literals such as strings and numbers, creating a Regexp
# literal (i.e. via the /regex/ syntax) is a two step process to create the
# literal slot for the Regexp, create a literal for the string between the
# '/' delimiters and create a new Regexp object passing it the string. Only
# then can the literal value be set, using the set_literal opcode.
instruction set_literal(literal) [ value -- value ]
call_frame->cm->literals()->put(state, literal, stack_top());
end
# [Description]
# The value identified by the operand _literal_ in the current state
# literals tuple is retrieved and placed onto the stack.
# [Notes]
# The literals tuple is part of the machine state, and holds all literal
# objects defined or used within a particular scope.
instruction push_literal(literal) [ -- literal ]
stack_push(call_frame->cm->literals()->at(state, literal));
end
section "Flow control"
# [Description]
# Unconditionally moves the instruction pointer to the position specified by
# _location_.
# [Notes]
# All goto instructions use absolute addressing. This is absolute movement
# rather than a relative one, so the operand must specify the ip starting
# from 0 to move to.
# [See Also]
# goto_if_true
# goto_if_false
instruction goto(location) [ -- ] => branch
call_frame->set_ip(location);
cache_ip(location);
DISPATCH;
end
# [Description]
# Remove the top value on the stack, and if `nil` or `false`, set the
# instruction pointer to the value specified by _location_.
# [See Also]
# goto
# goto_if_true
instruction goto_if_false(location) [ value -- ] => branch
Object* t1 = stack_pop();
if(!RTEST(t1)) {
call_frame->set_ip(location);
cache_ip(location);
DISPATCH;
}
end
# [Description]
# Remove the top value on the stack, and if not `nil` or `false`, set the
# instruction pointer to the value specified by _location_.
# [See Also]
# goto
# goto_if_false
instruction goto_if_true(location) [ value -- ] => branch
Object* t1 = stack_pop();
if(RTEST(t1)) {
call_frame->set_ip(location);
cache_ip(location);
DISPATCH;
}
end
# [Description]
# Return a value to the direct caller
#
# Pops the top value from the stack, and returns to the direct caller of the
# current invocation.
# [Notes]
# In a method, the `return` keyword uses this instruction. In a block
# though, `return` uses the raise_return instruction and `next` uses this
# instruction.
# [See Also]
# raise_return
# raise_exc
instruction ret() [ value -- value ] => return
if(call_frame->scope->made_alias_p()) {
call_frame->scope->flush_to_heap(state);
}
return stack_top();
end
section "Stack manipulations"
# [Description]
# Swaps the top two values on the stack, so that the second value becomes
# the first, and the first value becomes the second.
instruction swap_stack() [ s0 s1 -- s1 s0 ]
Object* t1 = stack_pop();
Object* t2 = stack_pop();
stack_push(t1);
stack_push(t2);
end
# [Description]
# Read a value from the top of the stack and push it on the stack again
# without removing the original value.
instruction dup_top() [ s0 -- s0 s0 ]
stack_push(stack_top());
end
# [Description]
# Duplicate multiple values on the stack
#
# Read _count_ values from the stack and push them onto the stack again
# in order without removing the original values.
# [Stack Before]
# value1
# value2
# ...
# [Stack After]
# value1
# value2
# value1
# value2
# ...
instruction dup_many(count) [ +count -- ++count ]
Object** objs = stack_back_position(count);
for(intptr_t i = 0; i < count; i++) {
stack_push(objs[i]);
}
end
# [Description]
# Removes the top value from the stack, discarding it.
# [Notes]
# Pop is typically used when the return value of another opcode is not
# required.
instruction pop() [ value -- ]
(void)stack_pop();
end
# [Description]
# Removes _count_ values from the stack and discard them.
# [Stack Before]
# value1
# value2
# ...
# [Stack After]
# ...
instruction pop_many(count) [ +count -- ]
for(intptr_t i = 0; i < count; i++) {
(void)stack_pop();
}
end
# [Description]
# Reverses the order on the stack of the top _count_ values.
# [Stack Before]
# obj1
# obj2
# obj3
# ...
# [Stack After]
# obj3
# obj2
# obj1
# ...
instruction rotate(count) [ +count -- +count ]
int diff = count >> 1;
Object** start = STACK_PTR - (count - 1);
Object** end = STACK_PTR;
for(int i = 0; i < diff; i++) {
Object* right = *end;
Object* left = *start;
*start = right;
*end = left;
start++;
end--;
}
end
# [Description]
# The top value on the stack is moved down by the specified number of
# _positions_, with all values above that position shuffling up by one.
# [Stack Before]
# obj1
# obj2
# ...
# objn
# [Stack After]
# obj2
# ...
# objn
# obj1
instruction move_down(positions) [ +positions -- +positions ]
Object* val = stack_top();
for(int i = 0; i < positions; i++) {
int target = -i;
int current = target - 1;
STACK_PTR[target] = STACK_PTR[current];
}
STACK_PTR[-positions] = val;
end
section "Manipulate local variables"
# [Description]
# Read the top of the stack and set the local variable identified by operand
# _local_ to it. The stack is not modified by this instruction.
# [See Also]
# push_local
instruction set_local(local) [ value -- value ]
call_frame->scope->set_local(local, stack_top());
end
# [Description]
# Retrieves the current value of the local variable referenced by operand
# _local_ and push it onto the stack.
# [See Also]
# set_local
instruction push_local(local) [ -- value ]
stack_push(call_frame->scope->get_local(local));
end
# [Description]
# Pushes the value of a local from an enclosing scope onto the stack
#
# Retrieves the value of a local variable. Operand _depth_ indicates how many
# upward enclosing scopes to walk up and then operand _index_ indicates which
# local in that context to read. The value is then pushed on the stack.
# [See Also]
# set_local_depth
# [Example]
# k = 0
# foo.each do |i|
# bar.each do |j|
# # i is a local variable from enclosing scope at depth 1
# # k is a local variable from enclosing scope at depth 2
# i = i + j + k
# end
# end
instruction push_local_depth(depth index) [ -- value ]
if(depth == 0) {
Exception::internal_error(state, call_frame,
"illegal push_local_depth usage");
RUN_EXCEPTION();
} else {
VariableScope* scope = call_frame->scope->parent();
if(!scope || scope->nil_p()) {
Exception::internal_error(state, call_frame,
"illegal push_local_depth usage, no parent");
RUN_EXCEPTION();
}
for(int j = 1; j < depth; j++) {
scope = scope->parent();
if(!scope || scope->nil_p()) {
Exception::internal_error(state, call_frame,
"illegal push_local_depth usage, no parent");
RUN_EXCEPTION();
}
}
if(index >= scope->number_of_locals()) {
Exception::internal_error(state, call_frame,
"illegal push_local_depth usage, bad index");
RUN_EXCEPTION();
}
stack_push(scope->get_local(index));
}
end
# [Description]
# Updates the value of a local variable contained in an enclosing scope
#
# Read a value from the top of the stack and use it to update a local
# variable in an enclosing scope. The _depth_ and _index_ operands
# identify the specific local the same as in `push_local_depth`.
# [See Also]
# push_local_depth
# [Example]
# foo.each do |i|
# bar.each do |j|
# i = i + j # i is a local variable from enclosing scope at depth 1
# end
# end
instruction set_local_depth(depth index) [ value -- value ]
if(depth == 0) {
Exception::internal_error(state, call_frame,
"illegal set_local_depth usage");
RUN_EXCEPTION();
} else {
VariableScope* scope = call_frame->scope->parent();
if(!scope || scope->nil_p()) {
Exception::internal_error(state, call_frame,
"illegal set_local_depth usage, no parent");
RUN_EXCEPTION();
}
for(int j = 1; j < depth; j++) {
scope = scope->parent();
if(!scope || scope->nil_p()) {
Exception::internal_error(state, call_frame,
"illegal set_local_depth usage, no parent");
RUN_EXCEPTION();
}
}
if(index >= scope->number_of_locals()) {
Exception::internal_error(state, call_frame,
"illegal set_local_depth usage, bad index");
RUN_EXCEPTION();
}
Object* val = stack_pop();
scope->set_local(state, index, val);
stack_push(val);
}
end
# [Description]
# Checks if the argument specified by the operand _index_ was passed to
# the current invocation. If so, push true, otherwise false.
# [Notes]
# Arguments are specified via a zero-index, so the first argument is 0.
instruction passed_arg(index) [ -- boolean ]
if(!call_frame->arguments) {
Exception::internal_error(state, call_frame,
"no arguments object");
RUN_EXCEPTION();
}
if(index < (int)call_frame->arguments->total() - vmm->post_args) {
stack_push(Qtrue);
} else {
stack_push(Qfalse);
}
end
section "Manipulate exceptions"
# [Description]
# Pushes the current exception onto the stack, so that it can be used for
# some purpose, such as checking the exception type, setting an exception
# variable in a rescue clause, etc.
# [See Also]
# raise_exc
# [Example]
# begin
# foo = BAR # BAR is not defined
# rescue NameError # push_exception used to check type of exception (via ===)
# puts "No BAR"
# end
instruction push_current_exception() [ -- exception ]
stack_push(state->vm()->thread_state()->current_exception());
end
# [Description]
# Clears any exceptions from the current thread.
instruction clear_exception() [ -- ]
state->vm()->thread_state()->clear_raise();
end
# [Description]
# Package up the current exception state into an object and push it. This
# is used to preserve the exception state around code that might mutate it.
# For instance, when handling an ensure while an exception is being raised
instruction push_exception_state() [ -- exc_state ]
stack_push(state->vm()->thread_state()->state_as_object(state));
end
# [Description]
# Pops a value off the stack and set the threads exception state from it.
# This instruction is only to be used with a value pushed on the stack
# by the push_exception_state instruction.
# [See Also]
# push_exception_state
instruction restore_exception_state() [ exception -- ]
Object* top = stack_pop();
if(top->nil_p()) {
state->vm()->thread_state()->clear();
} else {
state->vm()->thread_state()->set_state(state, top);
}
end
# [Description]
# Raises an exception
#
# Pops a value off the stack and make it the current exception.
# If the value is not an instance of Exception, a TypeError is raised.
instruction raise_exc() [ value -- ] => raise
flush_ip();
Object* t1 = stack_pop();
state->raise_exception(as<Exception>(t1));
RUN_EXCEPTION();
end
# [Description]
# Register an unwind handler
#
# Registers what to happen when an exception wants to unwind through the
# current invocation. Operand _ip_ specifies where to set the instruction
# pointer if used. Operand _type_ is either 0 for if the value should be
# used in rescue style (not run when unwinding because of a return caused by
# `raise_return`) or 1 for ensure style (always used). The registrations are
# nested within the current invocation and are automatically removed from
# the registry when they are used. The `pop_unwind` instruction can be used
# to remove an unused registration.
# [Notes]
# The registration also contains the value of the stack depth when
# created. If the registration is used, then the stack depth is
# restored to the value contained in the registration
# [See Also]
# pop_unwind
instruction setup_unwind(ip type) [ -- ] => handler
interp_assert(current_unwind < kMaxUnwindInfos);
UnwindInfo& info = unwinds[current_unwind++];
info.target_ip = ip;
info.stack_depth = stack_calculate_sp();
info.type = (UnwindType)type;
end
# [Description]
# Remove the next unused unwind registration from the current invocation.
# This instruction is paired with `setup_unwind` to remove registrations
# when control exits a section of code that registered a handler but didn't
# use it. For example, exiting a begin that had a rescue expression.
# [See Also]
# setup_unwind
instruction pop_unwind() [ -- ]
if(current_unwind <= 0) {
Exception::internal_error(state, call_frame, "unbalanced pop_unwind");
RUN_EXCEPTION();
}
--current_unwind;
end
# [Description]
# Cause the toplevel enclosing scope to return
#
# Only used in a block, pop a value from the stack and raise a special
# internal exception and begin unwinding the stack. The toplevel method
# scope will rescue the exception and return the value.
# [See Also]
# ret
instruction raise_return() [ value -- value ] => raise
flush_ip();
if(!(call_frame->flags & CallFrame::cIsLambda) &&
!call_frame->scope_still_valid(call_frame->top_scope(state))) {
Exception* exc = Exception::make_exception(state, G(jump_error), "unexpected return");
exc->locations(state, Location::from_call_stack(state, call_frame));
state->raise_exception(exc);
} else {
if(call_frame->flags & CallFrame::cIsLambda) {
state->vm()->thread_state()->raise_return(stack_top(), call_frame->promote_scope(state));
} else {
state->vm()->thread_state()->raise_return(stack_top(), call_frame->top_scope(state));
}
}
RUN_EXCEPTION();
end
# [Description]
# Return from a scope but run ensures first
#
# A one use instruction, used only in a method toplevel within a begin
# that has an ensure. Use the same internal exception as `raise_return`
# which will coax the ensure registration to run.
# [See Also]
# ret
# raise_return
instruction ensure_return() [ value -- value ] => raise
flush_ip();
state->vm()->thread_state()->raise_return(stack_top(), call_frame->promote_scope(state));
RUN_EXCEPTION();
end
# [Description]
# Cause the method that yielded the current block to return. Used to
# implement the `break` keyword in a block.
instruction raise_break() [ value -- value ] => raise
flush_ip();
if(call_frame->flags & CallFrame::cIsLambda) {
return stack_top();
} else if(call_frame->scope_still_valid(call_frame->scope->parent())) {
state->vm()->thread_state()->raise_break(stack_top(), call_frame->scope->parent());
} else {
Exception* exc = Exception::make_exception(state, G(jump_error), "attempted to break to exited method");
exc->locations(state, Location::from_call_stack(state, call_frame));
state->raise_exception(exc);
}
RUN_EXCEPTION();
end
# [Description]
# Continue unwinding the stack with the current exception. Verify that there
# is a current exception, then begin the unwinding process again.
instruction reraise() [ -- ] => raise
interp_assert(state->vm()->thread_state()->raise_reason() != cNone);
RUN_EXCEPTION();
end
section "Manipulate arrays"
# [Description]
# Create an array and populate with values on the stack
#
# Creates a new array, populating its contents by remove the number of
# values specified by operand _count_ and putting them into the array in the
# order they were on the stack. The resulting array is pushed onto the
# stack.
# [Stack Before]
# valueN
# ...
# value2
# value1
# ...
# [Stack After]
# [value1, value2, ..., valueN]
# ...
instruction make_array(count) [ +count -- array ]
Object* t2;
Array* ary = Array::create(state, count);
#ifdef RBX_ALLOC_TRACKING
if(unlikely(state->vm()->allocation_tracking())) {
ary->setup_allocation_site(state, call_frame);
}
#endif
int j = count - 1;
for(; j >= 0; j--) {
t2 = stack_pop();
ary->set(state, j, t2);
}
stack_push(ary);
end
# [Description]
# Removes the object on the top of the stack, and:
#
# 1. If the input is a tuple, a new array object is created based on the
# tuple data.
# 2. If the input is an array, it is unmodified.
# 3. If in 1.9 mode and the input is nil, an empty Array is returned
#
# If the input is any other type, call `Array.coerce_into_array(value)`.
# If the return value of the method call is an `Array`, make it the result.
# Otherwise make the result an 1 element `Array` contain the original value.
#
# The resulting array is then pushed back onto the stack.
instruction cast_array() [ value -- array ]
// Use stack_top and not stack_pop because we may need
// to preserve and reread the value from the stack below.
Object* t1 = stack_top();
if(!LANGUAGE_18_ENABLED(state) && t1->nil_p()) {
t1 = Array::create(state, 0);
} else if(Tuple* tup = try_as<Tuple>(t1)) {
t1 = Array::from_tuple(state, tup);
} else if(!kind_of<Array>(t1)) {
Object* recv = G(array);
Arguments args(G(sym_coerce_into_array), recv, 1, &t1);
Dispatch dis(G(sym_coerce_into_array));
Object* res = dis.send(state, call_frame, args);
// If the send still doesn't produce an array, wrap
// the value in one.
if(res && !kind_of<Array>(res)) {
Array* ary = Array::create(state, 1);
// Don't read t1 here, it's not GC safe because we called
// a method.
ary->set(state, 0, stack_top());
t1 = ary;
} else {
t1 = res;
}
}
(void)stack_pop(); // Remove original value
CHECK_AND_PUSH(t1);
end
# [Description]
# Pops an array off the top of the stack. If the array is empty, it is
# pushed back onto the stack, followed by `nil`.
#
# Otherwise, the array is shifted, then pushed back onto the stack,
# followed by the object that was shifted from the front of the array.
# [Stack Before]
# [value1, value2, ..., valueN]
# ...
# [Stack After]
# value1
# [value2, ..., valueN]
# ...
instruction shift_array() [ array -- array value ]
Array* array = as<Array>(stack_pop());
size_t size = (size_t)array->size();
if(size == 0) {
stack_push(array);
stack_push(Qnil);
} else {
size_t j = size - 1;
Object* shifted_value = array->get(state, 0);
Array* smaller_array = Array::create(state, j);
for(size_t i = 0; i < j; i++) {
smaller_array->set(state, i, array->get(state, i+1));
}
stack_push(smaller_array);
stack_push(shifted_value);
}
end
section "Manipulate instance variables"
# [Description]
# Pops a value off the stack, and uses it to set the value of the instance
# variable identifies by the literal specified by operand _index_. The
# value popped off the stack is then pushed back on again.
instruction set_ivar(index) [ value -- value ]
if(RTEST(call_frame->self()->frozen_p(state))) {
Exception::frozen_error(state, call_frame);
RUN_EXCEPTION();
}
Symbol* sym = as<Symbol>(call_frame->cm->literals()->at(state, index));
call_frame->self()->set_ivar(state, sym, stack_top());
end
# [Description]
# Pushes the instance variable identified by _index_ onto the stack.
instruction push_ivar(index) [ -- value ]
Symbol* sym = as<Symbol>(call_frame->cm->literals()->at(state, index));
Object* ret = call_frame->self()->get_ivar(state, sym);
CHECK_AND_PUSH(ret);
end
section "Manipulate constants"
# [Description]
# Locates the constant indicated by the operand _literal_ from the current
# context, and pushes it onto the stack. If the constant cannot be found in
# the current context, nothing is pushed onto the stack, and a NameError
# exception is raised.
# [Example]
# engine = RUBY_ENGINE # RUBY_ENGINE is a constant defined by Rubinius
instruction push_const(literal) [ -- constant ]
bool found;
Symbol* sym = as<Symbol>(call_frame->cm->literals()->at(state, literal));
Object* res = Helpers::const_get(state, call_frame, sym, &found);
if(!found) {
flush_ip();
res = Helpers::const_missing(state, sym, call_frame);
} else if(Autoload* autoload = try_as<Autoload>(res)) {
flush_ip();
res = autoload->resolve(state, call_frame);
}
CHECK_AND_PUSH(res);
end
# [Description]
# Pops an object off the stack, and uses value to set a constant named
# by the literal _index_. The value is pushed back onto the stack.
instruction set_const(index) [ value -- value ]
Symbol* sym = as<Symbol>(call_frame->cm->literals()->at(state, index));
call_frame->static_scope()->module()->set_const(state, sym, stack_top());
end
# [Description]
# Pop a value from the literals table specified by the operand _index_ and
# use it as the value of a constant named inside a Module object popped from
# the stack. The _value_ is pushed back on the stack.
# [Stack Before]
# value
# module
# ...
# [Stack After]
# value
# ...
instruction set_const_at(index) [ value module -- value ]
Symbol* sym = as<Symbol>(call_frame->cm->literals()->at(state, index));
Object* val = stack_pop();
Module* under = as<Module>(stack_pop());
under->set_const(state, sym, val);
stack_push(val);
end
# [Description]
# Pops _module_ off the stack, and searches within its namespace for the
# constant named by the literal specified by the operand _index_. If found,
# it is pushed onto the stack; otherwise, nothing is pushed onto the stack,
# and a `NameError` exception is raised.
# [Example]
# str = "abc"
# enum = Enumerable::Enumerator(str, :each_byte)
instruction find_const(index) [ module -- constant ]
bool found;
Module* under = as<Module>(stack_pop());
Symbol* sym = as<Symbol>(call_frame->cm->literals()->at(state, index));
Object* res = Helpers::const_get_under(state, under, sym, &found);
if(!found) {
flush_ip();
res = Helpers::const_missing_under(state, under, sym, call_frame);
} else if(Autoload* autoload = try_as<Autoload>(res)) {
flush_ip();
res = autoload->resolve(state, call_frame);
}
CHECK_AND_PUSH(res);
end
# [Description]
# Pushes the top-level global object that represents the top-level namespace
# for constants. Used to find constants relative to the toplevel. In Ruby,
# this is the class `Object`.
instruction push_cpath_top() [ -- constant ]
stack_push(G(object));
end
# [Description]
# Pushes a constant onto the stack. Caches the lookup to provide faster
# future lookup. This instruction is normally emitted only by the Generator.
# [See Also]
# push_const
# [Example]
# engine = RUBY_ENGINE # RUBY_ENGINE is a constant defined by Rubinius
instruction push_const_fast(literal association) [ -- constant ]
bool found;
Object* res = 0;
Object* val = call_frame->cm->literals()->at(state, association);
StaticScope* sc = call_frame->static_scope();
// See if the cache is present, if so, validate it and use the value
GlobalCacheEntry* cache;
if((cache = try_as<GlobalCacheEntry>(val)) != NULL) {
if(cache->valid_p(state, sc)) {
res = cache->value();
} else {
Symbol* sym = as<Symbol>(call_frame->cm->literals()->at(state, literal));
flush_ip();
res = Helpers::const_get(state, call_frame, sym, &found);
if(found) {
cache->update(state, res, sc);
}
}
} else {
flush_ip();
Symbol* sym = as<Symbol>(call_frame->cm->literals()->at(state, literal));
res = Helpers::const_get(state, call_frame, sym, &found);
if(found) {
if(Autoload* autoload = try_as<Autoload>(res)) {
flush_ip();
res = autoload->resolve(state, call_frame);
if(res) {
// resolve might run the GC, refetch static_scope
sc = call_frame->static_scope();
cache = GlobalCacheEntry::create(state, res, sc);
call_frame->cm->literals()->put(state, association, cache);
}
} else {
cache = GlobalCacheEntry::create(state, res, sc);
call_frame->cm->literals()->put(state, association, cache);
}
} else {
res = Helpers::const_missing(state, sym, call_frame);
}
}
CHECK_AND_PUSH(res);
end
section "Send messages"
# [Description]
# The call flags on the current execution context are set to the opcode
# argument _flags_.
# [Notes]
# Currently this only has one use, which is that send_stack_with_splat
# checks if flags is set to CALL_FLAG_CONCAT which indicates that
# the splat represents arguments at the beginning rather than the end.
instruction set_call_flags(flags) [ -- ]
SET_CALL_FLAGS(flags);
end
# [Description]
# Indicate that the next send is allowed to see `private` methods.
instruction allow_private() [ -- ]
SET_ALLOW_PRIVATE(true);
end
# [Description]
# Pops a _receiver_ object off the top of the stack and sends it the
# message specified by the operand _literal_ with zero arguments.
#
# When the method returns, the return value is pushed on the stack.
# [See Also]
# send_stack
# [Notes]
# This form of send is for methods that take no arguments.
instruction send_method(literal) [ receiver -- value ] => send
flush_ip();
Object* recv = stack_top();
InlineCache* cache = reinterpret_cast<InlineCache*>(literal);
SET_ALLOW_PRIVATE(false);
Arguments args(cache->name, recv, Qnil, 0, 0);
Object* ret = cache->execute(state, call_frame, args);
(void)stack_pop();
CHECK_AND_PUSH(ret);
end
# [Description]
# Sends a message with arguments on the stack
#
# Pops the _receiver_ of the message off the stack and sends the message
# specified by the operand _literal_ with _count_ arguments. The arguments
# are removed from the stack also.
#
# When the method returns, the return value is pushed on the stack.
# [Stack Before]
# argN
# ...
# arg2
# arg1
# receiver
# [Stack After]
# value
# ...
# [See Also]
# send_stack_with_block
# [Notes]
# This opcode does not pass a block to the receiver; see
# `send_stack_with_block` for the equivalent op code used when a block is to
# be passed.
instruction send_stack(literal count) [ receiver +count -- value ] => send
flush_ip();
Object* recv = stack_back(count);
InlineCache* cache = reinterpret_cast<InlineCache*>(literal);
SET_ALLOW_PRIVATE(false);
Arguments args(cache->name, recv, Qnil, count,
stack_back_position(count));
Object* ret = cache->execute(state, call_frame, args);
stack_clear(count + 1);
CHECK_AND_PUSH(ret);
end
# [Description]
# Sends a message with arguments and a block on the stack
#
# Pops the _receiver_ of the message off the stack and sends the message
# specified by the operand _literal_ with _count_ arguments. The arguments
# are removed from the stack also. A value that represents the block to pass
# on is popped off the stack after the normal arguments.
#
# When the method returns, the return value will be on top of the stack.
# [Stack Before]
# block
# argN
# ...
# arg2
# arg1
# receiver
# [Stack After]
# retval
# ...
# [See Also]
# send_stack
# [Notes]
# This opcode passes a block to the receiver; see `send_stack` for the
# equivalent op code used when no block is to be passed.
instruction send_stack_with_block(literal count) [ block receiver +count -- value ] => send
flush_ip();
Object* block = stack_pop();
Object* recv = stack_back(count);
InlineCache* cache = reinterpret_cast<InlineCache*>(literal);