|
31 | 31 | import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError;
|
32 | 32 | import static com.oracle.graal.python.runtime.exception.PythonErrorType.ValueError;
|
33 | 33 |
|
34 |
| -import java.io.BufferedReader; |
35 | 34 | import java.io.File;
|
36 | 35 | import java.io.IOException;
|
37 | 36 | import java.io.InputStream;
|
38 |
| -import java.io.InputStreamReader; |
39 | 37 | import java.io.OutputStream;
|
40 | 38 | import java.lang.ProcessBuilder.Redirect;
|
41 | 39 | import java.math.BigInteger;
|
|
58 | 56 | import com.oracle.graal.python.builtins.CoreFunctions;
|
59 | 57 | import com.oracle.graal.python.builtins.PythonBuiltinClassType;
|
60 | 58 | import com.oracle.graal.python.builtins.PythonBuiltins;
|
| 59 | +import com.oracle.graal.python.builtins.modules.SysModuleBuiltins.AuditNode; |
61 | 60 | import com.oracle.graal.python.builtins.objects.PNone;
|
62 | 61 | import com.oracle.graal.python.builtins.objects.bytes.BytesNodes;
|
63 | 62 | import com.oracle.graal.python.builtins.objects.bytes.BytesUtils;
|
64 | 63 | import com.oracle.graal.python.builtins.objects.bytes.PBytes;
|
65 | 64 | import com.oracle.graal.python.builtins.objects.bytes.PBytesLike;
|
66 | 65 | import com.oracle.graal.python.builtins.objects.common.SequenceNodes.LenNode;
|
67 |
| -import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetItemDynamicNode; |
| 66 | +import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; |
68 | 67 | import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.GetItemNode;
|
| 68 | +import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.ToArrayNode; |
69 | 69 | import com.oracle.graal.python.builtins.objects.dict.PDict;
|
70 | 70 | import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum;
|
71 | 71 | import com.oracle.graal.python.builtins.objects.floats.PFloat;
|
|
80 | 80 | import com.oracle.graal.python.builtins.objects.tuple.StructSequence;
|
81 | 81 | import com.oracle.graal.python.nodes.ErrorMessages;
|
82 | 82 | import com.oracle.graal.python.nodes.PGuards;
|
| 83 | +import com.oracle.graal.python.nodes.PNodeWithRaise; |
83 | 84 | import com.oracle.graal.python.nodes.PRaiseNode;
|
84 | 85 | import com.oracle.graal.python.nodes.call.special.LookupAndCallBinaryNode;
|
85 | 86 | import com.oracle.graal.python.nodes.expression.BinaryArithmetic;
|
|
117 | 118 | import com.oracle.truffle.api.TruffleLogger;
|
118 | 119 | import com.oracle.truffle.api.dsl.Cached;
|
119 | 120 | import com.oracle.truffle.api.dsl.Cached.Shared;
|
| 121 | +import com.oracle.truffle.api.dsl.CachedContext; |
120 | 122 | import com.oracle.truffle.api.dsl.Fallback;
|
121 | 123 | import com.oracle.truffle.api.dsl.GenerateNodeFactory;
|
122 | 124 | import com.oracle.truffle.api.dsl.ImportStatic;
|
|
126 | 128 | import com.oracle.truffle.api.frame.VirtualFrame;
|
127 | 129 | import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
128 | 130 | import com.oracle.truffle.api.library.CachedLibrary;
|
| 131 | +import com.oracle.truffle.api.nodes.Node; |
129 | 132 | import com.oracle.truffle.api.profiles.BranchProfile;
|
130 | 133 | import com.oracle.truffle.api.profiles.ConditionProfile;
|
131 | 134 |
|
@@ -379,86 +382,65 @@ private void checkEqualSign(byte[] bytes) {
|
379 | 382 | }
|
380 | 383 | }
|
381 | 384 |
|
382 |
| - @Builtin(name = "execv", minNumOfPositionalArgs = 3, declaresExplicitSelf = true) |
| 385 | + @Builtin(name = "execv", minNumOfPositionalArgs = 2, parameterNames = {"pathname", "argv"}) |
| 386 | + @ArgumentClinic(name = "pathname", conversionClass = PathConversionNode.class, args = {"false", "false"}) |
383 | 387 | @GenerateNodeFactory
|
384 |
| - public abstract static class ExecvNode extends PythonBuiltinNode { |
385 |
| - @Child private BytesNodes.ToBytesNode toBytes = BytesNodes.ToBytesNode.create(); |
| 388 | + public abstract static class ExecvNode extends PythonBinaryClinicBuiltinNode { |
386 | 389 |
|
387 |
| - @Specialization |
388 |
| - Object execute(VirtualFrame frame, PythonModule thisModule, String path, PList args) { |
389 |
| - return doExecute(frame, thisModule, path, args); |
| 390 | + @Override |
| 391 | + protected ArgumentClinicProvider getArgumentClinic() { |
| 392 | + return PosixModuleBuiltinsClinicProviders.ExecvNodeClinicProviderGen.INSTANCE; |
390 | 393 | }
|
391 | 394 |
|
392 | 395 | @Specialization
|
393 |
| - Object execute(VirtualFrame frame, PythonModule thisModule, String path, PTuple args) { |
394 |
| - // in case of execl the PList happens to be in the tuples first entry |
395 |
| - Object list = GetItemDynamicNode.getUncached().execute(args.getSequenceStorage(), 0); |
396 |
| - return doExecute(frame, thisModule, path, list instanceof PList ? (PList) list : args); |
| 396 | + Object execvArgsList(VirtualFrame frame, PosixPath path, PList argv, |
| 397 | + @CachedLibrary("getPosixSupport()") PosixSupportLibrary posixLib, |
| 398 | + @Cached ToArrayNode toArrayNode, |
| 399 | + @Cached ObjectToOpaquePathNode toOpaquePathNode, |
| 400 | + @Cached SysModuleBuiltins.AuditNode auditNode) { |
| 401 | + execv(frame, path, argv, argv.getSequenceStorage(), posixLib, toArrayNode, toOpaquePathNode, auditNode); |
| 402 | + throw CompilerDirectives.shouldNotReachHere("execv should not return normally"); |
397 | 403 | }
|
398 | 404 |
|
399 |
| - @Specialization(limit = "1") |
400 |
| - Object executePath(VirtualFrame frame, PythonModule thisModule, Object path, PTuple args, |
401 |
| - @CachedLibrary("path") PythonObjectLibrary lib) { |
402 |
| - return execute(frame, thisModule, lib.asPath(path), args); |
| 405 | + @Specialization |
| 406 | + Object execvArgsTuple(VirtualFrame frame, PosixPath path, PTuple argv, |
| 407 | + @CachedLibrary("getPosixSupport()") PosixSupportLibrary posixLib, |
| 408 | + @Cached ToArrayNode toArrayNode, |
| 409 | + @Cached ObjectToOpaquePathNode toOpaquePathNode, |
| 410 | + @Cached AuditNode auditNode) { |
| 411 | + execv(frame, path, argv, argv.getSequenceStorage(), posixLib, toArrayNode, toOpaquePathNode, auditNode); |
| 412 | + throw CompilerDirectives.shouldNotReachHere("execv should not return normally"); |
403 | 413 | }
|
404 | 414 |
|
405 |
| - @Specialization(limit = "1") |
406 |
| - Object executePath(VirtualFrame frame, PythonModule thisModule, Object path, PList args, |
407 |
| - @CachedLibrary("path") PythonObjectLibrary lib) { |
408 |
| - return doExecute(frame, thisModule, lib.asPath(path), args); |
| 415 | + @Specialization(guards = {"!isList(argv)", "!isTuple(argv)"}) |
| 416 | + @SuppressWarnings("unused") |
| 417 | + Object execvInvalidArgs(VirtualFrame frame, PosixPath path, Object argv) { |
| 418 | + throw raise(TypeError, ErrorMessages.ARG_D_MUST_BE_S, "execv()", 2, "tuple or list"); |
409 | 419 | }
|
410 | 420 |
|
411 |
| - Object doExecute(VirtualFrame frame, PythonModule thisModule, String path, PSequence args) { |
412 |
| - if (!getContext().isExecutableAccessAllowed()) { |
413 |
| - throw raiseOSError(frame, OSErrorEnum.EPERM); |
| 421 | + private void execv(VirtualFrame frame, PosixPath path, Object argv, SequenceStorage argvStorage, |
| 422 | + PosixSupportLibrary posixLib, |
| 423 | + SequenceStorageNodes.ToArrayNode toArrayNode, |
| 424 | + ObjectToOpaquePathNode toOpaquePathNode, |
| 425 | + SysModuleBuiltins.AuditNode auditNode) { |
| 426 | + Object[] args = toArrayNode.execute(argvStorage); |
| 427 | + if (args.length < 1) { |
| 428 | + throw raise(ValueError, ErrorMessages.ARG_MUST_NOT_BE_EMPTY, "execv()", 2); |
414 | 429 | }
|
415 |
| - try { |
416 |
| - return doExecuteInternal(thisModule, path, args); |
417 |
| - } catch (Exception e) { |
418 |
| - throw raiseOSError(frame, e, path); |
| 430 | + Object[] opaqueArgs = new Object[args.length]; |
| 431 | + for (int i = 0; i < args.length; ++i) { |
| 432 | + opaqueArgs[i] = toOpaquePathNode.execute(frame, args[i]); |
419 | 433 | }
|
420 |
| - } |
| 434 | + //TODO ValueError "execv() arg 2 first element cannot be empty" |
| 435 | + |
| 436 | + auditNode.audit("os.exec", path.originalObject, argv, PNone.NONE); |
421 | 437 |
|
422 |
| - @TruffleBoundary |
423 |
| - Object doExecuteInternal(PythonModule thisModule, String path, PSequence args) throws IOException { |
424 |
| - int size = args.getSequenceStorage().length(); |
425 |
| - if (size == 0) { |
426 |
| - throw raise(ValueError, ErrorMessages.ARG_D_MUST_NOT_BE_EMPTY, 2); |
427 |
| - } |
428 |
| - String[] cmd = new String[size]; |
429 |
| - // We don't need the path variable because it's already in the array |
430 |
| - // but I need to process it for CI gate |
431 |
| - cmd[0] = path; |
432 |
| - for (int i = 0; i < size; i++) { |
433 |
| - cmd[i] = GetItemDynamicNode.getUncached().execute(args.getSequenceStorage(), i).toString(); |
434 |
| - } |
435 |
| - PDict environ = (PDict) thisModule.getAttribute("environ"); |
436 |
| - ProcessBuilder builder = new ProcessBuilder(cmd); |
437 |
| - Map<String, String> environment = builder.environment(); |
438 |
| - environ.entries().forEach(entry -> { |
439 |
| - environment.put(new String(toBytes.execute(entry.key)), new String(toBytes.execute(entry.value))); |
440 |
| - }); |
441 |
| - Process pr = builder.start(); |
442 |
| - BufferedReader bfr = new BufferedReader(new InputStreamReader(pr.getInputStream())); |
443 |
| - OutputStream stream = getContext().getEnv().out(); |
444 |
| - String line = ""; |
445 |
| - while ((line = bfr.readLine()) != null) { |
446 |
| - stream.write(line.getBytes()); |
447 |
| - stream.write("\n".getBytes()); |
448 |
| - } |
449 |
| - BufferedReader stderr = new BufferedReader(new InputStreamReader(pr.getErrorStream())); |
450 |
| - OutputStream errStream = getContext().getEnv().err(); |
451 |
| - line = ""; |
452 |
| - while ((line = stderr.readLine()) != null) { |
453 |
| - errStream.write(line.getBytes()); |
454 |
| - errStream.write("\n".getBytes()); |
455 |
| - } |
456 | 438 | try {
|
457 |
| - pr.waitFor(); |
458 |
| - } catch (InterruptedException e) { |
459 |
| - throw new IOException(e); |
| 439 | + posixLib.execv(getPosixSupport(), path.value, opaqueArgs); |
| 440 | + } catch (PosixException e) { |
| 441 | + throw raiseOSErrorFromPosixException(frame, e); |
460 | 442 | }
|
461 |
| - throw new PythonExitException(this, pr.exitValue()); |
| 443 | + throw CompilerDirectives.shouldNotReachHere("execv should not return normally"); |
462 | 444 | }
|
463 | 445 | }
|
464 | 446 |
|
@@ -2141,6 +2123,61 @@ PBytes doBytes(PBytes bytes) {
|
2141 | 2123 | }
|
2142 | 2124 | }
|
2143 | 2125 |
|
| 2126 | + /** |
| 2127 | + * Helper node that accepts either str or bytes and converts it to a representation specific to |
| 2128 | + * the {@link PosixSupportLibrary} in use. Basically equivalent of |
| 2129 | + * {@code PyUnicode_EncodeFSDefault}. |
| 2130 | + */ |
| 2131 | + abstract static class StringOrBytesToOpaquePathNode extends PNodeWithRaise { |
| 2132 | + abstract Object execute(Object obj); |
| 2133 | + |
| 2134 | + @Specialization(limit = "1") |
| 2135 | + Object doString(String str, |
| 2136 | + @CachedContext(PythonLanguage.class) PythonContext context, |
| 2137 | + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib) { |
| 2138 | + return checkPath(posixLib.createPathFromString(context.getPosixSupport(), str)); |
| 2139 | + } |
| 2140 | + |
| 2141 | + @Specialization(limit = "1") |
| 2142 | + Object doPString(PString pstr, |
| 2143 | + @Cached CastToJavaStringNode castToJavaStringNode, |
| 2144 | + @CachedContext(PythonLanguage.class) PythonContext context, |
| 2145 | + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib) { |
| 2146 | + String str = castToJavaStringNode.execute(pstr); |
| 2147 | + return checkPath(posixLib.createPathFromString(context.getPosixSupport(), str)); |
| 2148 | + } |
| 2149 | + |
| 2150 | + @Specialization(limit = "1") |
| 2151 | + Object doBytes(PBytes bytes, |
| 2152 | + @Cached BytesNodes.ToBytesNode toBytesNode, |
| 2153 | + @CachedContext(PythonLanguage.class) PythonContext context, |
| 2154 | + @CachedLibrary("context.getPosixSupport()") PosixSupportLibrary posixLib) { |
| 2155 | + return checkPath(posixLib.createPathFromBytes(context.getPosixSupport(), toBytesNode.execute(bytes))); |
| 2156 | + } |
| 2157 | + |
| 2158 | + private Object checkPath(Object path) { |
| 2159 | + if (path == null) { |
| 2160 | + throw raise(ValueError, ErrorMessages.EMBEDDED_NULL_BYTE); |
| 2161 | + } |
| 2162 | + return path; |
| 2163 | + } |
| 2164 | + } |
| 2165 | + |
| 2166 | + /** |
| 2167 | + * Similar to {@code PyUnicode_FSConverter}, but the actual conversion is delegated to the |
| 2168 | + * {@link PosixSupportLibrary} implementation. |
| 2169 | + */ |
| 2170 | + abstract static class ObjectToOpaquePathNode extends Node { |
| 2171 | + abstract Object execute(VirtualFrame frame, Object obj); |
| 2172 | + |
| 2173 | + @Specialization |
| 2174 | + Object doIt(VirtualFrame frame, Object obj, |
| 2175 | + @Cached FspathNode fspathNode, |
| 2176 | + @Cached StringOrBytesToOpaquePathNode stringOrBytesToOpaquePathNode) { |
| 2177 | + return stringOrBytesToOpaquePathNode.execute(fspathNode.call(frame, obj)); |
| 2178 | + } |
| 2179 | + } |
| 2180 | + |
2144 | 2181 | abstract static class ConvertToTimespecBaseNode extends PythonBuiltinBaseNode {
|
2145 | 2182 | abstract void execute(VirtualFrame frame, Object obj, long[] timespec, int offset);
|
2146 | 2183 | }
|
|
0 commit comments