/
IRBytecodeAdapter.java
453 lines (394 loc) · 15.2 KB
/
IRBytecodeAdapter.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
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
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.jruby.ir.targets;
import com.headius.invokebinder.Signature;
import org.jruby.RubyModule;
import org.jruby.compiler.impl.SkinnyMethodAdapter;
import org.jruby.ir.instructions.ClosureAcceptingInstr;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.ir.targets.indy.IndyArgumentsCompiler;
import org.jruby.ir.targets.indy.IndyBlockCompiler;
import org.jruby.ir.targets.indy.IndyBranchCompiler;
import org.jruby.ir.targets.indy.IndyCheckpointCompiler;
import org.jruby.ir.targets.indy.IndyConstantCompiler;
import org.jruby.ir.targets.indy.IndyDynamicValueCompiler;
import org.jruby.ir.targets.indy.IndyGlobalVariableCompiler;
import org.jruby.ir.targets.indy.IndyInstanceVariableCompiler;
import org.jruby.ir.targets.indy.IndyInvocationCompiler;
import org.jruby.ir.targets.indy.IndyValueCompiler;
import org.jruby.ir.targets.indy.IndyYieldCompiler;
import org.jruby.ir.targets.simple.NormalArgumentsCompiler;
import org.jruby.ir.targets.simple.NormalBlockCompiler;
import org.jruby.ir.targets.simple.NormalBranchCompiler;
import org.jruby.ir.targets.simple.NormalCheckpointCompiler;
import org.jruby.ir.targets.simple.NormalConstantCompiler;
import org.jruby.ir.targets.simple.NormalDynamicValueCompiler;
import org.jruby.ir.targets.simple.NormalGlobalVariableCompiler;
import org.jruby.ir.targets.simple.NormalInstanceVariableCompiler;
import org.jruby.ir.targets.simple.NormalInvocationCompiler;
import org.jruby.ir.targets.simple.NormalValueCompiler;
import org.jruby.ir.targets.simple.NormalYieldCompiler;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Binding;
import org.jruby.runtime.Block;
import org.jruby.runtime.Frame;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.JavaNameMangler;
import org.jruby.util.collections.IntHashMap;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;
import java.lang.invoke.MethodType;
import java.util.HashMap;
import java.util.Map;
import static org.jruby.util.CodegenUtils.*;
/**
*
* @author headius
*/
public class IRBytecodeAdapter {
public static final int MAX_ARGUMENTS = 250;
public IRBytecodeAdapter(BytecodeMode bytecodeMode, SkinnyMethodAdapter adapter, Signature signature, ClassData classData) {
this.adapter = adapter;
this.signature = signature;
this.classData = classData;
switch (bytecodeMode) {
case INDY:
this.valueCompiler = new IndyValueCompiler(this);
this.dynamicValueCompiler = new IndyDynamicValueCompiler(this);
this.invocationCompiler = new IndyInvocationCompiler(this);
this.branchCompiler = new IndyBranchCompiler(this);
this.checkpointCompiler = new IndyCheckpointCompiler(this);
this.instanceVariableCompiler = new IndyInstanceVariableCompiler(this);
this.globalVariableCompiler = new IndyGlobalVariableCompiler(this);
this.yieldCompiler = new IndyYieldCompiler(this);
this.blockCompiler = new IndyBlockCompiler(this);
this.argumentsCompiler = new IndyArgumentsCompiler(this);
this.constantCompiler = new IndyConstantCompiler(this);
break;
case MIXED:
this.valueCompiler = new IndyValueCompiler(this);
this.dynamicValueCompiler = new NormalDynamicValueCompiler(this);
this.invocationCompiler = new NormalInvocationCompiler(this);
this.branchCompiler = new NormalBranchCompiler(this);
this.checkpointCompiler = new NormalCheckpointCompiler(this);
this.instanceVariableCompiler = new NormalInstanceVariableCompiler(this);
this.globalVariableCompiler = new IndyGlobalVariableCompiler(this);
this.yieldCompiler = new IndyYieldCompiler(this);
this.blockCompiler = new IndyBlockCompiler(this);
this.argumentsCompiler = new NormalArgumentsCompiler(this);
this.constantCompiler = new IndyConstantCompiler(this);
break;
case AOT:
this.valueCompiler = new NormalValueCompiler(this);
this.dynamicValueCompiler = new NormalDynamicValueCompiler(this);
this.invocationCompiler = new NormalInvocationCompiler(this);
this.branchCompiler = new NormalBranchCompiler(this);
this.checkpointCompiler = new NormalCheckpointCompiler(this);
this.instanceVariableCompiler = new NormalInstanceVariableCompiler(this);
this.globalVariableCompiler = new NormalGlobalVariableCompiler(this);
this.yieldCompiler = new NormalYieldCompiler(this);
this.blockCompiler = new NormalBlockCompiler(this);
this.argumentsCompiler = new NormalArgumentsCompiler(this);
this.constantCompiler = new NormalConstantCompiler(this);
break;
default:
throw new RuntimeException("unknown compile mode: " + bytecodeMode);
}
}
/**
* Get the compiler for constant Ruby values.
*
* @return the value compiler
*/
public ValueCompiler getValueCompiler() {
return valueCompiler;
}
/**
* Get the compiler for dynamic values.
*
* @return the dynamic value compiler
*/
public DynamicValueCompiler getDynamicValueCompiler() {
return dynamicValueCompiler;
}
/**
* Get the compiler for invocations.
*
* @return the invocation compiler
*/
public InvocationCompiler getInvocationCompiler() {
return invocationCompiler;
}
/**
* Get the compiler for dynamic branches.
*
* @return the branch compiler
*/
public BranchCompiler getBranchCompiler() {
return branchCompiler;
}
/**
* Checkpoint compiler.
*
* @return the checkpoint compiler
*/
public CheckpointCompiler getCheckpointCompiler() {
return checkpointCompiler;
}
/**
* Get the compiler for Ruby constant lookups.
*
* @return the constant compiler
*/
public ConstantCompiler getConstantCompiler() {
return constantCompiler;
}
/**
* Instance variable compiler.
*
* @return the instance variable compiler
*/
public InstanceVariableCompiler getInstanceVariableCompiler() {
return instanceVariableCompiler;
}
/**
* Global variable compiler.
*
* @return the global variable compiler
*/
public GlobalVariableCompiler getGlobalVariableCompiler() {
return globalVariableCompiler;
}
/**
* Block yielding compiler.
*
* @return the yield compiler
*/
public YieldCompiler getYieldCompiler() {
return yieldCompiler;
}
/**
* Block construction compiler.
*
* @return the block compiler
*/
public BlockCompiler getBlockCompiler() {
return blockCompiler;
}
/**
* The compiler for argument processing or preparation.
*
* @return the arguments compiler
*/
public ArgumentsCompiler getArgumentsCompiler() {
return argumentsCompiler;
}
public static void buildArrayFromLocals(SkinnyMethodAdapter adapter2, int base, int arity) {
if (arity == 0) {
adapter2.getstatic(p(IRubyObject.class), "NULL_ARRAY", ci(IRubyObject[].class));
return;
}
adapter2.pushInt(arity);
adapter2.invokestatic(p(Helpers.class), "anewarrayIRubyObjects", sig(IRubyObject[].class, int.class));
for (int i = 0; i < arity; ) {
int j = 0;
while (i + j < arity && j < Helpers.MAX_SPECIFIC_ARITY_OBJECT_ARRAY) {
adapter2.aload(base + i + j);
j++;
}
adapter2.pushInt(i);
adapter2.invokestatic(p(Helpers.class), "aastoreIRubyObjects", sig(IRubyObject[].class, params(IRubyObject[].class, IRubyObject.class, j, int.class)));
i += j;
}
}
public String getUniqueSiteName(String name) {
return "invokeOther" + getClassData().cacheFieldCount.getAndIncrement() + ":" + JavaNameMangler.mangleMethodName(name);
}
public ClassData getClassData() {
return classData;
}
public void startMethod() {
adapter.start();
}
public void endMethod() {
adapter.end(new Runnable() {
public void run() {
for (IntHashMap.Entry<Type> entry : variableTypes.entrySet()) {
final int i = entry.getKey();
String name = variableNames.get(i);
adapter.local(i, name, entry.getValue());
}
}
});
}
public void loadLocal(int i) {
adapter.aload(i);
}
public void loadContext() {
adapter.aload(signature.argOffset("context"));
}
public void loadSelfBlock() {
int selfBlockOffset = signature.argOffset(JVMVisitor.SELF_BLOCK_NAME);
if (selfBlockOffset == -1) {
adapter.aconst_null();
} else {
adapter.aload(selfBlockOffset);
}
}
public void loadStaticScope() {
adapter.aload(signature.argOffset("scope"));
}
public void loadSelf() {
adapter.aload(signature.argOffset("self"));
}
public void loadArgs() {
adapter.aload(signature.argOffset("args"));
}
public void loadBlock() {
adapter.aload(signature.argOffset(JVMVisitor.BLOCK_ARG_NAME));
}
public void loadFrameClass() {
int superNameOffset = signature.argOffset(JVMVisitor.SUPER_NAME_NAME);
if (superNameOffset == -1) {
// load from self block
loadSelfBlock();
adapter.invokevirtual(p(Block.class), "getBinding", sig(Binding.class));
adapter.invokevirtual(p(Binding.class), "getFrame", sig(Frame.class));
adapter.invokevirtual(p(Frame.class), "getKlazz", sig(RubyModule.class));
} else {
// when present, should be second-to-last element in signature
adapter.aload(signature.argCount() - 2);
}
}
public void loadFrameName() {
int superNameOffset = signature.argOffset(JVMVisitor.SUPER_NAME_NAME);
if (superNameOffset == -1) {
// load from self block
loadSelfBlock();
adapter.invokevirtual(p(Block.class), "getBinding", sig(Binding.class));
adapter.invokevirtual(p(Binding.class), "getMethod", sig(String.class));
} else {
adapter.aload(superNameOffset);
}
}
public void storeSelf() {
adapter.astore(signature.argOffset("self"));
}
public void storeArgs() {
adapter.astore(signature.argOffset("args"));
}
public void storeLocal(int i) {
adapter.astore(i);
}
public void invokeVirtual(Type type, Method method) {
adapter.invokevirtual(type.getInternalName(), method.getName(), method.getDescriptor());
}
public void invokeStatic(Type type, Method method) {
adapter.invokestatic(type.getInternalName(), method.getName(), method.getDescriptor());
}
public void invokeHelper(String name, String sig) {
adapter.invokestatic(p(Helpers.class), name, sig);
}
public void invokeHelper(String name, Class... x) {
adapter.invokestatic(p(Helpers.class), name, sig(x));
}
public void invokeIRHelper(String name, String sig) {
adapter.invokestatic(p(IRRuntimeHelpers.class), name, sig);
}
public void goTo(org.objectweb.asm.Label label) {
adapter.go_to(label);
}
public void pushHandle(Handle handle) {
adapter.getMethodVisitor().visitLdcInsn(handle);
}
public void mark(org.objectweb.asm.Label label) {
adapter.label(label);
}
public void returnValue() {
adapter.areturn();
}
public int newLocal(String name, Type type) {
int index = variableCount++;
if (type == Type.DOUBLE_TYPE || type == Type.LONG_TYPE) {
variableCount++;
}
variableTypes.put(index, type);
variableNames.put(index, name);
return index;
}
public org.objectweb.asm.Label newLabel() {
return new org.objectweb.asm.Label();
}
public void getStaticScope(String field) {
adapter.getstatic(classData.clsName, field, ci(StaticScope.class));
adapter.dup();
Label after = newLabel();
adapter.ifnonnull(after);
adapter.pop();
adapter.ldc(classData.visitor.staticScopeDescriptorMap.get(field));
loadStaticScope();
invokeHelper("restoreScope", StaticScope.class, String.class, StaticScope.class);
adapter.dup();
adapter.putstatic(classData.clsName, field, ci(StaticScope.class));
adapter.label(after);
}
public void outline(String name, MethodType type, Runnable body) {
SkinnyMethodAdapter oldAdapter = adapter;
adapter = new SkinnyMethodAdapter(
oldAdapter.getClassVisitor(),
Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
name,
sig(type),
null,
null);
body.run();
adapter.end();
adapter = oldAdapter;
}
public enum BlockPassType {
NONE(false, false),
GIVEN(true, false),
LITERAL(true, true);
private final boolean given;
private final boolean literal;
BlockPassType(boolean given, boolean literal) {
this.given = given;
this.literal = literal;
}
public boolean given() {
return given;
}
public boolean literal() {
return literal;
}
public static BlockPassType fromIR(ClosureAcceptingInstr callInstr) {
Operand closure = callInstr.getClosureArg();
return closure != null ? ( callInstr.hasLiteralClosure() ? BlockPassType.LITERAL : BlockPassType.GIVEN) : BlockPassType.NONE;
}
}
public SkinnyMethodAdapter adapter;
private int variableCount = 0;
private final IntHashMap<Type> variableTypes = new IntHashMap<>();
private final IntHashMap<String> variableNames = new IntHashMap<>();
protected final Signature signature;
protected final ClassData classData;
protected final ValueCompiler valueCompiler;
protected final DynamicValueCompiler dynamicValueCompiler;
protected final InvocationCompiler invocationCompiler;
protected final BranchCompiler branchCompiler;
protected final CheckpointCompiler checkpointCompiler;
protected final ConstantCompiler constantCompiler;
protected final InstanceVariableCompiler instanceVariableCompiler;
protected final GlobalVariableCompiler globalVariableCompiler;
protected final YieldCompiler yieldCompiler;
protected final BlockCompiler blockCompiler;
protected final ArgumentsCompiler argumentsCompiler;
public int ipc = 0; // counter for dumping instr index when in DEBUG
}