Skip to content

Commit 144415e

Browse files
author
Pavel Marek
committed
Add PCRE2 error handling.
(cherry picked from commit ccdfd08)
1 parent 9f6f0b9 commit 144415e

File tree

6 files changed

+98
-22
lines changed

6 files changed

+98
-22
lines changed

com.oracle.truffle.r.native/fficall/src/truffle_common/pcre2_rffi.c

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ int call_pcre2_match(
8181
int stop_after_first_match
8282
);
8383
void call_pcre2_pattern_free(pcre2_code *compiled_pattern);
84-
void call_pcre2_errcode_to_string(int errcode, char *buff, size_t buff_len);
84+
void call_pcre2_errcode_to_string(int errcode, uint8_t *buff, size_t buff_len);
8585

8686
// Helper functions
8787
static int is_valid_index(size_t index);
@@ -305,12 +305,9 @@ void call_pcre2_pattern_free(pcre2_code *compiled_pattern)
305305
pcre2_code_free(compiled_pattern);
306306
}
307307

308-
void call_pcre2_errcode_to_string(int errcode, char *buff, size_t buff_len)
308+
void call_pcre2_errcode_to_string(int errcode, uint8_t *buff, size_t buff_len)
309309
{
310-
if (errcode >= 0) {
311-
fatalError("pcre2_rffi.c: errcode >= 0");
312-
}
313-
int rc = pcre2_get_error_message(errcode, (uint8_t *)buff, buff_len);
310+
int rc = pcre2_get_error_message(errcode, buff, buff_len);
314311
if (rc < 0) {
315312
printf("Fatal error: pcre2_get_error_message returned %d\n", rc);
316313
exit(1);

com.oracle.truffle.r.native/fficall/src/truffle_nfi/rffiutils.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ void *unimplemented(const char *f) {
3333
exit(1);
3434
}
3535

36+
void fatalError(const char *msg) {
37+
printf("faatal error %s\n", msg);
38+
exit(1);
39+
}
40+
3641
void init_utils(TruffleEnv *env) {
3742
// nothing to initialize
3843
}

com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/GrepFunctions.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,8 @@ protected PCRE2RFFI.CompileResult compilePerlPattern(String pattern, boolean ign
234234
int options = ignoreCase ? PCRE2RFFI.Option.CASELESS.value : 0;
235235
PCRE2RFFI.CompileResult pcre = pcre2CompileNode.execute(pattern, options);
236236
if (interop.isNull(pcre.compiledPattern)) {
237-
// TODO output warning if pcre.errorMessage not NULL
238-
throw error(RError.Message.INVALID_REGEXP, pattern);
237+
assert pcre.errorMessage != null;
238+
throw error(Message.INVALID_REGEXP_REASON, pattern, pcre.errorMessage);
239239
}
240240
return pcre;
241241
}
@@ -266,9 +266,8 @@ protected Object doGrep(String patternArg, RStringVector vector, boolean ignoreC
266266
} else {
267267
PCRE2RFFI.CompileResult compileResult = pcre2CompileNode.execute(pattern, 0);
268268
if (interop.isNull(compileResult.compiledPattern)) {
269-
// Error occured during pattern compilation.
270-
// TODO: Inspect the error - call a PCRE specific pcre2_get_err_msg(...)
271-
throw RInternalError.unimplemented("PCRE2 error handling not yet implemented");
269+
assert compileResult.errorMessage != null;
270+
throw error(Message.INVALID_REGEXP_REASON, pattern, compileResult.errorMessage);
272271
}
273272
int captureCount = pcre2CaptureCountNode.execute(compileResult.compiledPattern);
274273
assert !interop.isNull(compileResult.compiledPattern);
@@ -1620,8 +1619,8 @@ protected RList split(RStringVector x, RStringVector splitArg, boolean fixed, bo
16201619
if (!currentSplit.isEmpty()) {
16211620
pcrePatterns[i] = commonNode.pcre2CompileNode.execute(currentSplit, 0);
16221621
if (interop.isNull(pcrePatterns[i].compiledPattern)) {
1623-
// TODO output warning if pcre.errorMessage not NULL
1624-
throw error(RError.Message.INVALID_REGEXP, currentSplit);
1622+
assert pcrePatterns[i].errorMessage != null;
1623+
throw error(RError.Message.INVALID_REGEXP_REASON, currentSplit, pcrePatterns[i].errorMessage);
16251624
}
16261625
}
16271626
}

com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/PCRE2Tests.java

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,19 @@
2424
package com.oracle.truffle.r.nodes.test;
2525

2626
import static org.junit.Assert.assertEquals;
27+
import static org.junit.Assert.assertNotEquals;
28+
import static org.junit.Assert.assertNotNull;
2729

2830
import java.util.ArrayList;
2931
import java.util.HashMap;
3032
import java.util.List;
3133
import java.util.Map;
3234
import java.util.Set;
3335

36+
import com.oracle.truffle.api.frame.VirtualFrame;
37+
import com.oracle.truffle.api.nodes.Node;
38+
import com.oracle.truffle.api.nodes.RootNode;
39+
import com.oracle.truffle.r.runtime.context.TruffleRLanguage;
3440
import org.junit.Assert;
3541
import org.junit.Before;
3642
import org.junit.Test;
@@ -47,6 +53,7 @@
4753

4854
@RunWith(Theories.class)
4955
public class PCRE2Tests extends TestBase {
56+
private TestRootNode testRootNode;
5057
private PCRE2RFFI.CompileNode compileNode;
5158
private PCRE2RFFI.MatchNode matchNode;
5259
private PCRE2RFFI.MemoryReleaseNode memoryReleaseNode;
@@ -144,6 +151,21 @@ public Builder expectedCaptureNames(String[] captureNames) {
144151
}
145152
}
146153

154+
private static class TestRootNode extends RootNode {
155+
TestRootNode() {
156+
super(TruffleRLanguage.getCurrentLanguage());
157+
}
158+
159+
void insertChildren(Node[] children) {
160+
insert(children);
161+
}
162+
163+
@Override
164+
public Object execute(VirtualFrame frame) {
165+
return null;
166+
}
167+
}
168+
147169
// @formatter:off
148170
@DataPoints
149171
public static TestData[] testData = {
@@ -234,11 +256,6 @@ public Builder expectedCaptureNames(String[] captureNames) {
234256
};
235257
// @formatter:on
236258

237-
// Declare dummy test so that `mx unittest` can discover this class.
238-
@Test
239-
public void dummy() {
240-
}
241-
242259
@Before
243260
public void init() {
244261
execInContext(() -> {
@@ -248,6 +265,8 @@ public void init() {
248265
captureNamesNode = RFFIFactory.getPCRE2RFFI().createGetCaptureNamesNode();
249266
captureCountNode = RFFIFactory.getPCRE2RFFI().createGetCaptureCountNode();
250267
interop = InteropLibrary.getUncached();
268+
testRootNode = new TestRootNode();
269+
testRootNode.insertChildren(new Node[]{compileNode, matchNode, memoryReleaseNode, captureNamesNode, captureCountNode});
251270
return null;
252271
});
253272
}
@@ -271,6 +290,30 @@ public void test(TestData testingData) {
271290
});
272291
}
273292

293+
@Test
294+
public void testFailedPatternCompilationStar() {
295+
execInContext(() -> {
296+
String pattern = ".**";
297+
PCRE2RFFI.CompileResult compileResult = compileNode.execute(pattern, 0);
298+
assertNotEquals(0, compileResult.errorCode);
299+
assertNotNull(compileResult.errorMessage);
300+
assertEquals(2, compileResult.errOffset);
301+
return null;
302+
});
303+
}
304+
305+
@Test
306+
public void testFailedPatternCompilationNoClosingBracket() {
307+
execInContext(() -> {
308+
String pattern = "abc[";
309+
PCRE2RFFI.CompileResult compileResult = compileNode.execute(pattern, 0);
310+
assertNotEquals(0, compileResult.errorCode);
311+
assertNotNull(compileResult.errorMessage);
312+
assertEquals(4, compileResult.errOffset);
313+
return null;
314+
});
315+
}
316+
274317
/**
275318
* @param expected Start indexes and end indexes are intertwined ([start_idx1, end_idx1,
276319
* start_idx2, end_idx2,...])

com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/NativeFunction.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public enum NativeFunction {
5454
get_capture_names("((string, sint32): void, pointer): sint32", "call_pcre2_"),
5555
match_count("(pointer, string, sint32, sint32, uint32): sint32", "call_pcre2_"),
5656
pattern_free("(pointer): void", "call_pcre2_"),
57+
errcode_to_string("(sint32, [uint8], uint32): void", "call_pcre2_"),
5758
// zip
5859
compress("([uint8], uint64, [uint8], uint64): sint32", "call_zip_"),
5960
uncompress("([uint8], uint64, [uint8], uint64): sint32", "call_zip_"),

com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/PCRE2RFFI.java

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.oracle.truffle.api.CompilerDirectives;
2727
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
2828
import com.oracle.truffle.api.TruffleLogger;
29+
import com.oracle.truffle.api.dsl.Cached;
2930
import com.oracle.truffle.api.dsl.ImportStatic;
3031
import com.oracle.truffle.api.interop.InteropLibrary;
3132
import com.oracle.truffle.api.interop.TruffleObject;
@@ -402,6 +403,9 @@ private void set(int idx, String name) {
402403
}
403404

404405
public static final class CompileNode extends NativeCallNode {
406+
@Child private GetErrorStringNode getErrorStringNode = GetErrorStringNode.create();
407+
@Child private InteropLibrary interop = InteropLibrary.getFactory().createDispatched(DSLConfig.getInteropLibraryCacheSize());
408+
405409
public CompileNode(DownCallNodeFactory downCallNodeFactory) {
406410
super(downCallNodeFactory.createDownCallNode());
407411
}
@@ -413,12 +417,16 @@ public static CompileNode create() {
413417
@CompilerDirectives.TruffleBoundary
414418
public CompileResult execute(String pattern, int options) {
415419
int[] errorCode = new int[]{0};
416-
int[] errorOffSet = new int[]{0};
417-
// We want to enable UTF-based mathing by default.
420+
int[] errorOffSet = new int[]{-1};
421+
// We want to enable UTF-based matching by default.
418422
options |= Option.UTF.value;
419423
Object pcreCode = call(NativeFunction.compile, pattern, options, errorCode, errorOffSet);
420-
// TODO: Fill in error message if necessary.
421-
return new CompileResult(pcreCode, errorCode[0], null, errorOffSet[0]);
424+
String errorMessage = null;
425+
if (interop.isNull(pcreCode)) {
426+
assert errorOffSet[0] >= 0;
427+
errorMessage = getErrorStringNode.execute(errorCode[0]);
428+
}
429+
return new CompileResult(pcreCode, errorCode[0], errorMessage, errorOffSet[0]);
422430
}
423431
}
424432

@@ -600,6 +608,29 @@ public static MemoryReleaseNode create() {
600608
}
601609
}
602610

611+
public static final class GetErrorStringNode extends NativeCallNode {
612+
private final int buffLen = 256;
613+
private final byte[] buff = new byte[buffLen];
614+
615+
public GetErrorStringNode(DownCallNodeFactory downCallNodeFactory) {
616+
super(downCallNodeFactory.createDownCallNode());
617+
}
618+
619+
public static GetErrorStringNode create() {
620+
return RFFIFactory.getPCRE2RFFI().createGetErrorStringNode();
621+
}
622+
623+
public String execute(int errCode) {
624+
NativeCharArray charArray = new NativeCharArray(buff);
625+
call(NativeFunction.errcode_to_string, errCode, charArray, buffLen);
626+
return charArray.getString();
627+
}
628+
}
629+
630+
private GetErrorStringNode createGetErrorStringNode() {
631+
return new GetErrorStringNode(downCallNodeFactory);
632+
}
633+
603634
public MemoryReleaseNode createMemoryReleaseNode() {
604635
return new MemoryReleaseNode(downCallNodeFactory);
605636
}

0 commit comments

Comments
 (0)