diff --git a/core/src/main/java/org/jruby/ast/RootNode.java b/core/src/main/java/org/jruby/ast/RootNode.java index 418193bdab7..525c153c911 100644 --- a/core/src/main/java/org/jruby/ast/RootNode.java +++ b/core/src/main/java/org/jruby/ast/RootNode.java @@ -31,6 +31,7 @@ import java.util.List; import org.jruby.ast.visitor.NodeVisitor; +import org.jruby.ext.coverage.CoverageData; import org.jruby.lexer.yacc.ISourcePosition; import org.jruby.parser.StaticScope; import org.jruby.runtime.DynamicScope; @@ -49,21 +50,26 @@ public class RootNode extends Node { private Node bodyNode; private String file; private int endPosition; + private boolean needsCodeCoverage; public RootNode(ISourcePosition position, DynamicScope scope, Node bodyNode, String file) { - this(position, scope, bodyNode, file, -1); + this(position, scope, bodyNode, file, -1, false); } - public RootNode(ISourcePosition position, DynamicScope scope, Node bodyNode, String file, int endPosition) { + public RootNode(ISourcePosition position, DynamicScope scope, Node bodyNode, String file, int endPosition, boolean needsCodeCoverage) { super(position, bodyNode.containsVariableAssignment()); - assert bodyNode != null : "bodyNode is not null"; - this.scope = scope; this.staticScope = scope.getStaticScope(); this.bodyNode = bodyNode; this.file = file; this.endPosition = endPosition; + this.needsCodeCoverage = needsCodeCoverage; + } + + @Deprecated + public RootNode(ISourcePosition position, DynamicScope scope, Node bodyNode, String file, int endPosition) { + this(position, scope, bodyNode, file, endPosition, false); } public NodeType getNodeType() { @@ -122,4 +128,9 @@ public boolean hasEndPosition() { public int getEndPosition() { return endPosition; } + + // Is coverage enabled and is this a valid source file for coverage to apply? + public boolean needsCoverage() { + return needsCodeCoverage; + } } diff --git a/core/src/main/java/org/jruby/parser/Parser.java b/core/src/main/java/org/jruby/parser/Parser.java index e7a3a79a7c2..88bf1524092 100644 --- a/core/src/main/java/org/jruby/parser/Parser.java +++ b/core/src/main/java/org/jruby/parser/Parser.java @@ -158,12 +158,6 @@ public Node parse(String file, LexerSource lexerSource, DynamicScope blockScope, totalTime += System.nanoTime() - startTime; totalBytes += lexerSource.getOffset(); - // set coverage baseline into coverage data - if (!configuration.isEvalParse() && runtime.getCoverageData().isCoverageEnabled()) { - configuration.growCoverageLines(parser.lexer.lineno()); - runtime.getCoverageData().prepareCoverage(file, configuration.getCoverage()); - } - return ast; } diff --git a/core/src/main/java/org/jruby/parser/ParserConfiguration.java b/core/src/main/java/org/jruby/parser/ParserConfiguration.java index cc8a932004f..613c7453d95 100644 --- a/core/src/main/java/org/jruby/parser/ParserConfiguration.java +++ b/core/src/main/java/org/jruby/parser/ParserConfiguration.java @@ -34,6 +34,7 @@ import org.jcodings.specific.UTF8Encoding; import org.jruby.Ruby; import org.jruby.RubyInstanceConfig; +import org.jruby.ext.coverage.CoverageData; import org.jruby.runtime.DynamicScope; import org.jruby.runtime.encoding.EncodingService; import org.jruby.runtime.scope.ManyVarsDynamicScope; @@ -85,7 +86,6 @@ public ParserConfiguration(Ruby runtime, int lineNumber, this.frozenStringLiteral = config.isFrozenStringLiteral(); } - public void setFrozenStringLiteral(boolean frozenStringLiteral) { this.frozenStringLiteral = frozenStringLiteral; } @@ -162,7 +162,11 @@ public DynamicScope getScope(String file) { // any of the specific-size scopes. return new ManyVarsDynamicScope(runtime.getStaticScopeFactory().newLocalScope(null, file), existingScope); } - + + public boolean isCoverageEnabled() { + return !isEvalParse() && runtime.getCoverageData().isCoverageEnabled(); + } + /** * Get whether we are saving the DATA contents of the file. */ @@ -185,7 +189,7 @@ public boolean isInlineSource() { public void coverLine(int i) { if (i < 0) return; // JRUBY-6868: why would there be negative line numbers? - if (!isEvalParse() && runtime.getCoverageData().isCoverageEnabled()) { + if (!isCoverageEnabled()) { growCoverageLines(i); coverage[i] = 0; } @@ -207,9 +211,24 @@ public void growCoverageLines(int i) { } } + /** + * At end of a parse if coverage is enabled we will do final processing + * of the primitive coverage array and make sure runtimes coverage data + * has been updated with this new data. + */ + public CoverageData finishCoverage(String file, int lines) { + if (!isCoverageEnabled()) return null; + + growCoverageLines(lines); + CoverageData data = runtime.getCoverageData(); + data.prepareCoverage(file, coverage); + return data; + } + /** * Get the coverage array, indicating all coverable lines */ + @Deprecated public int[] getCoverage() { return coverage; } diff --git a/core/src/main/java/org/jruby/parser/ParserSupport.java b/core/src/main/java/org/jruby/parser/ParserSupport.java index aa3d89e656f..634f1e6b417 100644 --- a/core/src/main/java/org/jruby/parser/ParserSupport.java +++ b/core/src/main/java/org/jruby/parser/ParserSupport.java @@ -47,6 +47,7 @@ import org.jruby.common.IRubyWarnings; import org.jruby.common.IRubyWarnings.ID; import org.jruby.exceptions.RaiseException; +import org.jruby.ext.coverage.CoverageData; import org.jruby.lexer.yacc.ISourcePosition; import org.jruby.lexer.yacc.ISourcePositionHolder; import org.jruby.lexer.yacc.RubyLexer; @@ -85,7 +86,7 @@ public void reset() { inSingleton = 0; inDefinition = false; } - + public StaticScope getCurrentScope() { return currentScope; } @@ -188,7 +189,8 @@ public Node newline_node(Node node, ISourcePosition position) { return node; } - + + // This is the last node made in the AST unintuitively so so post-processing can occur here. public Node addRootNode(Node topOfAST) { final int endPosition; @@ -198,28 +200,28 @@ public Node addRootNode(Node topOfAST) { endPosition = -1; } + ISourcePosition position; + CoverageData coverageData = configuration.finishCoverage(lexer.getFile(), lexer.lineno()); if (result.getBeginNodes().isEmpty()) { - ISourcePosition position; if (topOfAST == null) { topOfAST = NilImplicitNode.NIL; position = lexer.getPosition(); } else { position = topOfAST.getPosition(); } - - return new RootNode(position, result.getScope(), topOfAST, lexer.getFile(), endPosition); - } + } else { + position = topOfAST != null ? topOfAST.getPosition() : result.getBeginNodes().get(0).getPosition(); + BlockNode newTopOfAST = new BlockNode(position); + for (Node beginNode : result.getBeginNodes()) { + appendToBlock(newTopOfAST, beginNode); + } - ISourcePosition position = topOfAST != null ? topOfAST.getPosition() : result.getBeginNodes().get(0).getPosition(); - BlockNode newTopOfAST = new BlockNode(position); - for (Node beginNode: result.getBeginNodes()) { - appendToBlock(newTopOfAST, beginNode); + // Add real top to new top (unless this top is empty [only begin/end nodes or truly empty]) + if (topOfAST != null) newTopOfAST.add(topOfAST); + topOfAST = newTopOfAST; } - // Add real top to new top (unless this top is empty [only begin/end nodes or truly empty]) - if (topOfAST != null) newTopOfAST.add(topOfAST); - - return new RootNode(position, result.getScope(), newTopOfAST, lexer.getFile(), endPosition); + return new RootNode(position, result.getScope(), topOfAST, lexer.getFile(), endPosition, coverageData != null); } /* MRI: block_append */