Skip to content

Commit

Permalink
[Truffle] Sprintf check for too many args when $DEBUG is true
Browse files Browse the repository at this point in the history
  • Loading branch information
Brandon Fish committed Dec 13, 2016
1 parent 79a4021 commit 1c9c38e
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 9 deletions.
1 change: 0 additions & 1 deletion spec/truffle/tags/core/string/modulo_tags.txt
@@ -1,4 +1,3 @@
fails:String#% raises an ArgumentError for unused arguments when $DEBUG is true
fails:String#% behaves as if calling Kernel#Float for %e arguments, when the passed argument does not respond to #to_ary
fails:String#% behaves as if calling Kernel#Float for %e arguments, when the passed argument is hexadecimal string
fails:String#% behaves as if calling Kernel#Float for %E arguments, when the passed argument does not respond to #to_ary
Expand Down
Expand Up @@ -28,8 +28,8 @@ public PrintfCompiler(RubyContext context, RubyNode currentNode) {
this.currentNode = currentNode;
}

public CallTarget compile(byte[] format, Object[] arguments) {
final PrintfSimpleParser parser = new PrintfSimpleParser(bytesToChars(format), arguments);
public CallTarget compile(byte[] format, Object[] arguments, boolean isDebug) {
final PrintfSimpleParser parser = new PrintfSimpleParser(bytesToChars(format), arguments, isDebug);
final List<SprintfConfig> configs = parser.parse();
final PrintfSimpleTreeBuilder builder = new PrintfSimpleTreeBuilder(context, configs);

Expand Down
Expand Up @@ -19,10 +19,12 @@ public class PrintfSimpleParser {

private final char[] source;
private final Object[] arguments;
private final boolean isDebug;

public PrintfSimpleParser(char[] source, Object[] arguments) {
public PrintfSimpleParser(char[] source, Object[] arguments, boolean isDebug) {
this.source = source;
this.arguments = arguments;
this.isDebug = isDebug;
}

@SuppressWarnings("fallthrough")
Expand All @@ -31,6 +33,7 @@ public List<SprintfConfig> parse() {
ArgType argType = ArgType.NONE;

final int end = source.length;
int argCount = 0;

for (int i = 0; i < end; ) {

Expand Down Expand Up @@ -152,6 +155,7 @@ public List<SprintfConfig> parse() {
i = numberDollarWidth.getNextI();
} else {
checkNextArg(argType, 1); // TODO index next args
argCount += 1;
argType = ArgType.UNNUMBERED;
config.setWidthStar(true);
i++;
Expand All @@ -172,6 +176,7 @@ public List<SprintfConfig> parse() {
i = numberDollar.getNextI();
} else {
checkNextArg(argType, 1); // TODO idx
argCount += 1;
argType = ArgType.UNNUMBERED;
config.setPrecisionStar(true);
i += 2;
Expand Down Expand Up @@ -202,6 +207,7 @@ public List<SprintfConfig> parse() {
i++;
if (!argTypeSet) {
checkNextArg(argType, 1);
argCount += 1;
argType = ArgType.UNNUMBERED;
}
finished = true;
Expand All @@ -213,6 +219,7 @@ public List<SprintfConfig> parse() {
i++;
if (!argTypeSet) { // Speculative
checkNextArg(argType, 1);
argCount += 1;
argType = ArgType.UNNUMBERED;
}
finished = true;
Expand All @@ -227,6 +234,7 @@ public List<SprintfConfig> parse() {
case 'u':
if (!argTypeSet) {
checkNextArg(argType, 1); // TODO idx correctly
argCount += 1;
argType = ArgType.UNNUMBERED;
}
config.setFormatType(SprintfConfig.FormatType.INTEGER);
Expand All @@ -243,6 +251,7 @@ public List<SprintfConfig> parse() {
case 'f':
if (!argTypeSet) {
checkNextArg(argType, 1);
argCount += 1;
argType = ArgType.UNNUMBERED;
}
config.setFormatType(SprintfConfig.FormatType.FLOAT);
Expand All @@ -255,6 +264,13 @@ public List<SprintfConfig> parse() {
}
}
}
if((argType == ArgType.UNNUMBERED || argType == ArgType.NONE) &&
arguments.length > argCount ){
if(isDebug){
throw new InvalidFormatException("too many arguments for format string");
}
}

return configs;
}

Expand Down
Expand Up @@ -107,6 +107,8 @@
import org.jruby.truffle.language.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.language.dispatch.DoesRespondDispatchHeadNode;
import org.jruby.truffle.language.dispatch.MissingBehavior;
import org.jruby.truffle.language.globals.ReadGlobalVariableNode;
import org.jruby.truffle.language.globals.ReadGlobalVariableNodeGen;
import org.jruby.truffle.language.loader.CodeLoader;
import org.jruby.truffle.language.loader.RequireNode;
import org.jruby.truffle.language.loader.SourceLoader;
Expand Down Expand Up @@ -1774,18 +1776,25 @@ public abstract static class SprintfNode extends CoreMethodArrayArgumentsNode {

@Child private RopeNodes.MakeLeafRopeNode makeLeafRopeNode;
@Child private TaintNode taintNode;
@Child private ReadGlobalVariableNode readDebugGlobalNode;

private final BranchProfile exceptionProfile = BranchProfile.create();
private final ConditionProfile resizeProfile = ConditionProfile.createBinaryProfile();

@Specialization(guards = { "isRubyString(format)", "ropesEqual(format, cachedFormat)" })
public SprintfNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
this.readDebugGlobalNode = ReadGlobalVariableNodeGen.create(context, sourceSection, "$DEBUG");
}

@Specialization(guards = { "isRubyString(format)", "ropesEqual(format, cachedFormat)", "isDebug(frame) == cachedIsDebug" })
public DynamicObject formatCached(
VirtualFrame frame,
DynamicObject format,
Object[] arguments,
@Cached("isDebug(frame)") boolean cachedIsDebug,
@Cached("privatizeRope(format)") Rope cachedFormat,
@Cached("ropeLength(cachedFormat)") int cachedFormatLength,
@Cached("create(compileFormat(format, arguments))") DirectCallNode callPackNode) {
@Cached("create(compileFormat(format, arguments, isDebug(frame)))") DirectCallNode callPackNode) {
final BytesResult result;

try {
Expand All @@ -1807,8 +1816,14 @@ public DynamicObject formatUncached(
@Cached("create()") IndirectCallNode callPackNode) {
final BytesResult result;

boolean isDebug = false;
try {
isDebug = readDebugGlobalNode.executeBoolean(frame);
} catch (UnexpectedResultException e) {
}

try {
result = (BytesResult) callPackNode.call(frame, compileFormat(format, arguments),
result = (BytesResult) callPackNode.call(frame, compileFormat(format, arguments, isDebug),
new Object[]{ arguments, arguments.length });
} catch (FormatException e) {
exceptionProfile.enter();
Expand Down Expand Up @@ -1849,17 +1864,25 @@ private DynamicObject finishFormat(int formatLength, BytesResult result) {
}

@TruffleBoundary
protected CallTarget compileFormat(DynamicObject format, Object[] arguments) {
protected CallTarget compileFormat(DynamicObject format, Object[] arguments, boolean isDebug) {
assert RubyGuards.isRubyString(format);

try {
return new PrintfCompiler(getContext(), this)
.compile(Layouts.STRING.getRope(format).getBytes(), arguments);
.compile(Layouts.STRING.getRope(format).getBytes(), arguments, isDebug);
} catch (InvalidFormatException e) {
throw new RaiseException(coreExceptions().argumentError(e.getMessage(), this));
}
}

protected boolean isDebug(VirtualFrame frame) {
try {
return readDebugGlobalNode.executeBoolean(frame);
} catch (UnexpectedResultException e) {
return false;
}
}

}

@Primitive(name = "kernel_global_variables")
Expand Down

0 comments on commit 1c9c38e

Please sign in to comment.