Skip to content

Commit 650d0c8

Browse files
committed
[GR-42657] Avoid unresolved symbols in sqlite3 and add test.
PullRequest: graalpython/2879
2 parents c37ee59 + 83e3a9e commit 650d0c8

File tree

7 files changed

+133
-10
lines changed

7 files changed

+133
-10
lines changed

graalpython/com.oracle.graal.python.cext/modules/_sqlite/sqlite/sqlite3.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174096,7 +174096,10 @@ SQLITE_API const char sqlite3_version[] = SQLITE_VERSION;
174096174096
/* IMPLEMENTATION-OF: R-53536-42575 The sqlite3_libversion() function returns
174097174097
** a pointer to the to the sqlite3_version[] string constant.
174098174098
*/
174099-
SQLITE_API const char *sqlite3_libversion(void){ return sqlite3_version; }
174099+
/*
174100+
* GraalPy change: use SQLITE_VERSION instead of sqlite3_version
174101+
*/
174102+
SQLITE_API const char *sqlite3_libversion(void){ return SQLITE_VERSION; }
174100174103

174101174104
/* IMPLEMENTATION-OF: R-25063-23286 The sqlite3_sourceid() function returns a
174102174105
** pointer to a string constant whose value is the same as the

graalpython/com.oracle.graal.python.cext/modules/_sqlite/sqlite/sqlite3.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,10 @@ extern "C" {
182182
**
183183
** See also: [sqlite_version()] and [sqlite_source_id()].
184184
*/
185-
SQLITE_API SQLITE_EXTERN const char sqlite3_version[];
185+
/*
186+
* GraalPy change: avoid unresolved symbol "sqlite3_version"
187+
*/
188+
// SQLITE_API SQLITE_EXTERN const char sqlite3_version[];
186189
SQLITE_API const char *sqlite3_libversion(void);
187190
SQLITE_API const char *sqlite3_sourceid(void);
188191
SQLITE_API int sqlite3_libversion_number(void);

graalpython/com.oracle.graal.python.cext/src/_warnings.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,17 @@ int PyErr_WarnExplicitFormat(PyObject *category,
176176
return ret;
177177
}
178178

179+
void _PyErr_ChainExceptions(PyObject* a, PyObject* b, PyObject* c) {
180+
// empty dummy implementation
181+
}
182+
183+
PyObject* _PyErr_FormatFromCause(PyObject *exception, const char *format, ...) {
184+
// dummy implementation that ignores the cause
185+
va_list args;
186+
va_start(args, format);
187+
PyObject* formatted_msg = PyUnicode_FromFormatV(format, args);
188+
va_end(args);
189+
Graal_PyTruffleErr_CreateAndSetException(exception, formatted_msg);
190+
return NULL;
191+
}
179192

