|
43 | 43 | import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
|
44 | 44 |
|
45 | 45 | import java.io.IOException;
|
| 46 | +import java.nio.charset.CharsetEncoder; |
| 47 | +import java.nio.charset.StandardCharsets; |
46 | 48 |
|
| 49 | +import com.ibm.icu.impl.Punycode; |
| 50 | +import com.ibm.icu.text.StringPrepParseException; |
47 | 51 | import com.oracle.graal.python.PythonLanguage;
|
48 | 52 | import com.oracle.graal.python.builtins.objects.cext.capi.CApiContext;
|
49 | 53 | import com.oracle.graal.python.builtins.objects.cext.common.CExtCommonNodes.CheckFunctionResultNode;
|
|
61 | 65 | import com.oracle.graal.python.runtime.object.PythonObjectFactory;
|
62 | 66 | import com.oracle.graal.python.util.PythonUtils;
|
63 | 67 | import com.oracle.truffle.api.CallTarget;
|
| 68 | +import com.oracle.truffle.api.CompilerDirectives; |
64 | 69 | import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
65 | 70 | import com.oracle.truffle.api.CompilerDirectives.ValueType;
|
66 | 71 | import com.oracle.truffle.api.TruffleLanguage.Env;
|
@@ -166,24 +171,68 @@ public final DynamicObject getSymbolCache() {
|
166 | 171 | */
|
167 | 172 | @ValueType
|
168 | 173 | public static final class ModuleSpec {
|
169 |
| - /** |
170 |
| - * The name of the module to load (also just required for creating appropriate error |
171 |
| - * messages). |
172 |
| - */ |
173 |
| - public final String name; |
| 174 | + private static CharsetEncoder asciiEncoder; |
174 | 175 |
|
175 |
| - /** |
176 |
| - * The path of the C extension module to load (usually something ending with {@code .so} or |
177 |
| - * {@code .dylib} or similar). |
178 |
| - */ |
| 176 | + public final String name; |
179 | 177 | public final String path;
|
180 | 178 | public final Object originalModuleSpec;
|
| 179 | + private String encodedName; |
| 180 | + private boolean ascii; |
181 | 181 |
|
182 | 182 | public ModuleSpec(String name, String path, Object originalModuleSpec) {
|
183 | 183 | this.name = name;
|
184 | 184 | this.path = path;
|
185 | 185 | this.originalModuleSpec = originalModuleSpec;
|
186 | 186 | }
|
| 187 | + |
| 188 | + private static CharsetEncoder ensureASCIIEncoder() { |
| 189 | + if (asciiEncoder == null) { |
| 190 | + asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); |
| 191 | + } |
| 192 | + return asciiEncoder; |
| 193 | + } |
| 194 | + |
| 195 | + /** |
| 196 | + * Get the variable part of a module's export symbol name. Returns a bytes instance. For |
| 197 | + * non-ASCII-named modules, the name is encoded as per PEP 489. The hook_prefix pointer is |
| 198 | + * set to either ascii_only_prefix or nonascii_prefix, as appropriate. |
| 199 | + */ |
| 200 | + @TruffleBoundary |
| 201 | + public String getEncodedName() { |
| 202 | + if (encodedName != null) { |
| 203 | + return encodedName; |
| 204 | + } |
| 205 | + |
| 206 | + // Get the short name (substring after last dot) |
| 207 | + String basename = name.substring(name.lastIndexOf('.') + 1); |
| 208 | + |
| 209 | + if (ensureASCIIEncoder().canEncode(basename)) { |
| 210 | + ascii = true; |
| 211 | + } else { |
| 212 | + ascii = false; |
| 213 | + try { |
| 214 | + basename = Punycode.encode(basename, null).toString(); |
| 215 | + } catch (StringPrepParseException e) { |
| 216 | + throw CompilerDirectives.shouldNotReachHere(); |
| 217 | + } |
| 218 | + } |
| 219 | + |
| 220 | + // replace '-' by '_'; note: this is fast and does not use regex |
| 221 | + return (encodedName = basename.replace('-', '_')); |
| 222 | + } |
| 223 | + |
| 224 | + @TruffleBoundary |
| 225 | + public String getInitFunctionName(boolean hpy) { |
| 226 | + /* |
| 227 | + * n.b.: 'getEncodedName' also sets 'ascii' and must therefore be called before 'ascii' |
| 228 | + * is queried |
| 229 | + */ |
| 230 | + String s = getEncodedName(); |
| 231 | + if (hpy) { |
| 232 | + return "HPyInit_" + s; |
| 233 | + } |
| 234 | + return (ascii ? "PyInit_" : "PyInitU_") + s; |
| 235 | + } |
187 | 236 | }
|
188 | 237 |
|
189 | 238 | /**
|
@@ -216,17 +265,15 @@ public static Object loadCExtModule(Node location, PythonContext context, Module
|
216 | 265 |
|
217 | 266 | // Now, try to detect the C extension's API by looking for the appropriate init
|
218 | 267 | // functions.
|
219 |
| - String basename = spec.name.substring(spec.name.lastIndexOf('.') + 1); |
220 |
| - String hpyInitFuncName = "HPyInit_" + basename; |
221 |
| - String initFuncName = "PyInit_" + basename; |
| 268 | + String hpyInitFuncName = spec.getInitFunctionName(true); |
222 | 269 | try {
|
223 | 270 | if (llvmInteropLib.isMemberExisting(llvmLibrary, hpyInitFuncName)) {
|
224 | 271 | GraalHPyContext hpyContext = GraalHPyContext.ensureHPyWasLoaded(location, context, spec.name, spec.path);
|
225 | 272 | return hpyContext.initHPyModule(context, llvmLibrary, hpyInitFuncName, spec.name, spec.path, false, llvmInteropLib, checkHPyResultNode);
|
226 | 273 | }
|
227 |
| - return cApiContext.initCApiModule(location, llvmLibrary, initFuncName, spec, llvmInteropLib, checkFunctionResultNode); |
| 274 | + return cApiContext.initCApiModule(location, llvmLibrary, spec.getInitFunctionName(false), spec, llvmInteropLib, checkFunctionResultNode); |
228 | 275 | } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
|
229 |
| - throw new ImportException(CExtContext.wrapJavaException(e, location), spec.name, spec.path, ErrorMessages.CANNOT_INITIALIZE_WITH, spec.path, basename, ""); |
| 276 | + throw new ImportException(CExtContext.wrapJavaException(e, location), spec.name, spec.path, ErrorMessages.CANNOT_INITIALIZE_WITH, spec.path, spec.getEncodedName(), ""); |
230 | 277 | }
|
231 | 278 | }
|
232 | 279 |
|
|
0 commit comments