Skip to content

Commit b837d29

Browse files
authored
Merge pull request jruby#9330 from enebo/sync_p
Sync with prism update so we can compile jruby-prism using 10.0
2 parents 43596a8 + f4bade2 commit b837d29

5 files changed

Lines changed: 128 additions & 65 deletions

File tree

core/src/main/java/org/jruby/RubyFile.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
import java.net.URL;
6868
import java.nio.channels.Channels;
6969
import java.nio.channels.FileChannel;
70+
import java.nio.channels.SeekableByteChannel;
7071
import java.nio.charset.StandardCharsets;
7172
import java.nio.file.*;
7273
import java.nio.file.attribute.FileTime;
@@ -237,6 +238,23 @@ public RubyFile(Ruby runtime, String path, InputStream in) {
237238
this.setPath(path);
238239
}
239240

241+
242+
private RubyFile(Ruby runtime, String path, SeekableByteChannel in) {
243+
super(runtime, runtime.getFile(), in);
244+
this.setPath(path);
245+
}
246+
247+
/**
248+
* Only used by parser (prism) so that we can get a File
249+
* @param runtime the runtime
250+
* @param path the path of DATA
251+
* @param in the channel to use
252+
* @return a Ruby file
253+
*/
254+
public static RubyFile DATAFile(Ruby runtime, String path, SeekableByteChannel in) {
255+
return new RubyFile(runtime, path, in);
256+
}
257+
240258
public RubyFile(Ruby runtime, String path, FileChannel channel) {
241259
super(runtime, runtime.getFile(), channel);
242260
this.setPath(path);

core/src/main/java/org/jruby/ir/builder/EnsureBlockInfo.java

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@
4949
* run the stack of ensure blocks in the right order.
5050
* ----------------------------------------------------------------------------------- */
5151
class EnsureBlockInfo {
52-
final Label regionStart;
53-
final Label start;
54-
final Label end;
55-
final Label dummyRescueBlockLabel;
52+
final Label start; // Beginning of this ensure region
53+
final Label bodyStart; // Beginning of any actual instrs in the ensure body (will not be emitted if none)
54+
final Label end; // End of this ensure region
55+
final Label ensureRescue; // Where exceptions in an ensure flow through
5656
Variable savedGlobalException;
5757
boolean needsBacktrace;
5858

@@ -65,13 +65,13 @@ class EnsureBlockInfo {
6565
// This ensure block's instructions
6666
final List<Instr> instrs;
6767

68-
public EnsureBlockInfo(IRScope s, IRLoop l, Label bodyRescuer) {
68+
public EnsureBlockInfo(IRScope s, IRLoop l, Label bodyRescuer, int sourceLine) {
6969
// this technically may be any block and not specifically rescue but for the sake of looking at the CFG
7070
// it is more or less a begin block with exception handling around it.
71-
regionStart = s.getNewLabel("BEGIN");
72-
start = s.getNewLabel("RESC_START");
73-
end = s.getNewLabel("AFTER_RESC");
74-
dummyRescueBlockLabel = s.getNewLabel("RESC_DUMMY");
71+
start = s.getNewLabel("ENSURE_BEGIN_@" + sourceLine);
72+
bodyStart = s.getNewLabel("ENSURE_BODY_:@" + sourceLine);
73+
end = s.getNewLabel("ENSURE_END_:@" + sourceLine);
74+
ensureRescue = s.getNewLabel("ENSURE_CATCH_@" + sourceLine);
7575
instrs = new ArrayList<>(4);
7676
savedGlobalException = null;
7777
innermostLoop = l;
@@ -87,10 +87,14 @@ public void addInstrAtBeginning(Instr i) {
8787
instrs.add(0, i);
8888
}
8989

90-
public void emitBody(IRBuilder b) {
91-
b.addInstr(new LabelInstr(start));
90+
/**
91+
* Emit the saved instrs for the ensure body into the current location in the provided builder.
92+
* @param builder
93+
*/
94+
public void emitEnsureBody(IRBuilder builder) {
95+
builder.addInstr(new LabelInstr(bodyStart));
9296
for (Instr i: instrs) {
93-
b.addInstr(i);
97+
builder.addInstr(i);
9498
}
9599
}
96100

@@ -106,20 +110,20 @@ public void cloneIntoHostScope(IRBuilder builder) {
106110
// there are no actual instrs pushed yet (but ebi has reserved a frame for it -- e.g. the rescue/ensure
107111
// the next is in). Since it is doing nothing we have nothing to clone. By skipping this we prevent
108112
// setting exception regions and simplify CFG construction.
109-
if (instrs.size() == 0) return;
113+
if (instrs.isEmpty()) return;
110114

111115
SimpleCloneInfo ii = new SimpleCloneInfo(builder.scope, true);
112116

113117
// Clone required labels.
114118
// During normal cloning below, labels not found in the rename map
115119
// are not cloned.
116-
ii.renameLabel(start);
120+
ii.renameLabel(bodyStart);
117121
for (Instr i: instrs) {
118122
if (i instanceof LabelInstr) ii.renameLabel(((LabelInstr)i).getLabel());
119123
}
120124

121125
// Clone instructions now
122-
builder.addInstr(new LabelInstr(ii.getRenamedLabel(start)));
126+
builder.addInstr(new LabelInstr(ii.getRenamedLabel(bodyStart)));
123127
builder.addInstr(new ExceptionRegionStartMarkerInstr(bodyRescuer));
124128
for (Instr instr: instrs) {
125129
Instr clonedInstr = instr.clone(ii);

core/src/main/java/org/jruby/ir/builder/IRBuilder.java

Lines changed: 81 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import org.jruby.RubySymbol;
99
import org.jruby.ast.IterNode;
1010
import org.jruby.ast.StrNode;
11-
import org.jruby.common.IRubyWarnings;
1211
import org.jruby.ext.coverage.CoverageData;
1312
import org.jruby.ir.IRClassBody;
1413
import org.jruby.ir.IRClosure;
@@ -167,44 +166,58 @@ public static InterpreterContext buildRoot(IRManager manager, ParseResult rootNo
167166
return manager.getBuilderFactory().topIRBuilder(manager, script, rootNode).buildRootInner(rootNode);
168167
}
169168

169+
protected int currentLine() {
170+
return lastProcessedLineNum;
171+
}
172+
173+
// FIXME: When legacy parser goes away this should be totally rewritten as prism represents the whole structure as one node vs
174+
// legacy which has ensure as the parent to any rescue.
170175
// FIXME: consider mod_rescue, rescue, and pure ensure as separate entries
171-
// Note: reference is only passed in via Prism on legacy this is desugared into AST.
172-
protected Operand buildEnsureInternal(U body, U elseNode, U[] exceptions, U rescueBody, X optRescue, boolean isModifier,
173-
U ensureNode, boolean isRescue, U reference) {
174-
// Save $!
175-
final Variable savedGlobalException = temp();
176-
addInstr(new GetGlobalVariableInstr(savedGlobalException, symbol("$!")));
177-
178-
// ------------ Build the body of the ensure block ------------
179-
//
176+
/**
177+
* @param body the main statements to be executed
178+
* @param elseNode else path in a begin
179+
* @param exceptions possible list of excpetions which will jump to rescueBody
180+
* @param rescueBody the statements executed if the matching exception(s) occurs
181+
* @param optRescue possible other rescue in the same begin
182+
* @param isModifier foo rescue bar
183+
* @param ensureNode ensure block
184+
* @param reference variable for the rescue (NameError => name)
185+
* @return return last operand of begin execution
186+
*
187+
* Note: reference is only passed in via Prism on legacy this is desugared into AST.
188+
*/
189+
protected Operand buildEnsureInternal(U body, U elseNode, U[] exceptions, U rescueBody, X optRescue,
190+
boolean isModifier, U ensureNode, boolean isRescue, U reference) {
191+
var savedGlobalException = addResultInstr(new GetGlobalVariableInstr(temp(), symbol("$!")));
192+
180193
// The ensure code is built first so that when the protected body is being built,
181194
// the ensure code can be cloned at break/next/return sites in the protected body.
195+
EnsureBlockInfo ebi = new EnsureBlockInfo(scope, getCurrentLoop(), activeRescuers.peek(), currentLine() + 1);
182196

183-
// Push a new ensure block node onto the stack of ensure bodies being built
184-
// The body's instructions are stashed and emitted later.
185-
EnsureBlockInfo ebi = new EnsureBlockInfo(scope, getCurrentLoop(), activeRescuers.peek());
197+
// Rescue will change $! but we need to restore $! later. prism: ensure and isRescue means it is both (we
198+
// call this method again below to rip those two things apart.
199+
if (isRescue && ensureNode == null) ebi.savedGlobalException = savedGlobalException;
186200

187-
// Record $! save var if we had a non-empty rescue node.
188-
// $! will be restored from it where required.
189-
if (isRescue) ebi.savedGlobalException = savedGlobalException;
190-
191-
ensureBodyBuildStack.push(ebi);
192-
Operand ensureRetVal = ensureNode == null ? nil() : build(ensureNode);
193-
ensureBodyBuildStack.pop();
201+
// Record body of ensure and push to ensure body stack if there is an actual ensure body.
202+
Operand ensureRetVal = processEnsureBody(ensureNode, ebi);
194203

195204
// ------------ Build the protected region ------------
196205
activeEnsureBlockStack.push(ebi);
197206

198207
// Start of protected region
199-
addInstr(new LabelInstr(ebi.regionStart));
200-
addInstr(new ExceptionRegionStartMarkerInstr(ebi.dummyRescueBlockLabel));
201-
activeRescuers.push(ebi.dummyRescueBlockLabel);
208+
addInstr(new LabelInstr(ebi.start));
209+
addInstr(new ExceptionRegionStartMarkerInstr(ebi.ensureRescue));
210+
activeRescuers.push(ebi.ensureRescue);
202211

203212
// Generate IR for code being protected
204-
Variable ensureExprValue = temp();
205213
Operand rv;
206214
if (isRescue) {
207-
rv = buildRescueInternal(body, elseNode, exceptions, rescueBody, optRescue, isModifier, ebi, reference);
215+
if (ensureNode != null) {
216+
// both were passed in via Prism. Let's pretend they are nested like legacy where ensures enclose the rescue.
217+
rv = buildEnsureInternal(body, elseNode, exceptions, rescueBody, optRescue, isModifier, null, isRescue, reference);
218+
} else {
219+
rv = buildRescueInternal(body, elseNode, exceptions, rescueBody, optRescue, isModifier, ebi, reference);
220+
}
208221
} else {
209222
rv = build(body);
210223
}
@@ -215,8 +228,9 @@ protected Operand buildEnsureInternal(U body, U elseNode, U[] exceptions, U resc
215228

216229
// Is this a begin..(rescue..)?ensure..end node that actually computes a value?
217230
// (vs. returning from protected body)
218-
boolean isEnsureExpr = ensureNode != null && rv != U_NIL && !isRescue;
231+
boolean isEnsureExpr = ensureNode != null && rv != U_NIL;
219232

233+
Variable ensureExprValue = temp();
220234
// Clone the ensure body and jump to the end
221235
if (isEnsureExpr) {
222236
addInstr(new CopyInstr(ensureExprValue, rv));
@@ -230,27 +244,36 @@ protected Operand buildEnsureInternal(U body, U elseNode, U[] exceptions, U resc
230244
// ------------ Emit the ensure body alongwith dummy rescue block ------------
231245
// Now build the dummy rescue block that
232246
// catches all exceptions thrown by the body
233-
addInstr(new LabelInstr(ebi.dummyRescueBlockLabel));
247+
addInstr(new LabelInstr(ebi.ensureRescue));
234248
Variable exc = addResultInstr(new ReceiveJRubyExceptionInstr(temp()));
235249

236250
// Now emit the ensure body's stashed instructions
237-
if (ensureNode != null) ebi.emitBody(this);
251+
if (ensureNode != null) ebi.emitEnsureBody(this);
238252

239253
// 1. Ensure block has no explicit return => the result of the entire ensure expression is the result of the protected body.
240254
// 2. Ensure block has an explicit return => the result of the protected body is ignored.
241255
// U_NIL => there was a return from within the ensure block!
256+
// FIXME: This U_NIL case is wrong in a few cases: 'ensure; return 1 if true; end' or weirder 'ensure; return 1; true; end'
242257
if (ensureRetVal == U_NIL) rv = U_NIL;
243258

244-
// Return (rethrow exception/end)
245-
// rethrows the caught exception from the dummy ensure block
246-
addInstr(new ThrowExceptionInstr(exc));
247-
248-
// End label for the exception region
249-
addInstr(new LabelInstr(ebi.end));
259+
addInstr(new ThrowExceptionInstr(exc)); // rethrows the caught exception from the dummy ensure block
260+
addInstr(new LabelInstr(ebi.end)); // End label for the exception region
250261

251262
return isEnsureExpr ? ensureExprValue : rv;
252263
}
253264

265+
private Operand processEnsureBody(U ensureNode, EnsureBlockInfo ebi) {
266+
Operand ensureRetVal;
267+
if (ensureNode != null) {
268+
ensureBodyBuildStack.push(ebi);
269+
ensureRetVal = build(ensureNode);
270+
ensureBodyBuildStack.pop();
271+
} else {
272+
ensureRetVal = nil();
273+
}
274+
return ensureRetVal;
275+
}
276+
254277
public InterpreterContext buildEvalRoot(ParseResult rootNode) {
255278
executesOnce = false;
256279
coverageMode = rootNode.getCoverageMode();
@@ -1932,8 +1955,8 @@ protected Operand buildOpAsgnConstDecl(Y left, U right, RubySymbol operator) {
19321955
return copy(temp(), putConstant(left, result));
19331956
}
19341957

1935-
protected Operand buildOpAsgnConstDecl(Y left, RubySymbol name, U right, RubySymbol operator) {
1936-
Operand parent = buildColon2ForConstAsgnDeclNode((U) left, temp(), false);
1958+
protected Operand buildOpAsgnConstDecl(U left, RubySymbol name, U right, RubySymbol operator) {
1959+
Operand parent = buildColon2ForConstAsgnDeclNode(left, temp(), false);
19371960
Operand lhs = searchModuleForConst(temp(), parent, name);
19381961
Operand rhs = build(right);
19391962
Variable result = call(temp(), lhs, operator, rhs);
@@ -2499,7 +2522,6 @@ protected Operand buildRedo(int line) {
24992522
protected void buildRescueBodyInternal(U[] exceptions, U body, X consequent, Variable rv, Variable exc, Label endLabel,
25002523
U reference) {
25012524
// Compare and branch as necessary!
2502-
Label uncaughtLabel = getNewLabel("MISSED");
25032525
Label caughtLabel = getNewLabel("RESCUE");
25042526
if (exceptions == null || exceptions.length == 0) {
25052527
outputExceptionCheck(getManager().getStandardError(), exc, caughtLabel);
@@ -2510,7 +2532,6 @@ protected void buildRescueBodyInternal(U[] exceptions, U body, X consequent, Var
25102532
}
25112533

25122534
// Uncaught exception -- build other rescue nodes or rethrow!
2513-
addInstr(new LabelInstr(uncaughtLabel));
25142535
if (consequent != null) {
25152536
buildRescueBodyInternal(exceptionNodesFor(consequent), bodyFor(consequent), optRescueFor(consequent), rv,
25162537
exc, endLabel, referenceFor(consequent));
@@ -2579,21 +2600,33 @@ protected Operand buildAttrAssign(Variable result, U receiver, U argsNode, U blo
25792600

25802601
protected abstract Operand[] buildAttrAssignCallArgs(U args, Operand[] rhs, boolean containsAssignment);
25812602

2603+
/**
2604+
* @param bodyNode the main statements to be executed
2605+
* @param elseNode else path in a begin
2606+
* @param exceptions possible list of excpetions which will jump to rescueBody
2607+
* @param rescueBody the statements executed if the matching exception(s) occurs
2608+
* @param optRescue possible other rescue in the same begin
2609+
* @param isModifier foo rescue bar
2610+
* @param ensure ensure block
2611+
* @param reference variable for the rescue (NameError => name)
2612+
* @return return last operand of begin execution
2613+
*/
25822614
protected Operand buildRescueInternal(U bodyNode, U elseNode, U[] exceptions, U rescueBody,
25832615
X optRescue, boolean isModifier, EnsureBlockInfo ensure, U reference) {
25842616
boolean needsBacktrace = !canBacktraceBeRemoved(exceptions, rescueBody, optRescue, elseNode, isModifier);
25852617

25862618
// Labels marking start, else, end of the begin-rescue(-ensure)-end block
2587-
Label rBeginLabel = getNewLabel();
2588-
Label rEndLabel = ensure.end;
2589-
Label rescueLabel = getNewLabel("RESC_TEST"); // Label marking start of the first rescue code.
2619+
int line = currentLine() + 1;
2620+
Label beginLabel = getNewLabel("BEGIN_@" + line);
2621+
Label endLabel = ensure.end;
2622+
Label rescueTestLabel = getNewLabel("RESCUE_TEST_@" + line); // Label marking start of the first rescue code.
25902623
ensure.needsBacktrace = needsBacktrace;
25912624

2592-
addInstr(new LabelInstr(rBeginLabel));
2625+
addInstr(new LabelInstr(beginLabel));
25932626

25942627
// Placeholder rescue instruction that tells rest of the compiler passes the boundaries of the rescue block.
2595-
addInstr(new ExceptionRegionStartMarkerInstr(rescueLabel));
2596-
activeRescuers.push(rescueLabel);
2628+
addInstr(new ExceptionRegionStartMarkerInstr(rescueTestLabel));
2629+
activeRescuers.push(rescueTestLabel);
25972630
addInstr(getManager().needsBacktrace(needsBacktrace));
25982631

25992632
// Body
@@ -2628,7 +2661,7 @@ protected Operand buildRescueInternal(U bodyNode, U elseNode, U[] exceptions, U
26282661
//
26292662
// The retry should jump to 1, not 2.
26302663
// If we push the rescue block before building the body, we will jump to 2.
2631-
RescueBlockInfo rbi = new RescueBlockInfo(rBeginLabel, ensure.savedGlobalException);
2664+
RescueBlockInfo rbi = new RescueBlockInfo(beginLabel, ensure.savedGlobalException);
26322665
activeRescueBlockStack.push(rbi);
26332666

26342667
if (tmp != U_NIL) {
@@ -2638,7 +2671,7 @@ protected Operand buildRescueInternal(U bodyNode, U elseNode, U[] exceptions, U
26382671
// - If we dont have any ensure blocks, simply jump to the end of the rescue block
26392672
// - If we do, execute the ensure code.
26402673
ensure.cloneIntoHostScope(this);
2641-
addInstr(new JumpInstr(rEndLabel));
2674+
addInstr(new JumpInstr(endLabel));
26422675
} //else {
26432676
// If the body had an explicit return, the return instruction IR build takes care of setting
26442677
// up execution of all necessary ensure blocks. So, nothing to do here!
@@ -2650,7 +2683,7 @@ protected Operand buildRescueInternal(U bodyNode, U elseNode, U[] exceptions, U
26502683
//}
26512684

26522685
// Start of rescue logic
2653-
addInstr(new LabelInstr(rescueLabel));
2686+
addInstr(new LabelInstr(rescueTestLabel));
26542687

26552688
// This is optimized no backtrace path so we need to reenable backtraces since we are
26562689
// exiting that region.
@@ -2660,7 +2693,7 @@ protected Operand buildRescueInternal(U bodyNode, U elseNode, U[] exceptions, U
26602693
Variable exc = addResultInstr(new ReceiveRubyExceptionInstr(temp()));
26612694

26622695
// Build the actual rescue block(s)
2663-
buildRescueBodyInternal(exceptions, rescueBody, optRescue, rv, exc, rEndLabel, reference);
2696+
buildRescueBodyInternal(exceptions, rescueBody, optRescue, rv, exc, endLabel, reference);
26642697

26652698
activeRescueBlockStack.pop();
26662699
return rv;

core/src/main/java/org/jruby/ir/builder/IRBuilderAST.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2515,7 +2515,7 @@ public Operand buildOpAsgnConstDeclNode(OpAsgnConstDeclNode node) {
25152515
return buildOpAsgnConstDeclAnd(node.getFirstNode(), node.getSecondNode(), ((Colon3Node) node.getFirstNode()).getName());
25162516
}
25172517

2518-
return buildOpAsgnConstDecl((Colon3Node) node.getFirstNode(), ((Colon3Node) node.getFirstNode()).getName(), node.getSecondNode(), node.getSymbolOperator());
2518+
return buildOpAsgnConstDecl(node.getFirstNode(), ((Colon3Node) node.getFirstNode()).getName(), node.getSecondNode(), node.getSymbolOperator());
25192519
}
25202520

25212521
public Operand buildOpAsgnAnd(OpAsgnAndNode node) {

core/src/main/java/org/jruby/ir/interpreter/InterpreterContext.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,9 +275,17 @@ public String toStringInstrs() {
275275
StringBuilder b = new StringBuilder();
276276
int length = instructions.length;
277277

278+
var indent = 0;
278279
for (int i = 0; i < length; i++) {
279280
if (i > 0) b.append("\n");
280-
b.append(String.format("%6d",i)).append('\t').append(instructions[i]);
281+
var instr = instructions[i];
282+
if (instr instanceof ExceptionRegionEndMarkerInstr) {
283+
indent -= 1;
284+
}
285+
b.append(String.format("%6d",i+1)).append('\t').append(" ".repeat(indent)).append(instr);
286+
if (instr instanceof ExceptionRegionStartMarkerInstr) {
287+
indent += 1;
288+
}
281289
}
282290

283291
/* ENEBO: I this this is too much output espectially for ic and not fic

0 commit comments

Comments
 (0)