graalpython/com.oracle.graal.python.jni/src/capi_forwards.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4995,15 +4995,9 @@ void (*__target___PyErr_BadInternalCall)(const char*, int) = NULL;
49954995
PyAPI_FUNC(void) _PyErr_BadInternalCall(const char* a, int b) {
49964996
__target___PyErr_BadInternalCall(a, b);
49974997
}
4998-
PyAPI_FUNC(void) _PyErr_ChainExceptions(PyObject* a, PyObject* b, PyObject* c) {
4999-
unimplemented("_PyErr_ChainExceptions"); exit(-1);
5000-
}
50014998
PyAPI_FUNC(int) _PyErr_CheckSignals() {
50024999
unimplemented("_PyErr_CheckSignals"); exit(-1);
50035000
}
5004-
PyAPI_FUNC(PyObject*) _PyErr_FormatFromCause(PyObject* a, const char* b, ...) {
5005-
unimplemented("_PyErr_FormatFromCause"); exit(-1);
5006-
}
50075001
PyAPI_FUNC(void) _PyErr_GetExcInfo(PyThreadState* a, PyObject** b, PyObject** c, PyObject** d) {
50085002
unimplemented("_PyErr_GetExcInfo"); exit(-1);
50095003
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved.
2+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3+
#
4+
# The Universal Permissive License (UPL), Version 1.0
5+
#
6+
# Subject to the condition set forth below, permission is hereby granted to any
7+
# person obtaining a copy of this software, associated documentation and/or
8+
# data (collectively the "Software"), free of charge and under any and all
9+
# copyright rights in the Software, and any and all patent rights owned or
10+
# freely licensable by each licensor hereunder covering either (i) the
11+
# unmodified Software as contributed to or provided by such licensor, or (ii)
12+
# the Larger Works (as defined below), to deal in both
13+
#
14+
# (a) the Software, and
15+
#
16+
# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
17+
# one is included with the Software each a "Larger Work" to which the Software
18+
# is contributed by such licensors),
19+
#
20+
# without restriction, including without limitation the rights to copy, create
21+
# derivative works of, display, perform, and distribute the Software and make,
22+
# use, sell, offer for sale, import, export, have made, and have sold the
23+
# Software and the Larger Work(s), and to sublicense the foregoing rights on
24+
# either these or other terms.
25+
#
26+
# This license is subject to the following condition:
27+
#
28+
# The above copyright notice and either this complete permission notice or at a
29+
# minimum a reference to the UPL must be included in all copies or substantial
30+
# portions of the Software.
31+
#
32+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38+
# SOFTWARE.
39+
40+
import os
41+
42+
try:
43+
import __graalpython__
44+
managed_launcher = __graalpython__.is_managed_launcher()
45+
except ImportError:
46+
managed_launcher = False
47+
48+
def test_basic_functionality():
49+
"""
50+
This is a basic test to ensure that the module can be imported.
51+
The main sqlite3 test suite will be silently skipped if the
52+
"_sqlite3" module is not available.
53+
"""
54+
if not managed_launcher and os.name != 'nt':
55+
import sqlite3
56+
import _sqlite3
57+
conn = sqlite3.connect(':memory:')
58+
rows = conn.execute("select sqlite_version()")
59+
assert len(next(rows)[0]) >= 5
60+
conn.close()
61+

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiCodeGen.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import java.io.IOException;
5353
import java.nio.file.Files;
5454
import java.nio.file.Path;
55+
import java.nio.file.Paths;
5556
import java.util.ArrayList;
5657
import java.util.Arrays;
5758
import java.util.Collections;
@@ -62,6 +63,7 @@
6263
import java.util.function.IntFunction;
6364
import java.util.stream.Collectors;
6465
import java.util.stream.IntStream;
66+
import java.util.stream.Stream;
6567

6668
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltinRegistry;
6769
import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin;
@@ -480,13 +482,60 @@ private static boolean generateBuiltinRegistry(List<CApiBuiltinDesc> javaBuiltin
480482
return writeGenerated(Path.of("com.oracle.graal.python", "src", "com", "oracle", "graal", "python", "builtins", "modules", "cext", "PythonCextBuiltinRegistry.java"), lines);
481483
}
482484

485+
private static void checkUnimplementedAPI(Path path, List<CApiBuiltinDesc> additionalBuiltins) {
486+
List<String> lines;
487+
try {
488+
lines = Files.readAllLines(path);
489+
} catch (IOException e) {
490+
System.out.println(" Error while reading " + path + ": " + e.getMessage());
491+
return;
492+
}
493+
boolean msg = false;
494+
for (CApiBuiltinDesc builtin : additionalBuiltins) {
495+
if (builtin.call == NotImplemented) {
496+
for (int i = 0; i < lines.size(); i++) {
497+
String line = lines.get(i);
498+
int offset = line.indexOf(builtin.name);
499+
// avoid recognizing, e.g. "_PyObject_CallMethodId" in
500+
// "_PyObject_CallMethodIdNoArgs"
501+
if (offset > 0 && Character.isUnicodeIdentifierPart(line.charAt(offset - 1))) {
502+
continue;
503+
}
504+
if (offset + builtin.name.length() < line.length() && Character.isUnicodeIdentifierPart(line.charAt(offset + builtin.name.length()))) {
505+
continue;
506+
}
507+
if (line.contains(builtin.name)) {
508+
if (!msg) {
509+
msg = true;
510+
System.out.println("Checking " + path);
511+
}
512+
System.out.println(" " + builtin.name + " used in " + path + " line " + (i + 1) + ": " + line);
513+
}
514+
}
515+
}
516+
}
517+
}
518+
483519
/**
484520
* Entry point for the "mx python-capi-forwards" command.
485521
*/
486522
public static void main(String[] args) throws IOException {
487523
List<CApiBuiltinDesc> javaBuiltins = CApiFunction.getJavaBuiltinDefinitions();
488524
List<CApiBuiltinDesc> additionalBuiltins = CApiFunction.getOtherBuiltinDefinitions();
489525

526+
/*
527+
* Calling with arguments "check <path>" will recursively check all files in the path for
528+
* unimplemented C API functions.
529+
*/
530+
if (args.length == 2 && "check".equals(args[0])) {
531+
System.out.println("Checking usages of unimplemented API:");
532+
String path = args[1];
533+
try (Stream<Path> stream = Files.walk(Paths.get(path))) {
534+
stream.filter(Files::isRegularFile).forEach(p -> checkUnimplementedAPI(p, additionalBuiltins));
535+
}
536+
return;
537+
}
538+
490539
List<CApiBuiltinDesc> allBuiltins = new ArrayList<>();
491540
allBuiltins.addAll(additionalBuiltins);
492541
for (var entry : javaBuiltins) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,8 @@ public final class CApiFunction {
373373
@CApiBuiltin(name = "PyByteArray_Size", ret = Py_ssize_t, args = {PyObject}, call = CImpl)
374374
@CApiBuiltin(name = "PyGILState_Ensure", ret = PY_GIL_STATE_STATE, args = {}, acquiresGIL = false, call = CImpl)
375375
@CApiBuiltin(name = "PyGILState_Release", ret = Void, args = {PY_GIL_STATE_STATE}, acquiresGIL = false, call = CImpl)
376+
@CApiBuiltin(name = "_PyErr_ChainExceptions", ret = Void, args = {PyObject, PyObject, PyObject}, call = CImpl)
377+
@CApiBuiltin(name = "_PyErr_FormatFromCause", ret = PyObject, args = {PyObject, ConstCharPtrAsTruffleString, VARARGS}, call = CImpl)
376378

377379
/*
378380
* Functions that are implemented in C code that needs to run on Sulong:
@@ -782,9 +784,7 @@ public final class CApiFunction {
782784
@CApiBuiltin(name = "_PyDict_SizeOf", ret = Py_ssize_t, args = {PYDICTOBJECT_PTR}, call = NotImplemented)
783785
@CApiBuiltin(name = "_PyDictView_Intersect", ret = PyObject, args = {PyObject, PyObject}, call = NotImplemented)
784786
@CApiBuiltin(name = "_PyDictView_New", ret = PyObject, args = {PyObject, PyTypeObject}, call = NotImplemented)
785-
@CApiBuiltin(name = "_PyErr_ChainExceptions", ret = Void, args = {PyObject, PyObject, PyObject}, call = NotImplemented)
786787
@CApiBuiltin(name = "_PyErr_CheckSignals", ret = Int, args = {}, call = NotImplemented)
787-
@CApiBuiltin(name = "_PyErr_FormatFromCause", ret = PyObject, args = {PyObject, ConstCharPtrAsTruffleString, VARARGS}, call = NotImplemented)
788788
@CApiBuiltin(name = "_PyErr_GetExcInfo", ret = Void, args = {PyThreadState, PyObjectPtr, PyObjectPtr, PyObjectPtr}, call = NotImplemented)
789789
@CApiBuiltin(name = "_PyErr_GetTopmostException", ret = _PYERR_STACKITEM_PTR, args = {PyThreadState}, call = NotImplemented)
790790
@CApiBuiltin(name = "_PyErr_ProgramDecodedTextObject", ret = PyObject, args = {PyObject, Int, ConstCharPtr}, call = NotImplemented)

0 commit comments

Comments
 (0)