-
Notifications
You must be signed in to change notification settings - Fork 914
/
IRBlockBody.java
212 lines (172 loc) · 8.16 KB
/
IRBlockBody.java
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
package org.jruby.runtime;
import org.jruby.RubyArray;
import org.jruby.ir.IRClosure;
import org.jruby.ir.IRScope;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.builtin.IRubyObject;
import static org.jruby.api.Error.typeError;
public abstract class IRBlockBody extends ContextAwareBlockBody {
protected final String fileName;
protected final int lineNumber;
// For interpreter IR
public IRBlockBody(IRScope closure, Signature signature) {
// ThreadLocal not set by default to avoid having many thread-local values initialized
// servers such as Tomcat tend to do thread-local checks when un-deploying apps,
// for JRuby leads to 100s of SEVERE warnings for a mid-size (booted) Rails app
this(closure.getStaticScope(), closure.getFile(), closure.getLine(), signature);
}
// For Compiled.
public IRBlockBody(StaticScope scope, String file, int line, Signature signature) {
// ThreadLocal not set by default to avoid having many thread-local values initialized
// servers such as Tomcat tend to do thread-local checks when un-deploying apps,
// for JRuby leads to 100s of SEVERE warnings for a mid-size (booted) Rails app
super(scope, signature);
this.fileName = file;
this.lineNumber = line;
}
@Override
public abstract boolean canCallDirect();
@Override
public IRubyObject call(ThreadContext context, Block block) {
return call(context, block, IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);
}
@Override
public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0) {
return call(context, block, new IRubyObject[] {arg0}, Block.NULL_BLOCK);
}
@Override
public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
return call(context, block, new IRubyObject[] {arg0, arg1}, Block.NULL_BLOCK);
}
@Override
public IRubyObject call(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return call(context, block, new IRubyObject[]{arg0, arg1, arg2}, Block.NULL_BLOCK);
}
@Override
public IRubyObject call(ThreadContext context, Block block, IRubyObject[] args) {
return call(context, block, args, Block.NULL_BLOCK);
}
@Override
public IRubyObject call(ThreadContext context, Block block, IRubyObject[] args, Block blockArg) {
if (canCallDirect()) {
return callDirect(context, block, args, blockArg);
}
return commonYieldPath(context, block, prepareArgumentsForCall(context, args, block.type), null, blockArg);
}
@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block) {
if (canCallDirect()) {
return yieldDirect(context, block, null, null);
} else {
IRubyObject[] args = IRubyObject.NULL_ARRAY;
if (block.type == Block.Type.LAMBDA) signature.checkArity(context.runtime, args);
return commonYieldPath(context, block, args, null, Block.NULL_BLOCK);
}
}
@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0) {
IRubyObject[] args;
if (canCallDirect()) {
if (arg0 instanceof RubyArray) { // Unwrap the array arg
args = IRRuntimeHelpers.convertValueIntoArgArray(context, (RubyArray) arg0, signature);
} else {
args = new IRubyObject[] { arg0 };
}
return yieldDirect(context, block, args, null);
} else {
if (arg0 instanceof RubyArray) { // Unwrap the array arg
args = IRRuntimeHelpers.convertValueIntoArgArray(context, (RubyArray) arg0, signature);
// FIXME: arity error is against new args but actual error shows arity of original args.
if (block.type == Block.Type.LAMBDA) signature.checkArity(context.runtime, args);
return commonYieldPath(context, block, args, null, Block.NULL_BLOCK);
} else {
return doYield(context, block, arg0);
}
}
}
private IRubyObject yieldSpecificMultiArgsCommon(ThreadContext context, Block block, IRubyObject... args) {
if (signature.isOneArgument()) {
args = new IRubyObject[] { RubyArray.newArrayMayCopy(context.runtime, args) };
}
if (canCallDirect()) return yieldDirect(context, block, args, null);
if (signature.arityValue() == 0) {
args = IRubyObject.NULL_ARRAY; // discard args
}
if (block.type == Block.Type.LAMBDA) signature.checkArity(context.runtime, args);
return commonYieldPath(context, block, args, null, Block.NULL_BLOCK);
}
@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1) {
return yieldSpecificMultiArgsCommon(context, block, arg0, arg1);
}
@Override
public IRubyObject yieldSpecific(ThreadContext context, Block block, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
return yieldSpecificMultiArgsCommon(context, block, arg0, arg1, arg2);
}
public static IRubyObject[] toAry(ThreadContext context, IRubyObject value) {
final IRubyObject ary = Helpers.aryToAry(context, value);
if (ary instanceof RubyArray) return ((RubyArray) ary).toJavaArray();
if (ary == context.nil) return new IRubyObject[] { value };
throw typeError(context, "", value, "#to_ary should return Array");
}
protected IRubyObject doYieldLambda(ThreadContext context, Block block, IRubyObject value) {
// Lambda does not splat arrays even if a rest arg is present when it wants a single parameter filled.
IRubyObject[] args;
if (value == null) { // no args case from BlockBody.yieldSpecific
args = IRubyObject.NULL_ARRAY;
} else if (signature.required() == 1 || signature.arityValue() == -1) {
args = new IRubyObject[] { value };
} else {
args = toAry(context, value);
}
signature.checkArity(context.runtime, args);
return commonYieldPath(context, block, args, null, Block.NULL_BLOCK);
}
@Override
public IRubyObject doYield(ThreadContext context, Block block, IRubyObject value) {
if (block.type == Block.Type.LAMBDA) return doYieldLambda(context, block, value);
IRubyObject[] args;
if (value == null) { // no args case from BlockBody.yieldSpecific
args = IRubyObject.NULL_ARRAY;
} else if (!signature.isSpreadable()) {
args = new IRubyObject[] { value };
} else {
args = toAry(context, value);
}
return commonYieldPath(context, block, args, null, Block.NULL_BLOCK);
}
@Override
public IRubyObject doYield(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self) {
if (block.type == Block.Type.LAMBDA) signature.checkArity(context.runtime, args);
return commonYieldPath(context, block, args, self, Block.NULL_BLOCK);
}
protected abstract IRubyObject commonYieldPath(ThreadContext context, Block block, IRubyObject[] args, IRubyObject self, Block blockArg) ;
static void postYield(ThreadContext context, InterpreterContext ic, Binding binding, Visibility oldVis, Frame prevFrame) {
// IMPORTANT: Do not clear eval-type in case this is reused in bindings!
// Ex: eval("...", foo.instance_eval { binding })
// The dyn-scope used for binding needs to have its eval-type set to INSTANCE_EVAL
binding.getFrame().setVisibility(oldVis);
if (ic.popDynScope()) {
context.postYield(binding, prevFrame);
} else {
context.postYieldNoScope(prevFrame);
}
}
public IRClosure getScope() {
return (IRClosure) scope.getIRScope();
}
@Override
public String getFile() {
return fileName;
}
@Override
public int getLine() {
return lineNumber;
}
@Override
public boolean isRubyBlock() {
return true;
}
}