diff --git a/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/DowncallSignature.java b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/DowncallSignature.java new file mode 100644 index 0000000000..274e9210bf --- /dev/null +++ b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/DowncallSignature.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +public @interface DowncallSignature { + NativeSimpleType returnType(); + + NativeSimpleType[] argumentTypes() default {}; + + Class retConversion() default void.class; + + Class[] argConversions() default {}; +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSimpleType.java b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/NativeSimpleType.java similarity index 95% rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSimpleType.java rename to graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/NativeSimpleType.java index c2d169efc3..d4750eba04 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSimpleType.java +++ b/graalpython/com.oracle.graal.python.annotations/src/com/oracle/graal/python/annotations/NativeSimpleType.java @@ -38,7 +38,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package com.oracle.graal.python.runtime.nativeaccess; +package com.oracle.graal.python.annotations; /** * Simple native carrier types used by native access signatures. @@ -55,5 +55,5 @@ public enum NativeSimpleType { SINT64, FLOAT, DOUBLE, - RAW_POINTER; // arg must be long, retval is long + POINTER; // raw pointer represented in Java as a long } diff --git a/graalpython/com.oracle.graal.python.processor/src/META-INF/services/javax.annotation.processing.Processor b/graalpython/com.oracle.graal.python.processor/src/META-INF/services/javax.annotation.processing.Processor index 84137ac45b..c56a7e9bd4 100644 --- a/graalpython/com.oracle.graal.python.processor/src/META-INF/services/javax.annotation.processing.Processor +++ b/graalpython/com.oracle.graal.python.processor/src/META-INF/services/javax.annotation.processing.Processor @@ -1,4 +1,5 @@ com.oracle.graal.python.processor.ArgumentClinicProcessor com.oracle.graal.python.processor.GenerateEnumConstantsProcessor +com.oracle.graal.python.processor.GenerateNativeDowncallsProcessor com.oracle.graal.python.processor.CApiBuiltinsProcessor -com.oracle.graal.python.processor.SlotsProcessor \ No newline at end of file +com.oracle.graal.python.processor.SlotsProcessor diff --git a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java index fd18b0b0bf..31f6d9e399 100644 --- a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java +++ b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/CApiBuiltinsProcessor.java @@ -40,6 +40,8 @@ */ package com.oracle.graal.python.processor; +import static com.oracle.graal.python.processor.NativeDowncallMethodHandleGenerator.argName; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -86,6 +88,7 @@ import com.oracle.graal.python.annotations.CApiFields; import com.oracle.graal.python.annotations.CApiStructs; import com.oracle.graal.python.annotations.CApiUpcallTarget; +import com.oracle.graal.python.annotations.NativeSimpleType; import com.sun.source.tree.VariableTree; import com.sun.source.util.TreePathScanner; import com.sun.source.util.Trees; @@ -291,10 +294,6 @@ private String capiTypeToForeignPrimitiveType(VariableElement element) { return type.equals("void") ? "void" : ("j" + type); } - private static String argName(int i) { - return "" + (char) ('a' + i); - } - private static final String CAPI_BUILTIN = "com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin"; private static final String CAPI_BUILTINS = "com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltins"; private static final String CAPI_WRAPPER_DESCRIPTOR = "com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.CApiWrapperDescriptor"; @@ -1261,21 +1260,6 @@ private TypeMirror toJavaNfiType(TypeMirror typeMirror) { return processingEnv.getTypeUtils().getPrimitiveType(TypeKind.LONG); } - private static String getNativeMethodHandleVarName(String signatureName) { - return "NATIVE_METHOD_HANDLE_" + signatureName; - } - - private static String toClassLiteral(String javaType) { - return switch (javaType) { - case "void" -> "void.class"; - case "int" -> "int.class"; - case "long" -> "long.class"; - case "float" -> "float.class"; - case "double" -> "double.class"; - default -> throw new IllegalArgumentException("Unexpected Java type: " + javaType); - }; - } - private boolean isCannotRaise(VariableElement signature) { String externalFunctionSignatureInitializer = getExternalFunctionSignatureInitializer(signature); int start = externalFunctionSignatureInitializer.indexOf('('); @@ -1348,7 +1332,6 @@ private void generateExternalFunctionInvoker(List argTypes = Arrays.stream(sig.argumentTypes).map(this::toJavaNfiType).toList(); boolean isVoidReturn = "void".equals(returnType); @@ -1381,14 +1364,7 @@ private void generateExternalFunctionInvoker(List methodTypeArgs = new ArrayList<>(); - methodTypeArgs.add("long.class"); - for (String argType : argTypes) { - methodTypeArgs.add(toClassLiteral(argType)); - } - lines.add(" private static final MethodHandle " + getNativeMethodHandleVarName(sig.name) + " = NativeAccessSupport.createDowncallHandle(" + - "MethodType.methodType(" + returnTypeLiteral + ", " + String.join(", ", methodTypeArgs) + "), false);"); + NativeDowncallMethodHandleGenerator.emitMethodHandleField(lines, NativeDowncallMethodHandleGenerator.methodHandleVarName(sig.name), returnType, argTypes); lines.add(""); if (sig.cannotRaise) { @@ -1464,9 +1440,9 @@ private void generateExternalFunctionInvoker(List ValueLayout.JAVA_LONG;"); lines.add(" case FLOAT -> ValueLayout.JAVA_FLOAT;"); lines.add(" case DOUBLE -> ValueLayout.JAVA_DOUBLE;"); - lines.add(" case RAW_POINTER -> ValueLayout.JAVA_LONG;"); + lines.add(" case POINTER -> ValueLayout.JAVA_LONG;"); lines.add(" };"); lines.add(" }"); lines.add("}"); @@ -1626,6 +1603,8 @@ private void generateDummyNativeAccessSupport(Element[] origins) throws IOExcept lines.add("import java.lang.invoke.MethodHandle;"); lines.add("import java.lang.invoke.MethodType;"); lines.add(""); + lines.add("import " + NativeSimpleType.class.getCanonicalName() + ";"); + lines.add(""); lines.add("public final class " + NATIVE_ACCESS_SUPPORT_IMPL_CLASS_NAME + " extends " + NATIVE_ACCESS_SUPPORT_CLASS_NAME + " {"); lines.add(" @Override"); lines.add(" protected Object createArenaImpl() {"); diff --git a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/GenerateNativeDowncallsProcessor.java b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/GenerateNativeDowncallsProcessor.java new file mode 100644 index 0000000000..ede35d6f5f --- /dev/null +++ b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/GenerateNativeDowncallsProcessor.java @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.processor; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic.Kind; + +import com.oracle.graal.python.annotations.DowncallSignature; +import com.oracle.graal.python.annotations.NativeSimpleType; + +public class GenerateNativeDowncallsProcessor extends AbstractProcessor { + private record NativeDowncallDesc(String name, String symbolName, NativeSimpleType returnType, List argumentTypes, List argumentNames) { + } + + @Override + public Set getSupportedAnnotationTypes() { + return Set.of(DowncallSignature.class.getName()); + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return true; + } + try { + doProcess(roundEnv); + } catch (IOException ex) { + ex.printStackTrace(); + } catch (ProcessingError ex) { + processingEnv.getMessager().printMessage(Kind.ERROR, ex.getMessage(), ex.getElement()); + } + return true; + } + + private void doProcess(RoundEnvironment roundEnv) throws IOException, ProcessingError { + Set invokerElements = new LinkedHashSet<>(); + for (Element element : roundEnv.getElementsAnnotatedWith(DowncallSignature.class)) { + invokerElements.add(validateDowncallSignature(element)); + } + for (TypeElement invokerElement : invokerElements) { + generateInvoker(invokerElement); + } + } + + private static TypeElement validateDowncallSignature(Element element) throws ProcessingError { + if (element.getKind() != ElementKind.METHOD) { + throw error(element, "@DowncallSignature can only annotate methods"); + } + Element enclosingElement = element.getEnclosingElement(); + if (enclosingElement == null || enclosingElement.getKind() != ElementKind.CLASS) { + throw error(element, "@DowncallSignature can only annotate methods in classes"); + } + if (!enclosingElement.getModifiers().contains(Modifier.ABSTRACT)) { + throw error(enclosingElement, "@DowncallSignature methods must be enclosed in an abstract class"); + } + if (!element.getModifiers().contains(Modifier.ABSTRACT)) { + throw error(element, "@DowncallSignature methods must be abstract"); + } + return (TypeElement) enclosingElement; + } + + private void generateInvoker(TypeElement invokerElement) throws IOException, ProcessingError { + List downcalls = collectDowncalls(invokerElement); + if (downcalls.isEmpty()) { + throw error(invokerElement, "Annotated class does not declare any downcalls"); + } + + String packageName = processingEnv.getElementUtils().getPackageOf(invokerElement).getQualifiedName().toString(); + String invokerQualifiedName = invokerElement.getQualifiedName().toString(); + String invokerTypeRef = invokerQualifiedName.startsWith(packageName + ".") ? invokerQualifiedName.substring(packageName.length() + 1) : invokerQualifiedName; + String className = invokerElement.getSimpleName() + "Gen"; + + ArrayList lines = new ArrayList<>(); + lines.add("// @formatter:off"); + lines.add("// Checkstyle: stop"); + lines.add("// Generated by annotation processor: " + getClass().getName()); + lines.add("package " + packageName + ";"); + lines.add(""); + lines.add("import java.lang.invoke.MethodHandle;"); + lines.add("import java.lang.invoke.MethodType;"); + lines.add("import java.util.concurrent.atomic.AtomicLongArray;"); + lines.add(""); + lines.add("import com.oracle.graal.python.runtime.nativeaccess.NativeAccessSupport;"); + lines.add("import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary;"); + lines.add("import com.oracle.truffle.api.CompilerDirectives;"); + lines.add("import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;"); + lines.add(""); + lines.add("final class " + className + " extends " + invokerTypeRef + " {"); + lines.add(" private final PythonContext context;"); + lines.add(" private final AtomicLongArray cachedFunctions = new AtomicLongArray(" + downcalls.size() + ");"); + lines.add(" private volatile NativeLibrary nativeLibrary;"); + lines.add(""); + + for (NativeDowncallDesc downcall : downcalls) { + NativeDowncallMethodHandleGenerator.emitMethodHandleField(lines, methodHandleName(downcall.name), downcall.returnType, downcall.argumentTypes); + } + + lines.add(""); + lines.add(" " + className + "(PythonContext context) {"); + lines.add(" this.context = context;"); + lines.add(" }"); + + for (int i = 0; i < downcalls.size(); i++) { + emitDowncallMethod(lines, downcalls.get(i), i); + } + + lines.add(""); + lines.add(" @TruffleBoundary"); + lines.add(" private long lookup(int functionIndex, String symbolName) {"); + lines.add(" long symbol = cachedFunctions.get(functionIndex);"); + lines.add(" if (symbol == 0) {"); + lines.add(" symbol = loadFunction(symbolName);"); + lines.add(" cachedFunctions.compareAndSet(functionIndex, 0, symbol);"); + lines.add(" symbol = cachedFunctions.get(functionIndex);"); + lines.add(" }"); + lines.add(" return symbol;"); + lines.add(" }"); + lines.add(""); + lines.add(" @TruffleBoundary"); + lines.add(" private long loadFunction(String symbolName) {"); + lines.add(" return ensureLibrary().lookupSymbol(symbolName);"); + lines.add(" }"); + lines.add(""); + lines.add(" @TruffleBoundary"); + lines.add(" private NativeLibrary ensureLibrary() {"); + lines.add(" NativeLibrary library = nativeLibrary;"); + lines.add(" if (library == null) {"); + lines.add(" library = " + invokerTypeRef + ".loadNativeLibrary(context);"); + lines.add(" nativeLibrary = library;"); + lines.add(" }"); + lines.add(" return library;"); + lines.add(" }"); + lines.add("}"); + + var file = processingEnv.getFiler().createSourceFile(packageName + "." + className, invokerElement); + try (var writer = file.openWriter()) { + writer.append(String.join(System.lineSeparator(), lines)); + } + } + + private static List collectDowncalls(TypeElement invokerElement) throws ProcessingError { + List result = new ArrayList<>(); + Set methodNames = new java.util.HashSet<>(); + for (Element enclosedElement : invokerElement.getEnclosedElements()) { + if (enclosedElement.getKind() == ElementKind.METHOD && enclosedElement.getAnnotation(DowncallSignature.class) != null) { + NativeDowncallDesc downcall = extractDowncall((ExecutableElement) enclosedElement); + if (!methodNames.add(downcall.name)) { + throw error(enclosedElement, "Duplicate downcall method name: %s", downcall.name); + } + result.add(downcall); + } + } + return result; + } + + private static NativeDowncallDesc extractDowncall(ExecutableElement method) throws ProcessingError { + DowncallSignature annotation = method.getAnnotation(DowncallSignature.class); + if (annotation == null) { + throw error(method, "Downcall method must be annotated with @DowncallSignature"); + } + + NativeSimpleType[] argTypes = annotation.argumentTypes(); + if (argTypes.length != method.getParameters().size()) { + throw error(method, "@DowncallSignature argumentTypes length must match method parameter count (%d != %d)", argTypes.length, method.getParameters().size()); + } + validateJavaType(method, method.getReturnType(), annotation.returnType()); + for (int i = 0; i < argTypes.length; i++) { + validateJavaType(method.getParameters().get(i), method.getParameters().get(i).asType(), argTypes[i]); + } + + List argumentTypes = List.of(argTypes); + List argumentNames = extractArgumentNames(method); + String symbolName = method.getSimpleName().toString(); + return new NativeDowncallDesc( + symbolName, + symbolName, + annotation.returnType(), + argumentTypes, + argumentNames); + } + + private static List extractArgumentNames(ExecutableElement method) throws ProcessingError { + List result = new ArrayList<>(method.getParameters().size()); + for (VariableElement parameter : method.getParameters()) { + String argName = parameter.getSimpleName().toString(); + if (argName.isBlank()) { + throw error(parameter, "Downcall parameter name must not be blank"); + } + if (!SourceVersion.isIdentifier(argName) || SourceVersion.isKeyword(argName)) { + throw error(parameter, "Downcall parameter name is not a valid Java identifier: %s", argName); + } + result.add(argName); + } + return result; + } + + private static void validateJavaType(Element element, TypeMirror actualType, NativeSimpleType nativeType) throws ProcessingError { + TypeKind expected = switch (nativeType) { + case VOID -> TypeKind.VOID; + case SINT8 -> TypeKind.BYTE; + case SINT16 -> TypeKind.SHORT; + case SINT32 -> TypeKind.INT; + case SINT64, POINTER -> TypeKind.LONG; + case FLOAT -> TypeKind.FLOAT; + case DOUBLE -> TypeKind.DOUBLE; + }; + if (actualType.getKind() != expected) { + throw error(element, "Java type %s does not match native type %s", actualType, nativeType); + } + } + + private static ProcessingError error(Element element, String fmt, Object... args) throws ProcessingError { + throw new ProcessingError(element, fmt, args); + } + + private static String nativeSimpleTypeToJavaType(NativeSimpleType type) { + return switch (type) { + case VOID -> "void"; + case SINT8 -> "byte"; + case SINT16 -> "short"; + case SINT32 -> "int"; + case SINT64, POINTER -> "long"; + case FLOAT -> "float"; + case DOUBLE -> "double"; + }; + } + + private static void emitDowncallMethod(List lines, NativeDowncallDesc downcall, int functionIndex) { + lines.add(""); + lines.add(" @TruffleBoundary(allowInlining = true, transferToInterpreterOnException = false)"); + lines.add(" @Override"); + lines.add(" " + nativeSimpleTypeToJavaType(downcall.returnType) + " " + downcall.name + "(" + typedArgs(downcall.argumentTypes, downcall.argumentNames) + ") {"); + lines.add(" long functionPointer = lookup(" + functionIndex + ", " + stringLiteral(downcall.symbolName) + ");"); + lines.add(" try {"); + if (NativeSimpleType.VOID == downcall.returnType) { + lines.add(" " + methodHandleName(downcall.name) + ".invokeExact(" + invokeArgs(downcall.argumentNames) + ");"); + } else { + lines.add(" return (" + nativeSimpleTypeToJavaType(downcall.returnType) + ") " + methodHandleName(downcall.name) + ".invokeExact(" + invokeArgs(downcall.argumentNames) + ");"); + } + lines.add(" } catch (Throwable t) {"); + lines.add(" throw CompilerDirectives.shouldNotReachHere(t);"); + lines.add(" }"); + lines.add(" }"); + } + + private static String methodHandleName(String downcallName) { + return NativeDowncallMethodHandleGenerator.methodHandleVarName(downcallName.toUpperCase()); + } + + private static String typedArgs(List argTypes, List argNames) { + List args = new ArrayList<>(argTypes.size()); + for (int i = 0; i < argTypes.size(); i++) { + args.add(nativeSimpleTypeToJavaType(argTypes.get(i)) + " " + argNames.get(i)); + } + return String.join(", ", args); + } + + private static String invokeArgs(List argNames) { + String args = String.join(", ", argNames); + return args.isEmpty() ? "functionPointer" : "functionPointer, " + args; + } + + private static String stringLiteral(String value) { + return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\""; + } +} diff --git a/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/NativeDowncallMethodHandleGenerator.java b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/NativeDowncallMethodHandleGenerator.java new file mode 100644 index 0000000000..e31e059c70 --- /dev/null +++ b/graalpython/com.oracle.graal.python.processor/src/com/oracle/graal/python/processor/NativeDowncallMethodHandleGenerator.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.processor; + +import java.util.ArrayList; +import java.util.List; + +import com.oracle.graal.python.annotations.NativeSimpleType; + +final class NativeDowncallMethodHandleGenerator { + private NativeDowncallMethodHandleGenerator() { + } + + static String argName(int i) { + if (i >= 26) { + throw new IllegalArgumentException("Generated downcall stubs support at most 26 synthetic argument names (a-z), got index " + i); + } + return "" + (char) ('a' + i); + } + + static String methodHandleVarName(String signatureName) { + return "NATIVE_METHOD_HANDLE_" + signatureName; + } + + static String toClassLiteral(String javaType) { + return switch (javaType) { + case "void" -> "void.class"; + case "byte" -> "byte.class"; + case "short" -> "short.class"; + case "int" -> "int.class"; + case "long" -> "long.class"; + case "float" -> "float.class"; + case "double" -> "double.class"; + default -> throw new IllegalArgumentException("Unexpected Java type: " + javaType); + }; + } + + static String toClassLiteral(NativeSimpleType nativeType) { + return switch (nativeType) { + case VOID -> "void.class"; + case SINT8 -> "byte.class"; + case SINT16 -> "short.class"; + case SINT32 -> "int.class"; + case SINT64, POINTER -> "long.class"; + case FLOAT -> "float.class"; + case DOUBLE -> "double.class"; + }; + } + + static void emitMethodHandleField(List lines, String fieldName, String returnType, List argTypes) { + List methodTypeArgs = new ArrayList<>(); + methodTypeArgs.add("long.class"); + for (String argType : argTypes) { + methodTypeArgs.add(toClassLiteral(argType)); + } + lines.add(" private static final MethodHandle " + fieldName + " = NativeAccessSupport.createDowncallHandle(" + + "MethodType.methodType(" + toClassLiteral(returnType) + ", " + String.join(", ", methodTypeArgs) + "), false);"); + } + + static void emitMethodHandleField(List lines, String fieldName, NativeSimpleType returnType, List argTypes) { + List methodTypeArgs = new ArrayList<>(); + methodTypeArgs.add("long.class"); + for (NativeSimpleType argType : argTypes) { + methodTypeArgs.add(toClassLiteral(argType)); + } + lines.add(" private static final MethodHandle " + fieldName + " = NativeAccessSupport.createDowncallHandle(" + + "MethodType.methodType(" + toClassLiteral(returnType) + ", " + String.join(", ", methodTypeArgs) + "), false);"); + } +} diff --git a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/TpSlotsTests.java b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/TpSlotsTests.java index f188f9974c..85c30f5d3f 100644 --- a/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/TpSlotsTests.java +++ b/graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/builtin/objects/TpSlotsTests.java @@ -49,6 +49,7 @@ import org.junit.BeforeClass; import org.junit.Test; +import com.oracle.graal.python.annotations.NativeSimpleType; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TpSlots.Builder; import com.oracle.graal.python.builtins.objects.type.TpSlots.TpSlotGroup; @@ -57,7 +58,6 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlot.TpSlotNative; import com.oracle.graal.python.runtime.nativeaccess.NativeContext; import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer; -import com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType; import com.oracle.graal.python.util.Function; public class TpSlotsTests { diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py b/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py index 5629fd5401..6511da2dca 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_posix.py @@ -258,6 +258,18 @@ def __fspath__(self): with self.assertRaisesRegex(TypeError, r"expected C.__fspath__\(\) to return str or bytes, not bytearray"): os.open(C(), 0) + def test_open_bytes_path(self): + try: + with open(os.fsencode(TEST_FULL_PATH1), os.O_WRONLY | os.O_CREAT) as fd: + os.write(fd, b'hello') + with open(os.fsencode(TEST_FULL_PATH1), os.O_RDONLY) as fd: + self.assertEqual(b'hello', os.read(fd, 5)) + finally: + try: + os.unlink(TEST_FULL_PATH1) + except Exception: + pass + def test_fd_converter(self): class MyInt(int): def fileno(self): return 0 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_resource.py b/graalpython/com.oracle.graal.python.test/src/tests/test_resource.py index 5133726973..4ae2b0179b 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_resource.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_resource.py @@ -1,4 +1,4 @@ -# Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # The Universal Permissive License (UPL), Version 1.0 @@ -37,9 +37,18 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import sys +import unittest + +try: + _POSIX_MODULE_BACKEND = __graalpython__.posix_module_backend() +except Exception: + _POSIX_MODULE_BACKEND = "cpython" + +_NATIVE_POSIX_LINUX = sys.platform == "linux" and _POSIX_MODULE_BACKEND == "native" + def test_import(): - import sys if sys.platform not in ['darwin', 'linux']: return imported = True @@ -50,7 +59,7 @@ def test_import(): assert imported -def test_gerusage(): +def test_getrusage(): from resource import getrusage, RUSAGE_SELF try: from resource import RUSAGE_THREAD @@ -66,3 +75,18 @@ def test_gerusage(): assert ru.ru_utime >= 0 assert ru.ru_stime >= 0 assert ru.ru_maxrss > 0 + + +@unittest.skipUnless(_NATIVE_POSIX_LINUX, "Requires native POSIX backend on Linux") +def test_gerusage_cpu_time_progress(): + import time + from resource import getrusage, RUSAGE_SELF + + start = getrusage(RUSAGE_SELF) + deadline = time.monotonic() + 0.25 + value = 0 + while time.monotonic() < deadline: + value = (value * 3 + 1) % 1000003 + end = getrusage(RUSAGE_SELF) + assert value >= 0 + assert (end.ru_utime + end.ru_stime) - (start.ru_utime + start.ru_stime) > 1e-3 diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_socket.py b/graalpython/com.oracle.graal.python.test/src/tests/test_socket.py index c49686d2c3..12b7826de8 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_socket.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_socket.py @@ -110,3 +110,33 @@ def server(): sock.recv_into(buffer, 1) assert b == b'123' thread.join() + + +def test_sendto_recvfrom_roundtrip(): + try: + if __graalpython__.posix_module_backend() == "java": + return + except NameError: + pass + + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as server: + server.bind(("127.0.0.1", 0)) + server_addr = server.getsockname() + + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as client: + client.bind(("127.0.0.1", 0)) + client_addr = client.getsockname() + + sent = client.sendto(b"phase5", server_addr) + assert sent == 6 + + data, addr = server.recvfrom(64) + assert data == b"phase5" + assert addr == client_addr + + sent = server.sendto(b"reply", client_addr) + assert sent == 5 + + data, addr = client.recvfrom(64) + assert data == b"reply" + assert addr == server_addr diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_threading.py b/graalpython/com.oracle.graal.python.test/src/tests/test_threading.py index d1e6076bb7..c3401a8efc 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_threading.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_threading.py @@ -37,9 +37,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. +import sys +import unittest + def test_stuck_thread(): - import sys import subprocess if sys.implementation.name == 'graalpy' and __graalpython__.posix_module_backend() == 'java': @@ -64,7 +66,6 @@ def test_stuck_thread(): def test_threads_joining_main_thread_at_shutdown(): import subprocess - import sys script = r""" import threading @@ -80,3 +81,36 @@ def f(): """ result = subprocess.run([sys.executable, "-c", script], stdout=subprocess.PIPE, stderr=subprocess.PIPE) assert result.returncode == 0, result.stderr.decode("utf-8", "replace") + + +@unittest.skipIf(sys.implementation.name == "graalpy", "Blocked on Truffle API support for blocking native reads during thread-local handshakes") +# see GR-75767 for details +def test_blocking_os_read_thread_does_not_deadlock_import_re(): + import subprocess + + if sys.platform == "win32": + return + + script = r""" +import os +import threading +import sys + +read_fd, write_fd = os.pipe() +threading.Thread(target=lambda: os.read(read_fd, 1), daemon=True).start() + +import re + +print("ok", flush=True) +os._exit(0) +""" + + result = subprocess.run( + [sys.executable, "-c", script], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + timeout=10, + ) + assert result.returncode == 0, result.stderr + assert result.stdout.rstrip().endswith("ok"), result.stdout diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java index 68323f962f..b77f1de174 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java @@ -113,6 +113,7 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.CApiConstant; +import com.oracle.graal.python.annotations.NativeSimpleType; import com.oracle.graal.python.builtins.Python3Core; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.modules.GraalPythonModuleBuiltins.DebugNode; @@ -192,7 +193,6 @@ import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.exception.PythonErrorType; import com.oracle.graal.python.runtime.nativeaccess.NativeSignature; -import com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.MroSequenceStorage; import com.oracle.graal.python.runtime.sequence.storage.NativeByteSequenceStorage; @@ -636,7 +636,7 @@ Object getErrorReturnValue() { case SINT64 -> -1L; case FLOAT -> -1.0f; case DOUBLE -> -1.0; - case RAW_POINTER -> NULLPTR; + case POINTER -> NULLPTR; }; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java index ce3b3aaa84..cee5f0aa77 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java @@ -46,11 +46,11 @@ import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readIntField; import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField; import static com.oracle.graal.python.builtins.objects.object.PythonObject.IMMORTAL_REFCNT; -import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___FILE__; import static com.oracle.graal.python.nodes.StringLiterals.T_DASH; import static com.oracle.graal.python.nodes.StringLiterals.T_EMPTY_STRING; import static com.oracle.graal.python.nodes.StringLiterals.T_UNDERSCORE; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.util.PythonUtils.toTruffleStringUncached; import static com.oracle.graal.python.util.PythonUtils.tsLiteral; @@ -79,6 +79,7 @@ import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.annotations.CApiConstant; +import com.oracle.graal.python.annotations.NativeSimpleType; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.modules.cext.PythonCApiAssertions; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltinRegistry; @@ -111,14 +112,6 @@ import com.oracle.graal.python.builtins.objects.str.StringNodes; import com.oracle.graal.python.builtins.objects.str.StringUtils; import com.oracle.graal.python.builtins.objects.thread.PLock; -import com.oracle.graal.python.runtime.nativeaccess.NativeAccessSupport; -import com.oracle.graal.python.runtime.nativeaccess.NativeContext; -import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer; -import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary; -import com.oracle.graal.python.runtime.nativeaccess.NativeLibraryLoadException; -import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; -import com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType; -import com.oracle.graal.python.runtime.nativeaccess.NativeSignature; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.object.GetClassNode; @@ -131,6 +124,13 @@ import com.oracle.graal.python.runtime.PythonContext.PythonThreadState; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.graal.python.runtime.nativeaccess.NativeAccessSupport; +import com.oracle.graal.python.runtime.nativeaccess.NativeContext; +import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer; +import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary; +import com.oracle.graal.python.runtime.nativeaccess.NativeLibraryLoadException; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; +import com.oracle.graal.python.runtime.nativeaccess.NativeSignature; import com.oracle.graal.python.util.ConcurrentWeakSet; import com.oracle.graal.python.util.Function; import com.oracle.graal.python.util.PythonSystemThreadTask; @@ -811,8 +811,8 @@ void runBackgroundGCTask(PythonContext context) { /** * Called from native threads before using the C API. Returns * {@link #GRAALPY_ATTACH_NATIVE_OWNED} if this method entered the context and a matching - * {@link #detachNativeThread()} is required, {@link #GRAALPY_ATTACH_NATIVE_FOREIGN} if the context - * was already active on this thread from Truffle/Java/Python, and + * {@link #detachNativeThread()} is required, {@link #GRAALPY_ATTACH_NATIVE_FOREIGN} if the + * context was already active on this thread from Truffle/Java/Python, and * {@link #GRAALPY_ATTACH_NATIVE_FAILED} if entering failed. */ private int attachNativeThread() { @@ -957,7 +957,8 @@ private static CApiContext loadCApi(Node node, PythonContext context, TruffleStr if (PythonContext.isCurrentThreadVirtual()) { throw new ApiInitException(ErrorMessages.NATIVE_EXTENSIONS_VIRTUAL_THREAD); } - // Check this before marking the API as loaded so that we don't get a different error the second time a C import is attempted + // Check this before marking the API as loaded so that we don't get a different error + // the second time a C import is attempted if (!NativeAccessSupport.isAvailable()) { throw new ImportException(null, name, path, toTruffleStringUncached(NativeContext.UNAVAILABLE)); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java index 8890221a5c..f0d2945329 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/PyCFunctionWrapper.java @@ -50,6 +50,7 @@ import com.oracle.graal.python.annotations.Builtin; import com.oracle.graal.python.annotations.CApiUpcallTarget; +import com.oracle.graal.python.annotations.NativeSimpleType; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTiming; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.NativeToPythonInternalNode; @@ -67,7 +68,6 @@ import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.nativeaccess.NativeSignature; -import com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.RootCallTarget; @@ -86,10 +86,10 @@ */ public abstract class PyCFunctionWrapper { - private static final NativeSignature SIGNATURE_1_ARG = NativeSignature.create(NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER); - private static final NativeSignature SIGNATURE_2_ARG = NativeSignature.create(NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER); - private static final NativeSignature SIGNATURE_3_ARG = NativeSignature.create(NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER, - NativeSimpleType.RAW_POINTER); + private static final NativeSignature SIGNATURE_1_ARG = NativeSignature.create(NativeSimpleType.POINTER, NativeSimpleType.POINTER); + private static final NativeSignature SIGNATURE_2_ARG = NativeSignature.create(NativeSimpleType.POINTER, NativeSimpleType.POINTER, NativeSimpleType.POINTER); + private static final NativeSignature SIGNATURE_3_ARG = NativeSignature.create(NativeSimpleType.POINTER, NativeSimpleType.POINTER, NativeSimpleType.POINTER, + NativeSimpleType.POINTER); private static final MethodHandle HANDLE_UNARY; private static final MethodHandle HANDLE_BINARY; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TpSlotWrapper.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TpSlotWrapper.java index 5ac79f6293..33b269e2a5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TpSlotWrapper.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/TpSlotWrapper.java @@ -40,11 +40,11 @@ */ package com.oracle.graal.python.builtins.objects.cext.capi; +import static com.oracle.graal.python.annotations.NativeSimpleType.POINTER; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT32; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT64; import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.checkThrowableBeforeNative; import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; -import static com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType.RAW_POINTER; -import static com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType.SINT32; -import static com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType.SINT64; import static com.oracle.graal.python.util.PythonUtils.EMPTY_OBJECT_ARRAY; import java.lang.invoke.MethodHandle; @@ -104,16 +104,16 @@ public abstract class TpSlotWrapper { - private static final NativeSignature SIGNATURE_P_P = NativeSignature.create(RAW_POINTER, RAW_POINTER); - private static final NativeSignature SIGNATURE_P_PP = NativeSignature.create(RAW_POINTER, RAW_POINTER, RAW_POINTER); - private static final NativeSignature SIGNATURE_P_PPP = NativeSignature.create(RAW_POINTER, RAW_POINTER, RAW_POINTER, RAW_POINTER); - private static final NativeSignature SIGNATURE_P_PPI = NativeSignature.create(RAW_POINTER, RAW_POINTER, RAW_POINTER, SINT32); - private static final NativeSignature SIGNATURE_P_PL = NativeSignature.create(RAW_POINTER, RAW_POINTER, SINT64); - private static final NativeSignature SIGNATURE_I_P = NativeSignature.create(SINT32, RAW_POINTER); - private static final NativeSignature SIGNATURE_I_PP = NativeSignature.create(SINT32, RAW_POINTER, RAW_POINTER); - private static final NativeSignature SIGNATURE_I_PPP = NativeSignature.create(SINT32, RAW_POINTER, RAW_POINTER, RAW_POINTER); - private static final NativeSignature SIGNATURE_I_PLP = NativeSignature.create(SINT32, RAW_POINTER, SINT64, RAW_POINTER); - private static final NativeSignature SIGNATURE_L_P = NativeSignature.create(SINT64, RAW_POINTER); + private static final NativeSignature SIGNATURE_P_P = NativeSignature.create(POINTER, POINTER); + private static final NativeSignature SIGNATURE_P_PP = NativeSignature.create(POINTER, POINTER, POINTER); + private static final NativeSignature SIGNATURE_P_PPP = NativeSignature.create(POINTER, POINTER, POINTER, POINTER); + private static final NativeSignature SIGNATURE_P_PPI = NativeSignature.create(POINTER, POINTER, POINTER, SINT32); + private static final NativeSignature SIGNATURE_P_PL = NativeSignature.create(POINTER, POINTER, SINT64); + private static final NativeSignature SIGNATURE_I_P = NativeSignature.create(SINT32, POINTER); + private static final NativeSignature SIGNATURE_I_PP = NativeSignature.create(SINT32, POINTER, POINTER); + private static final NativeSignature SIGNATURE_I_PPP = NativeSignature.create(SINT32, POINTER, POINTER, POINTER); + private static final NativeSignature SIGNATURE_I_PLP = NativeSignature.create(SINT32, POINTER, SINT64, POINTER); + private static final NativeSignature SIGNATURE_L_P = NativeSignature.create(SINT64, POINTER); private static final MethodHandle HANDLE_GET_ATTR; private static final MethodHandle HANDLE_BINARY_SLOT_FUNC; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java index 5414762822..ca6f0fc009 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/transitions/ArgDescriptor.java @@ -40,6 +40,7 @@ */ package com.oracle.graal.python.builtins.objects.cext.capi.transitions; +import com.oracle.graal.python.annotations.NativeSimpleType; import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.FromLongNode; import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ToNativeBorrowedNode; import com.oracle.graal.python.builtins.objects.cext.capi.ExternalFunctionNodes.ToPythonStringNode; @@ -52,30 +53,29 @@ import com.oracle.graal.python.builtins.objects.cext.capi.transitions.CApiTransitions.PythonToNativeNode; import com.oracle.graal.python.builtins.objects.cext.common.CExtToJavaNode; import com.oracle.graal.python.builtins.objects.cext.common.CExtToNativeNode; -import com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType; import com.oracle.graal.python.util.Supplier; enum ArgBehavior { PyObject( - NativeSimpleType.RAW_POINTER, + NativeSimpleType.POINTER, PythonToNativeNode::create, NativeToPythonNode::create, NativeToPythonNode.getUncached(), PythonToNativeNewRefNode::create, NativeToPythonTransferNode::create, NativeToPythonTransferNode.getUncached()), - PyObjectBorrowed(NativeSimpleType.RAW_POINTER, ToNativeBorrowedNode::new, NativeToPythonNode::create, NativeToPythonNode.getUncached(), null, null, null), - PyObjectAsTruffleString(NativeSimpleType.RAW_POINTER, null, ToPythonStringNode::create, ToPythonStringNode.getUncached(), null, null, null), + PyObjectBorrowed(NativeSimpleType.POINTER, ToNativeBorrowedNode::new, NativeToPythonNode::create, NativeToPythonNode.getUncached(), null, null, null), + PyObjectAsTruffleString(NativeSimpleType.POINTER, null, ToPythonStringNode::create, ToPythonStringNode.getUncached(), null, null, null), PyTypeObject( - NativeSimpleType.RAW_POINTER, + NativeSimpleType.POINTER, PythonToNativeNode::create, NativeToPythonClassNode::create, NativeToPythonClassNode.getUncached(), PythonToNativeNewRefNode::create, NativeToPythonTransferNode::create, NativeToPythonTransferNode.getUncached()), - Pointer(NativeSimpleType.RAW_POINTER), - TruffleStringPointer(NativeSimpleType.RAW_POINTER, null, CharPtrToPythonNode::create, CharPtrToPythonNode.getUncached()), + Pointer(NativeSimpleType.POINTER), + TruffleStringPointer(NativeSimpleType.POINTER, null, CharPtrToPythonNode::create, CharPtrToPythonNode.getUncached()), Char8(NativeSimpleType.SINT8), UChar8(NativeSimpleType.SINT8), Char16(NativeSimpleType.SINT16), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativeCExtSymbol.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativeCExtSymbol.java index e6833a9631..d6981aab03 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativeCExtSymbol.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/common/NativeCExtSymbol.java @@ -40,10 +40,10 @@ */ package com.oracle.graal.python.builtins.objects.cext.common; +import com.oracle.graal.python.annotations.NativeSimpleType; import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor; import com.oracle.graal.python.runtime.nativeaccess.NativeContext; import com.oracle.graal.python.runtime.nativeaccess.NativeFunctionPointer; -import com.oracle.graal.python.runtime.nativeaccess.NativeSimpleType; import com.oracle.truffle.api.strings.TruffleString; public interface NativeCExtSymbol { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/PMMap.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/PMMap.java index 6d95332953..836b012b3d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/PMMap.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/mmap/PMMap.java @@ -48,7 +48,7 @@ import com.oracle.graal.python.nodes.PConstructAndRaiseNode; import com.oracle.graal.python.nodes.util.CastToJavaIntExactNode; import com.oracle.graal.python.runtime.AsyncHandler; -import com.oracle.graal.python.runtime.NFIPosixSupport; +import com.oracle.graal.python.runtime.NativePosixSupport; import com.oracle.graal.python.runtime.PosixSupportLibrary; import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; import com.oracle.graal.python.runtime.PythonContext; @@ -211,7 +211,7 @@ void close(PosixSupportLibrary posixLib, Object posixSupport) { @ExportMessage boolean isNative( @Bind Node inliningTarget) { - return PythonContext.get(inliningTarget).getPosixSupport() instanceof NFIPosixSupport; + return PythonContext.get(inliningTarget).getPosixSupport() instanceof NativePosixSupport; } @ExportMessage diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java deleted file mode 100644 index a15d44f747..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixSupport.java +++ /dev/null @@ -1,2810 +0,0 @@ -/* - * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The Universal Permissive License (UPL), Version 1.0 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -// skip GIL -package com.oracle.graal.python.runtime; - -import static com.oracle.graal.python.nodes.StringLiterals.J_DEFAULT; -import static com.oracle.graal.python.nodes.StringLiterals.J_NATIVE; -import static com.oracle.graal.python.nodes.StringLiterals.J_NFI_LANGUAGE; -import static com.oracle.graal.python.nodes.StringLiterals.T_NATIVE; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_IN_ADDR_S_ADDR; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY; -import static com.oracle.graal.python.runtime.NFIPosixConstants.OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH; -import static com.oracle.graal.python.runtime.NFIPosixConstants.SIZEOF_STRUCT_SOCKADDR_IN; -import static com.oracle.graal.python.runtime.NFIPosixConstants.SIZEOF_STRUCT_SOCKADDR_IN6; -import static com.oracle.graal.python.runtime.NFIPosixConstants.SIZEOF_STRUCT_SOCKADDR_SA_FAMILY; -import static com.oracle.graal.python.runtime.NFIPosixConstants.SIZEOF_STRUCT_SOCKADDR_STORAGE; -import static com.oracle.graal.python.runtime.NFIPosixConstants.SIZEOF_STRUCT_SOCKADDR_UN_SUN_PATH; -import static com.oracle.graal.python.runtime.PosixConstants.AF_INET; -import static com.oracle.graal.python.runtime.PosixConstants.AF_INET6; -import static com.oracle.graal.python.runtime.PosixConstants.AF_UNIX; -import static com.oracle.graal.python.runtime.PosixConstants.AF_UNSPEC; -import static com.oracle.graal.python.runtime.PosixConstants.HOST_NAME_MAX; -import static com.oracle.graal.python.runtime.PosixConstants.INET6_ADDRSTRLEN; -import static com.oracle.graal.python.runtime.PosixConstants.INET_ADDRSTRLEN; -import static com.oracle.graal.python.runtime.PosixConstants.L_ctermid; -import static com.oracle.graal.python.runtime.PosixConstants.NI_MAXHOST; -import static com.oracle.graal.python.runtime.PosixConstants.NI_MAXSERV; -import static com.oracle.graal.python.runtime.PosixConstants.PATH_MAX; -import static com.oracle.graal.python.runtime.PosixConstants.WNOHANG; -import static com.oracle.graal.python.runtime.PosixConstants._POSIX_HOST_NAME_MAX; -import static com.oracle.graal.python.runtime.PosixSupportLibrary.POSIX_FILENAME_SEPARATOR; -import static com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; -import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR; -import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR_BE; -import static com.oracle.graal.python.util.PythonUtils.EMPTY_LONG_ARRAY; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; -import static com.oracle.graal.python.util.PythonUtils.callCallTarget; -import static com.oracle.truffle.api.CompilerDirectives.SLOWPATH_PROBABILITY; -import static com.oracle.truffle.api.CompilerDirectives.injectBranchProbability; -import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere; -import static com.oracle.truffle.api.strings.TruffleString.Encoding.UTF_8; - -import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; - -import org.graalvm.nativeimage.ImageInfo; - -import com.oracle.graal.python.PythonLanguage; -import com.oracle.graal.python.annotations.PythonOS; -import com.oracle.graal.python.builtins.PythonBuiltinClassType; -import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; -import com.oracle.graal.python.nodes.ErrorMessages; -import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.runtime.PosixSupportLibrary.AcceptResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.AddrInfoCursor; -import com.oracle.graal.python.runtime.PosixSupportLibrary.AddrInfoCursorLibrary; -import com.oracle.graal.python.runtime.PosixSupportLibrary.Buffer; -import com.oracle.graal.python.runtime.PosixSupportLibrary.GetAddrInfoException; -import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet4SockAddr; -import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet6SockAddr; -import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidAddressException; -import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidUnixSocketPathException; -import com.oracle.graal.python.runtime.PosixSupportLibrary.OpenPtyResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; -import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; -import com.oracle.graal.python.runtime.PosixSupportLibrary.PwdResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.RecvfromResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.RusageResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.SelectResult; -import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval; -import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr; -import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddrLibrary; -import com.oracle.graal.python.runtime.PosixSupportLibrary.UnixSockAddr; -import com.oracle.graal.python.util.FunctionWithSignature; -import com.oracle.graal.python.util.OverflowException; -import com.oracle.graal.python.util.PythonUtils; -import com.oracle.truffle.api.ArrayUtils; -import com.oracle.truffle.api.CompilerAsserts; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.TruffleFile; -import com.oracle.truffle.api.TruffleLanguage.Env; -import com.oracle.truffle.api.TruffleLogger; -import com.oracle.truffle.api.TruffleSafepoint; -import com.oracle.truffle.api.dsl.Bind; -import com.oracle.truffle.api.dsl.Cached; -import com.oracle.truffle.api.dsl.Cached.Shared; -import com.oracle.truffle.api.dsl.NeverDefault; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.interop.ArityException; -import com.oracle.truffle.api.interop.InteropLibrary; -import com.oracle.truffle.api.interop.UnknownIdentifierException; -import com.oracle.truffle.api.interop.UnsupportedMessageException; -import com.oracle.truffle.api.interop.UnsupportedTypeException; -import com.oracle.truffle.api.library.CachedLibrary; -import com.oracle.truffle.api.library.ExportLibrary; -import com.oracle.truffle.api.library.ExportMessage; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.strings.TruffleString; -import com.oracle.truffle.nfi.api.SignatureLibrary; - -import sun.misc.Unsafe; - -/** - * Implementation that invokes the native POSIX functions directly using NFI. This requires either - * that the native access is allowed or to configure managed LLVM backend for NFI. - */ -@ExportLibrary(PosixSupportLibrary.class) -public final class NFIPosixSupport extends PosixSupport { - private static final String SUPPORTING_NATIVE_LIB_NAME = "posix"; - - private static final int UNAME_BUF_LENGTH = 256; - private static final int DIRENT_NAME_BUF_LENGTH = 256; - private static final int PWD_OUTPUT_LEN = 5; - private static final int PWD_BUFFER_MAX_SIZE = Integer.MAX_VALUE >> 2; - - private static final int MAX_READ = Integer.MAX_VALUE / 2; - - private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NFIPosixSupport.class); - - private static final Unsafe UNSAFE = PythonUtils.initUnsafe(); - - private static final Object CRYPT_LOCK = new Object(); - - private enum PosixNativeFunction { - init_constants("([sint64], sint32):sint32"), - - get_errno("():sint32"), - set_errno("(sint32):void"), - call_mmap("(sint64, sint32, sint32, sint32, sint64):sint64"), - call_munmap("(sint64, sint64):sint32"), - call_msync("(sint64, sint64, sint64):void"), - call_strerror("(sint32, [sint8], sint32):void"), - call_getpid("():sint64"), - call_umask("(sint32):sint32"), - call_openat("(sint32, [sint8], sint32, sint32):sint32"), - call_close("(sint32):sint32"), - call_read("(sint32, [sint8], uint64):sint64"), - call_write("(sint32, [sint8], uint64):sint64"), - call_dup("(sint32):sint32"), - call_dup2("(sint32, sint32, sint32):sint32"), - call_pipe2("([sint32]):sint32"), - call_select("(sint32, [sint32], sint32, [sint32], sint32, [sint32], sint32, sint64, sint64, [sint8]):sint32"), - call_poll("(sint32, sint32, sint64, sint64):sint32"), - call_lseek("(sint32, sint64, sint32):sint64"), - call_ftruncate("(sint32, sint64):sint32"), - call_truncate("([sint8], sint64):sint32"), - call_fsync("(sint32):sint32"), - call_flock("(sint32, sint32):sint32"), - call_fcntl_lock("(sint32, sint32, sint32, sint32, sint64, sint64):sint32"), - call_fstatat("(sint32, [sint8], sint32, [sint64]):sint32"), - call_fstat("(sint32, [sint64]):sint32"), - call_statvfs("([sint8], [sint64]):sint32"), - call_fstatvfs("(sint32, [sint64]):sint32"), - call_uname("([sint8], [sint8], [sint8], [sint8], [sint8], sint32):sint32"), - call_unlinkat("(sint32, [sint8], sint32):sint32"), - call_linkat("(sint32, [sint8], sint32, [sint8], sint32):sint32"), - call_symlinkat("([sint8], sint32, [sint8]):sint32"), - call_mkdirat("(sint32, [sint8], sint32):sint32"), - call_getcwd("([sint8], uint64):sint32"), - call_chdir("([sint8]):sint32"), - call_fchdir("(sint32):sint32"), - call_isatty("(sint32):sint32"), - call_opendir("([sint8]):sint64"), - call_fdopendir("(sint32):sint64"), - call_closedir("(sint64):sint32"), - call_readdir("(sint64, [sint8], uint64, [sint64]):sint32"), - call_rewinddir("(sint64):void"), - call_utimensat("(sint32, [sint8], [sint64], sint32):sint32"), - call_futimens("(sint32, [sint64]):sint32"), - call_futimes("(sint32, [sint64]):sint32"), - call_lutimes("([sint8], [sint64]):sint32"), - call_utimes("([sint8], [sint64]):sint32"), - call_renameat("(sint32, [sint8], sint32, [sint8]):sint32"), - call_faccessat("(sint32, [sint8], sint32, sint32, sint32):sint32"), - call_fchmodat("(sint32, [sint8], sint32, sint32):sint32"), - call_fchmod("(sint32, sint32):sint32"), - call_fchownat("(sint32, [sint8], sint64, sint64, sint32):sint32"), - call_fchown("(sint32, sint64, sint64):sint32"), - call_readlinkat("(sint32, [sint8], [sint8], uint64):sint64"), - get_inheritable("(sint32):sint32"), - set_inheritable("(sint32, sint32):sint32"), - get_blocking("(sint32):sint32"), - set_blocking("(sint32, sint32):sint32"), - get_terminal_size("(sint32, [sint32]):sint32"), - call_raise("(sint32):sint32"), - call_alarm("(sint32):sint32"), - call_getitimer("(sint32, [sint64]):sint32"), - call_setitimer("(sint32, [sint64], [sint64]):sint32"), - signal_self("(sint32):sint32"), - call_kill("(sint64, sint32):sint32"), - call_killpg("(sint64, sint32):sint32"), - call_waitpid("(sint64, [sint32], sint32):sint64"), - call_wcoredump("(sint32):sint32"), - call_wifcontinued("(sint32):sint32"), - call_wifstopped("(sint32):sint32"), - call_wifsignaled("(sint32):sint32"), - call_wifexited("(sint32):sint32"), - call_wexitstatus("(sint32):sint32"), - call_wtermsig("(sint32):sint32"), - call_wstopsig("(sint32):sint32"), - call_getuid("():sint64"), - call_geteuid("():sint64"), - call_getgid("():sint64"), - call_getegid("():sint64"), - call_getppid("():sint64"), - call_getpgid("(sint64):sint64"), - call_setpgid("(sint64,sint64):sint32"), - call_getpgrp("():sint64"), - call_getsid("(sint64):sint64"), - call_setsid("():sint64"), - call_getgroups("(sint64, [sint64]):sint32"), - call_getrusage("(sint32, [sint64]):sint32"), - call_openpty("([sint32]):sint32"), - call_ctermid("([sint8]):sint32"), - call_setenv("([sint8], [sint8], sint32):sint32"), - call_unsetenv("([sint8]):sint32"), - fork_exec("([sint8], [sint64], sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, sint32, [sint32], sint64):sint32"), - call_execv("([sint8], [sint64], sint32):void"), - call_system("([sint8]):sint32"), - - call_getpwuid_r("(uint64,[sint8],sint32,[uint64]):sint32"), - call_getpwname_r("([sint8],[sint8],sint32,[uint64]):sint32"), - call_setpwent("():void"), - call_endpwent("():void"), - call_getpwent("([sint64]):pointer"), - get_getpwent_data("(pointer,[sint8],sint32,[uint64]):sint32"), - get_sysconf_getpw_r_size_max("():sint64"), - - call_socket("(sint32, sint32, sint32):sint32"), - call_accept("(sint32, [sint8], [sint32]):sint32"), - call_bind("(sint32, [sint8], sint32):sint32"), - call_connect("(sint32, [sint8], sint32):sint32"), - call_listen("(sint32, sint32):sint32"), - call_getpeername("(sint32, [sint8], [sint32]):sint32"), - call_getsockname("(sint32, [sint8], [sint32]):sint32"), - call_send("(sint32, [sint8], sint32, sint32, sint32):sint32"), - call_sendto("(sint32, [sint8], sint32, sint32, sint32, [sint8], sint32):sint32"), - call_recv("(sint32, [sint8], sint32, sint32, sint32):sint32"), - call_recvfrom("(sint32, [sint8], sint32, sint32, sint32, [sint8], [sint32]):sint32"), - call_shutdown("(sint32, sint32): sint32"), - call_getsockopt("(sint32, sint32, sint32, [sint8], [sint32]):sint32"), - call_setsockopt("(sint32, sint32, sint32, [sint8], sint32):sint32"), - - call_inet_addr("([sint8]):sint32"), - call_inet_aton("([sint8]):sint64"), - call_inet_ntoa("(sint32, [sint8]):sint32"), - call_inet_pton("(sint32, [sint8], [sint8]):sint32"), - call_inet_ntop("(sint32, [sint8], [sint8], sint32):sint32"), - call_gethostname("([sint8], sint64):sint32"), - - call_getnameinfo("([sint8], sint32, [sint8], sint32, [sint8], sint32, sint32):sint32"), - call_getaddrinfo("([sint8], [sint8], sint32, sint32, sint32, sint32, [sint64]):sint32"), - call_freeaddrinfo("(sint64):void"), - call_gai_strerror("(sint32, [sint8], sint32):void"), - get_addrinfo_members("(sint64, [sint32], [sint64], [sint8]):sint32"), - - call_sem_open("([sint8], sint32, sint32, sint32):pointer"), - call_sem_close("(pointer):sint32"), - call_sem_unlink("([sint8]):sint32"), - call_sem_getvalue("(pointer, [sint32]):sint32"), - call_sem_post("(pointer):sint32"), - call_sem_wait("(pointer):sint32"), - call_sem_trywait("(pointer):sint32"), - call_sem_timedwait("(pointer, sint64):sint32"), - - call_ioctl_bytes("(sint32, uint64, [sint8]):sint32"), - call_ioctl_int("(sint32, uint64, sint32):sint32"), - - call_sysconf("(sint32):sint64"), - - crypt("([sint8], [sint8]):sint64"); - - private final String signature; - - PosixNativeFunction(String signature) { - this.signature = signature; - } - } - - protected static final class InvokeNativeFunction extends Node { - private static final InvokeNativeFunction UNCACHED = new InvokeNativeFunction(SignatureLibrary.getUncached(), InteropLibrary.getUncached()); - - @Child private SignatureLibrary functionInterop; - @Child private InteropLibrary resultInterop; - - public InvokeNativeFunction(SignatureLibrary functionInterop, InteropLibrary resultInterop) { - this.functionInterop = functionInterop; - this.resultInterop = resultInterop; - } - - @NeverDefault - public static InvokeNativeFunction create() { - return new InvokeNativeFunction(SignatureLibrary.getFactory().createDispatched(2), null); - } - - public static InvokeNativeFunction getUncached() { - return UNCACHED; - } - - public Object call(NFIPosixSupport posix, PosixNativeFunction function, Object... args) { - if (injectBranchProbability(SLOWPATH_PROBABILITY, posix.nfiLibrary == null)) { - loadLibrary(this, posix); - } - if (injectBranchProbability(SLOWPATH_PROBABILITY, posix.cachedFunctions.get(function.ordinal()) == null)) { - loadFunction(this, posix, posix.nfiLibrary, function); - } - FunctionWithSignature funObject = posix.cachedFunctions.get(function.ordinal()); - try { - return functionInterop.call(funObject.signature(), funObject.function(), args); - } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - - public long callLong(NFIPosixSupport posix, PosixNativeFunction function, Object... args) { - try { - return getResultInterop().asLong(call(posix, function, args)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - - public int callInt(NFIPosixSupport posix, PosixNativeFunction function, Object... args) { - try { - return getResultInterop().asInt(call(posix, function, args)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - - public byte callByte(NFIPosixSupport posix, PosixNativeFunction function, Object... args) { - try { - return getResultInterop().asByte(call(posix, function, args)); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - - // Temporary - will be replaced with something else when we move this to Truffle - private static String getLibPath(PythonContext context) { - CompilerAsserts.neverPartOfCompilation(); - String libPythonName = PythonContext.getSupportLibName(NFIPosixSupport.SUPPORTING_NATIVE_LIB_NAME); - TruffleFile homePath = context.getEnv().getInternalTruffleFile(context.getCAPIHome().toJavaStringUncached()); - TruffleFile file = homePath.resolve(libPythonName); - return file.getPath(); - } - - @TruffleBoundary - private static void loadLibrary(Node node, NFIPosixSupport posix) { - String path = getLibPath(posix.context); - try { - posix.nfiLibrary = loadLibrary(node, posix, path); - } catch (Throwable e) { - throw new UnsupportedOperationException(String.format(""" - Could not load posix support library from path '%s'. Troubleshooting:\s - Check permissions of the file. - Missing runtime Maven dependency 'org.graalvm.truffle:truffle-nfi-libffi' (should be a dependency of `org.graalvm.polyglot:python{-community}`)?""", - path)); - } - } - - @TruffleBoundary - private static Object loadLibrary(Node node, NFIPosixSupport posix, String path) { - String backend = posix.nfiBackend.toJavaStringUncached(); - Env env = posix.context.getEnv(); - - posix.context.ensureNFILanguage(null, "PosixModuleBackend", "native"); - - Source loadSrc; - if (path != null) { - String withClause = backend.equals(J_NATIVE) ? "" : "with " + backend; - String src = String.format("%sload (RTLD_LOCAL) \"%s\"", withClause, path); - if (LOGGER.isLoggable(Level.FINE)) { - LOGGER.fine(String.format("Loading native library: %s", src)); - } - loadSrc = Source.newBuilder(J_NFI_LANGUAGE, src, "load:" + SUPPORTING_NATIVE_LIB_NAME).internal(true).build(); - } else { - loadSrc = Source.newBuilder(J_NFI_LANGUAGE, J_DEFAULT, J_DEFAULT).internal(true).build(); - } - - return callCallTarget(env.parseInternal(loadSrc), node); - } - - @TruffleBoundary - private static void loadFunction(Node node, NFIPosixSupport posix, Object library, PosixNativeFunction function) { - Object unbound; - try { - InteropLibrary interop = InteropLibrary.getUncached(); - - String sig = String.format("with %s %s", posix.nfiBackend, function.signature); - Source sigSrc = Source.newBuilder(J_NFI_LANGUAGE, sig, "posix-nfi-signature").internal(true).build(); - Object signature = PythonUtils.callCallTarget(posix.context.getEnv().parseInternal(sigSrc), node); - unbound = interop.readMember(library, function.name()); - posix.cachedFunctions.set(function.ordinal(), new FunctionWithSignature(signature, unbound)); - } catch (UnsupportedMessageException | UnknownIdentifierException e) { - throw CompilerDirectives.shouldNotReachHere(function.name(), e); - } - } - - public InteropLibrary getResultInterop() { - if (resultInterop == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - resultInterop = insert(InteropLibrary.getFactory().createDispatched(2)); - } - return resultInterop; - } - } - - private final PythonContext context; - private final TruffleString nfiBackend; - private volatile Object nfiLibrary; - private volatile Object cryptLibrary; - private final AtomicReferenceArray cachedFunctions; - @CompilationFinal(dimensions = 1) private long[] constantValues; - - public NFIPosixSupport(PythonContext context, TruffleString nfiBackend) { - assert nfiBackend.equalsUncached(T_NATIVE, TS_ENCODING); - this.context = context; - this.nfiBackend = nfiBackend; - this.cachedFunctions = new AtomicReferenceArray<>(PosixNativeFunction.values().length); - setEnv(context.getEnv()); - } - - long getConstant(NFIPosixConstants constant) { - if (constantValues == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - constantValues = new long[NFIPosixConstants.values().length]; - int result = InvokeNativeFunction.getUncached().callInt(this, PosixNativeFunction.init_constants, constantValues, constantValues.length); - if (result != 0) { - throw CompilerDirectives.shouldNotReachHere("Mismatched build of posix native library"); - } - } - return constantValues[constant.ordinal()]; - } - - @Override - public void setEnv(Env env) { - if (env.isPreInitialization()) { - return; - } - // Java NIO (and TruffleFile) do not expect/support changing native working directory since - // it is inherently thread-unsafe operation. It is not defined how NIO behaves when native - // cwd changes, thus we need to prevent TruffleFile from resolving relative paths using - // NIO by setting Truffle cwd to a know value. This cannot be done lazily in chdir() because - // native cwd is global, but Truffle cwd is per context. - // TruffleFile will be unaware of the real working directory and keep resolving against the - // original working directory. This should not matter since we do not use TruffleFile for - // ordinary I/O when using NFI backend. - try { - TruffleFile truffleFile = context.getEnv().getInternalTruffleFile(".").getAbsoluteFile(); - context.getEnv().setCurrentWorkingDirectory(truffleFile); - } catch (Exception e) { - LOGGER.log(Level.WARNING, "Unable to change Truffle working directory", e); - } - } - - @ExportMessage - public TruffleString getBackend() { - return nfiBackend; - } - - @ExportMessage - public TruffleString strerror(int errorCode, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) { - // From man pages: The GNU C Library uses a buffer of 1024 characters for strerror(). - // This buffer size therefore should be sufficient to avoid an ERANGE error when calling - // strerror_r(). - byte[] buf = new byte[1024]; - invokeNode.call(this, PosixNativeFunction.call_strerror, errorCode, buf, buf.length); - // TODO PyUnicode_DecodeLocale - return cStringToTruffleString(buf, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - @ExportMessage - public long getpid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getpid); - } - - @ExportMessage - public int umask(int mask, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_umask, mask); - if (result < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result; - } - - @ExportMessage - public int openat(int dirFd, Object pathname, int flags, int mode, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int fd = invokeNode.callInt(this, PosixNativeFunction.call_openat, dirFd, pathToCString(pathname), flags, mode); - if (fd < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return fd; - } - - @ExportMessage - public int close(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - final int rv = invokeNode.callInt(this, PosixNativeFunction.call_close, fd); - if (rv < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return rv; - } - - @ExportMessage - public Buffer read(int fd, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long count = Math.min(length, MAX_READ); - Buffer buffer = Buffer.allocate(count); - setErrno(invokeNode, 0); // TODO CPython does this, but do we need it? - long n = invokeNode.callLong(this, PosixNativeFunction.call_read, fd, buffer.data, count); - if (n < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return buffer.withLength(n); - } - - @ExportMessage - public long write(int fd, Buffer data, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - setErrno(invokeNode, 0); // TODO CPython does this, but do we need it? - long n = invokeNode.callLong(this, PosixNativeFunction.call_write, fd, data.data, data.length); - if (n < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return n; - } - - @ExportMessage - public int dup(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int newFd = invokeNode.callInt(this, PosixNativeFunction.call_dup, fd); - if (newFd < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return newFd; - } - - @ExportMessage - public int dup2(int fd, int fd2, boolean inheritable, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int newFd = invokeNode.callInt(this, PosixNativeFunction.call_dup2, fd, fd2, inheritable ? 1 : 0); - if (newFd < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return newFd; - } - - @ExportMessage - public boolean getInheritable(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.get_inheritable, fd); - if (result < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result != 0; - } - - @ExportMessage - public void setInheritable(int fd, boolean inheritable, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - if (invokeNode.callInt(this, PosixNativeFunction.set_inheritable, fd, inheritable ? 1 : 0) < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public int[] pipe( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int[] fds = new int[2]; - if (invokeNode.callInt(this, PosixNativeFunction.call_pipe2, fds) != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return fds; - } - - @ExportMessage - public SelectResult select(int[] readfds, int[] writefds, int[] errorfds, Timeval timeout, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int largestFD = findMax(readfds, -1); - largestFD = findMax(writefds, largestFD); - largestFD = findMax(errorfds, largestFD); - // This will be treated as boolean array (output parameter), each item indicating if given - // FD was selected or not - byte[] selected = new byte[readfds.length + writefds.length + errorfds.length]; - int nfds = largestFD == -1 ? 0 : largestFD + 1; - long secs = -1, usecs = -1; - if (timeout != null) { - secs = timeout.getSeconds(); - usecs = timeout.getMicroseconds(); - } - int result = invokeNode.callInt(this, PosixNativeFunction.call_select, nfds, - readfds, readfds.length, - writefds, writefds.length, - errorfds, errorfds.length, - secs, usecs, selected); - if (result < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new SelectResult( - selectFillInResult(readfds, selected, 0), - selectFillInResult(writefds, selected, readfds.length), - selectFillInResult(errorfds, selected, readfds.length + writefds.length)); - - } - - private static boolean[] selectFillInResult(int[] fds, byte[] selected, int selectedOffset) { - boolean[] res = new boolean[fds.length]; - for (int i = 0; i < fds.length; i++) { - res[i] = selected[selectedOffset + i] != 0; - } - return res; - } - - private static int findMax(int[] items, int currentMax) { - int max = currentMax; - for (int item : items) { - if (item > max) { - max = item; - } - } - return max; - } - - @ExportMessage - public boolean poll(int fd, boolean forWriting, Timeval timeout, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long secs = -1, usecs = -1; - if (timeout != null) { - secs = timeout.getSeconds(); - usecs = timeout.getMicroseconds(); - } - int result = invokeNode.callInt(this, PosixNativeFunction.call_poll, fd, - forWriting ? 1 : 0, secs, usecs); - if (result < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - if (result == 0) { - return false; - } else { - return true; - } - } - - @ExportMessage - public long lseek(int fd, long offset, int how, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long res = invokeNode.callLong(this, PosixNativeFunction.call_lseek, fd, offset, how); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return res; - } - - @ExportMessage - public void ftruncate(int fd, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_ftruncate, fd, length); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void truncate(Object path, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_truncate, pathToCString(path), length); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void fsync(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_fsync, fd); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - void flock(int fd, int operation, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_flock, fd, operation); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - void fcntlLock(int fd, boolean blocking, int lockType, int whence, long start, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_fcntl_lock, fd, blocking, lockType, whence, start, length); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public boolean getBlocking(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.get_blocking, fd); - if (result < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result != 0; - } - - @ExportMessage - public void setBlocking(int fd, boolean blocking, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - if (invokeNode.callInt(this, PosixNativeFunction.set_blocking, fd, blocking ? 1 : 0) < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public int[] getTerminalSize(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int[] size = new int[2]; - if (invokeNode.callInt(this, PosixNativeFunction.get_terminal_size, fd, size) != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return size; - } - - @ExportMessage - public long sysconf(int name, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long result = invokeNode.callLong(this, PosixNativeFunction.call_sysconf, name); - if (result == -1) { - int errno = getErrno(invokeNode); - if (errno != 0) { - throw newPosixException(invokeNode, errno); - } - } - return result; - } - - @ExportMessage - public long[] fstatat(int dirFd, Object pathname, boolean followSymlinks, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] out = new long[13]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_fstatat, dirFd, pathToCString(pathname), followSymlinks ? 1 : 0, out); - if (res != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return out; - } - - @ExportMessage - public long[] fstat(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] out = new long[13]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_fstat, fd, out); - if (res != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return out; - } - - @ExportMessage - public long[] statvfs(Object path, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] out = new long[11]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_statvfs, pathToCString(path), out); - if (res != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return out; - } - - @ExportMessage - public long[] fstatvfs(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] out = new long[11]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_fstatvfs, fd, out); - if (res != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return out; - } - - @ExportMessage - public Object[] uname( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - byte[] sys = new byte[UNAME_BUF_LENGTH]; - byte[] node = new byte[UNAME_BUF_LENGTH]; - byte[] rel = new byte[UNAME_BUF_LENGTH]; - byte[] ver = new byte[UNAME_BUF_LENGTH]; - byte[] machine = new byte[UNAME_BUF_LENGTH]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_uname, sys, node, rel, ver, machine, UNAME_BUF_LENGTH); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new Object[]{ - // TODO PyUnicode_DecodeFSDefault - cStringToTruffleString(sys, fromByteArrayNode, switchEncodingFromUtf8Node), - cStringToTruffleString(node, fromByteArrayNode, switchEncodingFromUtf8Node), - cStringToTruffleString(rel, fromByteArrayNode, switchEncodingFromUtf8Node), - cStringToTruffleString(ver, fromByteArrayNode, switchEncodingFromUtf8Node), - cStringToTruffleString(machine, fromByteArrayNode, switchEncodingFromUtf8Node) - }; - } - - @ExportMessage - public void unlinkat(int dirFd, Object pathname, boolean rmdir, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_unlinkat, dirFd, pathToCString(pathname), rmdir ? 1 : 0); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void linkat(int oldFdDir, Object oldPath, int newFdDir, Object newPath, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_linkat, oldFdDir, pathToCString(oldPath), newFdDir, pathToCString(newPath), flags); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void symlinkat(Object target, int linkpathDirFd, Object linkpath, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_symlinkat, pathToCString(target), linkpathDirFd, pathToCString(linkpath)); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void mkdirat(int dirFd, Object pathname, int mode, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_mkdirat, dirFd, pathToCString(pathname), mode); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public Object getcwd( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - for (int bufLen = 1024;; bufLen += 1024) { - Buffer buffer = Buffer.allocate(bufLen); - int n = invokeNode.callInt(this, PosixNativeFunction.call_getcwd, buffer.data, bufLen); - if (n == 0) { - buffer = buffer.withLength(findZero(buffer.data)); - return buffer; - } - int errno = getErrno(invokeNode); - if (errno != OSErrorEnum.ERANGE.getNumber()) { - throw newPosixException(invokeNode, errno); - } - } - } - - @ExportMessage - public void chdir(Object path, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_chdir, pathToCString(path)); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void fchdir(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_fchdir, fd); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public boolean isatty(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_isatty, fd) != 0; - } - - @ExportMessage - public Object opendir(Object path, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long ptr = invokeNode.callLong(this, PosixNativeFunction.call_opendir, pathToCString(path)); - if (ptr == 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return ptr; - } - - @ExportMessage - public Object fdopendir(int fd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long ptr = invokeNode.callLong(this, PosixNativeFunction.call_fdopendir, fd); - if (ptr == 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return ptr; - } - - @ExportMessage - public void closedir(Object dirStreamObj, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_closedir, dirStreamObj); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public Object readdir(Object dirStreamObj, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - Buffer name = Buffer.allocate(DIRENT_NAME_BUF_LENGTH); - long[] out = new long[2]; - int result; - do { - result = invokeNode.callInt(this, PosixNativeFunction.call_readdir, dirStreamObj, name.data, DIRENT_NAME_BUF_LENGTH, out); - } while (result != 0 && name.data[0] == '.' && (name.data[1] == 0 || (name.data[1] == '.' && name.data[2] == 0))); - if (result != 0) { - return new DirEntry(name.withLength(findZero(name.data)), out[0], (int) out[1]); - } - int errno = getErrno(invokeNode); - if (errno == 0) { - return null; - } - throw newPosixException(invokeNode, errno); - } - - @ExportMessage - public void rewinddir(Object dirStreamObj, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - invokeNode.call(this, PosixNativeFunction.call_rewinddir, dirStreamObj); - } - - @ExportMessage - @SuppressWarnings("static-method") - public Object dirEntryGetName(Object dirEntryObj) { - DirEntry dirEntry = (DirEntry) dirEntryObj; - return dirEntry.name; - } - - @ExportMessage - public static class DirEntryGetPath { - @Specialization(guards = "endsWithSlash(scandirPath)") - static Buffer withSlash(@SuppressWarnings("unused") NFIPosixSupport receiver, DirEntry dirEntry, Object scandirPath) { - Buffer scandirPathBuffer = (Buffer) scandirPath; - int pathLen = scandirPathBuffer.data.length; - int nameLen = (int) dirEntry.name.length; - byte[] buf = new byte[pathLen + nameLen]; - PythonUtils.arraycopy(scandirPathBuffer.data, 0, buf, 0, pathLen); - PythonUtils.arraycopy(dirEntry.name.data, 0, buf, pathLen, nameLen); - return Buffer.wrap(buf); - } - - @Specialization(guards = "!endsWithSlash(scandirPath)") - static Buffer withoutSlash(@SuppressWarnings("unused") NFIPosixSupport receiver, DirEntry dirEntry, Object scandirPath) { - Buffer scandirPathBuffer = (Buffer) scandirPath; - int pathLen = scandirPathBuffer.data.length; - int nameLen = (int) dirEntry.name.length; - byte[] buf = new byte[pathLen + 1 + nameLen]; - PythonUtils.arraycopy(scandirPathBuffer.data, 0, buf, 0, pathLen); - buf[pathLen] = POSIX_FILENAME_SEPARATOR; - PythonUtils.arraycopy(dirEntry.name.data, 0, buf, pathLen + 1, nameLen); - return Buffer.wrap(buf); - } - - protected static boolean endsWithSlash(Object path) { - Buffer b = (Buffer) path; - return b.data[b.data.length - 1] == POSIX_FILENAME_SEPARATOR; - } - } - - @ExportMessage - @SuppressWarnings("static-method") - public long dirEntryGetInode(Object dirEntry) { - DirEntry entry = (DirEntry) dirEntry; - return entry.ino; - } - - @ExportMessage - @SuppressWarnings("static-method") - public int dirEntryGetType(Object dirEntryObj) { - DirEntry dirEntry = (DirEntry) dirEntryObj; - return dirEntry.type; - } - - @ExportMessage - public void utimensat(int dirFd, Object pathname, long[] timespec, boolean followSymlinks, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert PosixConstants.HAVE_UTIMENSAT.value; - assert timespec == null || timespec.length == 4; - int ret = invokeNode.callInt(this, PosixNativeFunction.call_utimensat, dirFd, pathToCString(pathname), wrap(timespec), followSymlinks ? 1 : 0); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void futimens(int fd, long[] timespec, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert PosixConstants.HAVE_FUTIMENS.value; - assert timespec == null || timespec.length == 4; - int ret = invokeNode.callInt(this, PosixNativeFunction.call_futimens, fd, wrap(timespec)); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void futimes(int fd, Timeval[] timeval, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert timeval == null || timeval.length == 2; - int ret = invokeNode.callInt(this, PosixNativeFunction.call_futimes, fd, wrap(timeval)); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void lutimes(Object filename, Timeval[] timeval, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert timeval == null || timeval.length == 2; - int ret = invokeNode.callInt(this, PosixNativeFunction.call_lutimes, pathToCString(filename), wrap(timeval)); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void utimes(Object filename, Timeval[] timeval, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert timeval == null || timeval.length == 2; - int ret = invokeNode.callInt(this, PosixNativeFunction.call_utimes, pathToCString(filename), wrap(timeval)); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void renameat(int oldDirFd, Object oldPath, int newDirFd, Object newPath, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_renameat, oldDirFd, pathToCString(oldPath), newDirFd, pathToCString(newPath)); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public boolean faccessat(int dirFd, Object path, int mode, boolean effectiveIds, boolean followSymlinks, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_faccessat, dirFd, pathToCString(path), mode, effectiveIds ? 1 : 0, followSymlinks ? 1 : 0); - if (ret != 0 && LOGGER.isLoggable(Level.FINE)) { - log(Level.FINE, "faccessat return value: %d, errno: %d", ret, getErrno(invokeNode)); - } - return ret == 0; - } - - @ExportMessage - public void fchmodat(int dirFd, Object path, int mode, boolean followSymlinks, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_fchmodat, dirFd, pathToCString(path), mode, followSymlinks ? 1 : 0); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void fchmod(int fd, int mode, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_fchmod, fd, mode); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void fchownat(int dirFd, Object path, long owner, long group, boolean followSymlinks, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_fchownat, dirFd, pathToCString(path), owner, group, followSymlinks ? 1 : 0); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public void fchown(int fd, long owner, long group, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int ret = invokeNode.callInt(this, PosixNativeFunction.call_fchown, fd, owner, group); - if (ret != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - public Object readlinkat(int dirFd, Object path, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - Buffer buffer = Buffer.allocate(PATH_MAX.value); - long n = invokeNode.callLong(this, PosixNativeFunction.call_readlinkat, dirFd, pathToCString(path), buffer.data, PATH_MAX.value); - if (n < 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return buffer.withLength(n); - } - - @ExportMessage - public void kill(long pid, int signal, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_kill, pid, signal); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void raise(int signal, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_raise, signal); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public int alarm(int seconds, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_alarm, seconds); - } - - @ExportMessage - public Timeval[] getitimer(int which, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] currentValue = new long[4]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_getitimer, which, currentValue); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return unwrapTimeval(currentValue); - } - - @ExportMessage - public Timeval[] setitimer(int which, Timeval delay, Timeval interval, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] oldValue = new long[4]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_setitimer, which, wrapItimerval(delay, interval), oldValue); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return unwrapTimeval(oldValue); - } - - @ExportMessage - public void signalSelf(int signal, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - if (!ImageInfo.inImageRuntimeCode()) { - throw new UnsupportedPosixFeatureException("self-signals are only supported in native standalone"); - } - int res = invokeNode.callInt(this, PosixNativeFunction.signal_self, signal); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void killpg(long pgid, int signal, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_killpg, pgid, signal); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public long[] waitpid(long pid, int options, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int[] status = new int[1]; - boolean hasNohang = (options & WNOHANG.getValueIfDefined()) != 0; - int subOptions = options | WNOHANG.getValueIfDefined(); - Object wrappedStatus = status; - long res = invokeNode.callLong(this, PosixNativeFunction.call_waitpid, pid, wrappedStatus, subOptions); - while (res == 0 && !hasNohang) { - TruffleSafepoint.setBlockedThreadInterruptible(invokeNode, (ignored) -> { - Thread.sleep(20); - }, null); - res = invokeNode.callLong(this, PosixNativeFunction.call_waitpid, pid, wrappedStatus, subOptions); - } - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new long[]{res, status[0]}; - } - - @ExportMessage - public boolean wcoredump(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wcoredump, status) != 0; - } - - @ExportMessage - public boolean wifcontinued(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wifcontinued, status) != 0; - } - - @ExportMessage - public boolean wifstopped(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wifstopped, status) != 0; - } - - @ExportMessage - public boolean wifsignaled(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wifsignaled, status) != 0; - } - - @ExportMessage - public boolean wifexited(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wifexited, status) != 0; - } - - @ExportMessage - public int wexitstatus(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wexitstatus, status); - } - - @ExportMessage - public int wtermsig(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wtermsig, status); - } - - @ExportMessage - public int wstopsig(int status, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_wstopsig, status); - } - - @ExportMessage - public long getuid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getuid); - } - - @ExportMessage - public long geteuid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_geteuid); - } - - @ExportMessage - public long getgid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getgid); - } - - @ExportMessage - public long getegid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getegid); - } - - @ExportMessage - public long getppid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getppid); - } - - @ExportMessage - public void setpgid(long pid, long pgid, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_setpgid, pid, pgid); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public long getpgid(long pid, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long res = invokeNode.callLong(this, PosixNativeFunction.call_getpgid, pid); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return res; - } - - @ExportMessage - public long getpgrp(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callLong(this, PosixNativeFunction.call_getpgrp); - } - - @ExportMessage - public long getsid(long pid, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long res = invokeNode.callLong(this, PosixNativeFunction.call_getsid, pid); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return res; - } - - @ExportMessage - public long setsid( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long res = invokeNode.callLong(this, PosixNativeFunction.call_setsid); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return res; - } - - @ExportMessage - public long[] getgroups( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - // The first call gets us the number of groups, so we can allocate the output array - int res = invokeNode.callInt(this, PosixNativeFunction.call_getgroups, 0, 0); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - if (res == 0) { - return EMPTY_LONG_ARRAY; - } - long[] groups = new long[res]; - res = invokeNode.callInt(this, PosixNativeFunction.call_getgroups, groups.length, groups); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return groups; - } - - @ExportMessage - public RusageResult getrusage(int who, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long[] result = new long[16]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_getrusage, who, result); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new RusageResult(Double.longBitsToDouble(result[0]), Double.longBitsToDouble(result[1]), - result[2], result[3], result[4], result[5], - result[6], result[7], result[8], result[9], result[10], - result[11], result[12], result[13], result[14], result[15]); - } - - @ExportMessage - public OpenPtyResult openpty(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int[] outvars = new int[2]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_openpty, outvars); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new OpenPtyResult(outvars[0], outvars[1]); - } - - @ExportMessage - public TruffleString ctermid(@Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - byte[] buf = new byte[L_ctermid.value]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_ctermid, buf); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - // TODO PyUnicode_DecodeFSDefault - return cStringToTruffleString(buf, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - @ExportMessage - public void setenv(Object name, Object value, boolean overwrite, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_setenv, pathToCString(name), pathToCString(value), overwrite ? 1 : 0); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void unsetenv(Object name, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_unsetenv, pathToCString(name)); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd, - int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - - // The following strings and string arrays need to be present in the native function: - // - char** of executable names ('\0'-terminated strings with an extra NULL at the end) - // - char** of arguments ('\0'-terminated strings with an extra NULL at the end) - // - an optional char** of env variables ('\0'-terminated strings with an extra NULL at the - // end), must distinguish between NULL (child inherits env) and an empty array (child gets - // empty env) - // - an optional char* cwd ('\0'-terminated string or NULL) - // We do this by concatenating all strings (including their terminating '\0' characters) - // into one large byte buffer (which becomes 'char *') and pass an additional array of - // offsets to mark where the individual strings begin. To prevent memory allocation - // in C (and related free()), we reuse this array of integer offsets as an array of - // C-strings (char **). For this reason, the array of offsets is allocated as long[]. - // In the offsets array we mark the places where NULL should be with a special value -1. - // All that is left is to let the native function know where in the offsets array the - // individual string arrays begin: - // - executable names are always at index 0 - // - argsPos is the index in the offsets array pointing to the first argument - // - envPos is either -1 or an index in the offsets array pointing to the first env string - // - cwdPos is either -1 or an index in the offsets array pointing to the cwd string - - // First we calculate the lengths of the offsets array and the string buffer (dataLen). - int offsetsLen; - int argsPos; - int envPos; - int cwdPos; - long dataLen; - - try { - offsetsLen = executables.length + 1; - dataLen = addLengthsOfCStrings(0, executables); - - argsPos = offsetsLen; - offsetsLen += args.length + 1; - dataLen = addLengthsOfCStrings(dataLen, args); - - if (env != null) { - envPos = offsetsLen; - offsetsLen += env.length + 1; - dataLen = addLengthsOfCStrings(dataLen, env); - } else { - envPos = -1; - } - - if (cwd != null) { - cwdPos = offsetsLen; - offsetsLen += 1; - // The +1 in the second argument can overflow only if the buffer contains 2^63-1 - // bytes, which is impossible since we are using Java arrays limited to 2^31-1. - dataLen = PythonUtils.addExact(dataLen, ((Buffer) cwd).length + 1L); - } else { - cwdPos = -1; - } - } catch (OverflowException e) { - throw newPosixException(invokeNode, OSErrorEnum.E2BIG.getNumber()); - } - - // This also guarantees that offsetsLen did not overflow: we add +1 to dataLen for each - // '\0', i.e. dataLen >= "number of strings" and offsetsLen < "number of strings" + 3 - // (3 accounts for the NULL terminating the executables, args and env arrays). - if (dataLen >= Integer.MAX_VALUE - 3) { - throw newPosixException(invokeNode, OSErrorEnum.E2BIG.getNumber()); - } - - byte[] data = new byte[(int) dataLen]; - long[] offsets = new long[offsetsLen]; - long offset = 0; - - offset = encodeCStringArray(data, offset, offsets, 0, executables); - offset = encodeCStringArray(data, offset, offsets, argsPos, args); - if (env != null) { - offset = encodeCStringArray(data, offset, offsets, envPos, env); - } - if (cwd != null) { - Buffer buf = (Buffer) cwd; - int strLen = (int) buf.length; - PythonUtils.arraycopy(buf.data, 0, data, (int) offset, strLen); - offsets[cwdPos] = offset; - offset += strLen + 1L; - } - assert offset == dataLen; - - int res = invokeNode.callInt(this, PosixNativeFunction.fork_exec, - data, offsets, offsets.length, argsPos, envPos, cwdPos, - stdinReadFd, stdinWriteFd, - stdoutReadFd, stdoutWriteFd, - stderrReadFd, stderrWriteFd, - errPipeReadFd, errPipeWriteFd, - closeFds ? 1 : 0, - restoreSignals ? 1 : 0, - callSetsid ? 1 : 0, - fdsToKeep, fdsToKeep.length); - if (res == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return res; - } - - @ExportMessage - public void execv(Object pathname, Object[] args, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - - // The following strings and string arrays need to be present in the native function: - // - char* - the pathname ('\0'-terminated string) - // - char** of arguments ('\0'-terminated strings with an extra NULL at the end) - // We do this by concatenating all strings (including their terminating '\0' characters) - // into one large byte buffer (which becomes 'char *') and pass an additional array of - // offsets to mark where the individual strings begin. To prevent memory allocation - // in C (and related free()), we reuse this array of integer offsets as an array of - // C-strings (char **). For this reason, the array of offsets is allocated as long[]. - // In the offsets array we mark the places where NULL should be with a special value -1. - // - the pathname is always at index 0 - // - the arguments start at index 1 - - // First we calculate the lengths of the offsets array and the string buffer (dataLen). - int offsetsLen = 1 + args.length + 1; - long pathnameLen = ((Buffer) pathname).length; - long dataLen; - - try { - // The +1 can overflow only if the buffer contains 2^63-1 bytes, which is impossible - // since we are using Java arrays limited to 2^31-1. - dataLen = addLengthsOfCStrings(pathnameLen + 1L, args); - } catch (OverflowException e) { - throw newPosixException(invokeNode, OSErrorEnum.E2BIG.getNumber()); - } - - // This also guarantees that offsetsLen did not overflow: we add +1 to dataLen for each - // '\0', i.e. dataLen >= "number of strings" and offsetsLen == "number of strings" + 1 - // (1 accounts for the NULL terminating the args array). - // Also, dataLen > pathnameLen, so this check makes sure that the cast of pathnameLen to int - // below is safe. - if (dataLen >= Integer.MAX_VALUE - 1) { - throw newPosixException(invokeNode, OSErrorEnum.E2BIG.getNumber()); - } - - byte[] data = new byte[(int) dataLen]; - long[] offsets = new long[offsetsLen]; - - PythonUtils.arraycopy(((Buffer) pathname).data, 0, data, 0, (int) pathnameLen); - long offset = encodeCStringArray(data, pathnameLen + 1L, offsets, 1, args); - assert offset == dataLen; - - invokeNode.call(this, PosixNativeFunction.call_execv, data, offsets, offsets.length); - throw getErrnoAndThrowPosixException(invokeNode); - } - - @ExportMessage - public int system(Object command, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_system, pathToCString(command)); - } - - private static long addLengthsOfCStrings(long prevLen, Object[] src) throws OverflowException { - long len = prevLen; - for (Object o : src) { - len = PythonUtils.addExact(len, ((Buffer) o).length); - } - return PythonUtils.addExact(len, src.length); // add space for terminating '\0' - } - - /** - * Copies null-terminated strings to a buffer {@code data} starting at position {@code offset}, - * and stores the offset of each string to the {@code offsets} array starting at index - * {@code startPos}. - */ - private static long encodeCStringArray(byte[] data, long startOffset, long[] offsets, int startPos, Object[] src) { - // The code that calculates dataLen already checked that there is no overflow and that all - // offsets fit into an int. - long offset = startOffset; - for (int i = 0; i < src.length; ++i) { - Buffer buf = (Buffer) src[i]; - int strLen = (int) buf.length; - PythonUtils.arraycopy(buf.data, 0, data, (int) offset, strLen); - offsets[startPos + i] = offset; - offset += strLen + 1; // +1 for the terminating \0 character - } - offsets[startPos + src.length] = -1; // this will become NULL in C (the char* array - // needs to be terminated by a NULL) - return offset; - } - - private static final class MMapHandle { - private final long pointer; - private final long length; - - public MMapHandle(long pointer, long length) { - this.pointer = pointer; - this.length = length; - } - } - - @ExportMessage - public Object mmap(long length, int prot, int flags, int fd, long offset, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - long address = invokeNode.callLong(this, PosixNativeFunction.call_mmap, length, prot, flags, fd, offset); - if (address == 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return new MMapHandle(address, length); - } - - @ExportMessage - @SuppressWarnings("static-method") - public byte mmapReadByte(Object mmap, long index) { - MMapHandle handle = (MMapHandle) mmap; - if (index < 0 || index >= handle.length) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IndexOutOfBoundsException(); - } - return UNSAFE.getByte(handle.pointer + index); - } - - @ExportMessage - @SuppressWarnings("static-method") - public void mmapWriteByte(Object mmap, long index, byte value) { - MMapHandle handle = (MMapHandle) mmap; - checkIndexAndLen(handle, index, 1); - UNSAFE.putByte(handle.pointer + index, value); - } - - @ExportMessage - @SuppressWarnings("static-method") - public int mmapReadBytes(Object mmap, long index, byte[] bytes, int length) { - MMapHandle handle = (MMapHandle) mmap; - checkIndexAndLen(handle, index, length); - UNSAFE.copyMemory(null, handle.pointer + index, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, length); - return length; - } - - @ExportMessage - @SuppressWarnings("static-method") - public void mmapWriteBytes(Object mmap, long index, byte[] bytes, int length) { - MMapHandle handle = (MMapHandle) mmap; - checkIndexAndLen(handle, index, length); - UNSAFE.copyMemory(bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, handle.pointer + index, length); - } - - @ExportMessage - public void mmapFlush(Object mmap, long offset, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - MMapHandle handle = (MMapHandle) mmap; - checkIndexAndLen(handle, offset, length); - invokeNode.call(this, PosixNativeFunction.call_msync, handle.pointer, offset, length); - } - - @ExportMessage - public void mmapUnmap(Object mmap, long length, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - MMapHandle handle = (MMapHandle) mmap; - if (length != handle.length) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IllegalArgumentException(); - } - int result = invokeNode.callInt(this, PosixNativeFunction.call_munmap, handle.pointer, length); - if (result != 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - } - - @ExportMessage - @SuppressWarnings("static-method") - public long mmapGetPointer(Object mmap) { - MMapHandle handle = (MMapHandle) mmap; - return handle.pointer; - } - - private static void checkIndexAndLen(MMapHandle handle, long index, long length) { - if (length < 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IllegalArgumentException(); - } - if (index < 0 || index + length > handle.length) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IndexOutOfBoundsException(); - } - } - - @ExportMessage - public int socket(int domain, int type, int protocol, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_socket, domain, type, protocol); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result; - } - - @ExportMessage - public AcceptResult accept(int sockfd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - int result = invokeNode.callInt(this, PosixNativeFunction.call_accept, sockfd, addr.data, addr.len); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new AcceptResult(result, addr); - } - - @ExportMessage - public void bind(int sockfd, UniversalSockAddr usa, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; - int result = invokeNode.callInt(this, PosixNativeFunction.call_bind, sockfd, addr.data, addr.getLen()); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void connect(int sockfd, UniversalSockAddr usa, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; - int result = invokeNode.callInt(this, PosixNativeFunction.call_connect, sockfd, addr.data, addr.getLen()); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public void listen(int sockfd, int backlog, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int result = invokeNode.callInt(this, PosixNativeFunction.call_listen, sockfd, backlog); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public UniversalSockAddr getpeername(int sockfd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - int result = invokeNode.callInt(this, PosixNativeFunction.call_getpeername, sockfd, addr.data, addr.len); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return addr; - } - - @ExportMessage - public UniversalSockAddr getsockname(int sockfd, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - int result = invokeNode.callInt(this, PosixNativeFunction.call_getsockname, sockfd, addr.data, addr.len); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return addr; - } - - @ExportMessage - public int send(int sockfd, byte[] buf, int offset, int len, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - checkBounds(buf, offset, len); - int result = invokeNode.callInt(this, PosixNativeFunction.call_send, sockfd, buf, offset, len, flags); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result; - } - - @ExportMessage - public int sendto(int sockfd, byte[] buf, int offset, int len, int flags, UniversalSockAddr usa, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - checkBounds(buf, offset, len); - UniversalSockAddrImpl destAddr = (UniversalSockAddrImpl) usa; - int result = invokeNode.callInt(this, PosixNativeFunction.call_sendto, sockfd, buf, offset, len, flags, destAddr.data, destAddr.getLen()); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result; - } - - @ExportMessage - public int recv(int sockfd, byte[] buf, int offset, int len, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - checkBounds(buf, offset, len); - int result = invokeNode.callInt(this, PosixNativeFunction.call_recv, sockfd, buf, offset, len, flags); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return result; - } - - @ExportMessage - public RecvfromResult recvfrom(int sockfd, byte[] buf, int offset, int len, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - checkBounds(buf, offset, len); - UniversalSockAddrImpl srcAddr = new UniversalSockAddrImpl(this); - int result = invokeNode.callInt(this, PosixNativeFunction.call_recvfrom, sockfd, buf, offset, len, flags, srcAddr.data, srcAddr.len); - if (result == -1) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return new RecvfromResult(result, srcAddr); - } - - @ExportMessage - public void shutdown(int sockfd, int how, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_shutdown, sockfd, how); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public int getsockopt(int sockfd, int level, int optname, byte[] optval, int optlen, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert optlen >= 0 && optval.length >= optlen; - int[] bufLen = new int[]{optlen}; - int res = invokeNode.callInt(this, PosixNativeFunction.call_getsockopt, sockfd, level, optname, optval, bufLen); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return bufLen[0]; - } - - @ExportMessage - public void setsockopt(int sockfd, int level, int optname, byte[] optval, int optlen, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - assert optlen >= 0 && optval.length >= optlen; - int res = invokeNode.callInt(this, PosixNativeFunction.call_setsockopt, sockfd, level, optname, optval, optlen); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - public int inet_addr(Object src, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.call_inet_addr, pathToCString(src)); - } - - @ExportMessage - public int inet_aton(Object src, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws InvalidAddressException { - long r = invokeNode.callLong(this, PosixNativeFunction.call_inet_aton, pathToCString(src)); - if (r < 0) { - throw new InvalidAddressException(); - } - return (int) r; - } - - @ExportMessage - public Object inet_ntoa(int src, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - Buffer buf = Buffer.allocate(INET_ADDRSTRLEN.value); - int len = invokeNode.callInt(this, PosixNativeFunction.call_inet_ntoa, src, buf.data); - return buf.withLength(len); - } - - @ExportMessage - public byte[] inet_pton(int family, Object src, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException, InvalidAddressException { - byte[] buf = new byte[family == AF_INET.value ? 4 : 16]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_inet_pton, family, pathToCString(src), buf); - // Rather unusually, the return value of 0 does not indicate success but is used by - // inet_pton to report invalid format of the address (without setting errno). - // Success is reported by returning 1. - if (res == 1) { - return buf; - } - if (res == 0) { - throw new InvalidAddressException(); - } - throw getErrnoAndThrowPosixException(invokeNode); - } - - @ExportMessage - public Object inet_ntop(int family, byte[] src, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - if ((family == AF_INET.value && src.length < 4) || (family == AF_INET6.value && src.length < 16)) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IllegalArgumentException("Invalid length of IPv4/6 address"); - } - Buffer buf = Buffer.allocate(INET6_ADDRSTRLEN.value); - int res = invokeNode.callInt(this, PosixNativeFunction.call_inet_ntop, family, src, buf.data, INET6_ADDRSTRLEN.value); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return buf.withLength(findZero(buf.data)); - } - - @ExportMessage - public Object gethostname(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int maxLen = (HOST_NAME_MAX.defined ? HOST_NAME_MAX.getValueIfDefined() : _POSIX_HOST_NAME_MAX.value) + 1; - Buffer buf = Buffer.allocate(maxLen); - int res = invokeNode.callInt(this, PosixNativeFunction.call_gethostname, buf.data, maxLen); - if (res != 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return buf.withLength(findZero(buf.data)); - } - - @ExportMessage - public Object[] getnameinfo(UniversalSockAddr usa, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws GetAddrInfoException { - Buffer host = Buffer.allocate(NI_MAXHOST.value); - Buffer serv = Buffer.allocate(NI_MAXSERV.value); - UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; - int res = invokeNode.callInt(this, PosixNativeFunction.call_getnameinfo, addr.data, addr.getLen(), host.data, NI_MAXHOST.value, serv.data, NI_MAXSERV.value, flags); - if (res != 0) { - throw new GetAddrInfoException(res, gai_strerror(res, invokeNode, fromByteArrayNode, switchEncodingFromUtf8Node)); - } - return new Object[]{ - host.withLength(findZero(host.data)), - serv.withLength(findZero(serv.data)), - }; - } - - @ExportMessage - public AddrInfoCursor getaddrinfo(Object node, Object service, int family, int sockType, int protocol, int flags, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws GetAddrInfoException { - long[] ptr = new long[1]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_getaddrinfo, pathToCStringOrNull(node), pathToCStringOrNull(service), family, sockType, protocol, flags, ptr); - if (res != 0) { - throw new GetAddrInfoException(res, gai_strerror(res, invokeNode, fromByteArrayNode, switchEncodingFromUtf8Node)); - } - assert ptr[0] != 0; // getaddrinfo should return at least one result - return new AddrInfoCursorImpl(this, ptr[0], invokeNode); - } - - @ExportMessage - public TruffleString crypt(TruffleString word, TruffleString salt, - @Bind Node inliningTarget, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("toUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingToUtf8Node, - @Shared("tsCopyBytes") @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode, - @Cached TruffleString.FromZeroTerminatedNativePointerNode fromZeroTerminatedNativePointerNode, - @Cached TruffleString.AsManagedNode asManagedNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - /* - * We don't want to link the posix library with libcrypt, because it might not be available - * on the target system and would make the whole posix library fail to load. So we load it - * dynamically on demand. - */ - if (injectBranchProbability(SLOWPATH_PROBABILITY, cryptLibrary == null)) { - try { - cryptLibrary = InvokeNativeFunction.loadLibrary(inliningTarget, this, PythonLanguage.getPythonOS() != PythonOS.PLATFORM_DARWIN ? "libcrypt.so" : null); - } catch (Throwable e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw PRaiseNode.raiseStatic(invokeNode, PythonBuiltinClassType.SystemError, ErrorMessages.UNABLE_TO_LOAD_LIBCRYPT); - } - } - PosixNativeFunction function = PosixNativeFunction.crypt; - if (injectBranchProbability(SLOWPATH_PROBABILITY, cachedFunctions.get(function.ordinal()) == null)) { - InvokeNativeFunction.loadFunction(inliningTarget, this, cryptLibrary, function); - } - FunctionWithSignature funObject = cachedFunctions.get(function.ordinal()); - /* - * From the manpage: Upon successful completion, crypt returns a pointer to a string which - * encodes both the hashed passphrase, and the settings that were used to encode it. See - * crypt(5) for more detail on the format of hashed passphrases. crypt places its result in - * a static storage area, which will be overwritten by subsequent calls to crypt. It is not - * safe to call crypt from multiple threads simultaneously. Upon error, it may return a NULL - * pointer or a pointer to an invalid hash, depending on the implementation. - */ - // Note GIL is not enough as crypt is using global memory, so we need a really global lock - synchronized (CRYPT_LOCK) { - long resultPtr; - Object[] args = new Object[]{ - stringToUTF8CString(word, switchEncodingToUtf8Node, copyToByteArrayNode), - stringToUTF8CString(salt, switchEncodingToUtf8Node, copyToByteArrayNode)}; - try { - Object interopResult = invokeNode.functionInterop.call(funObject.signature(), funObject.function(), args); - resultPtr = invokeNode.getResultInterop().asLong(interopResult); - } catch (UnsupportedTypeException | ArityException | UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - // CPython doesn't handle the case of "invalid hash" return specially and neither do we - if (resultPtr == 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - // TODO PyUnicode_DecodeFSDefault - TruffleString utf8 = fromZeroTerminatedNativePointerNode.execute8Bit(resultPtr, 0, UTF_8, false); - return asManagedNode.execute(switchEncodingFromUtf8Node.execute(utf8, TS_ENCODING), TS_ENCODING); - } - } - - private TruffleString gai_strerror(int errorCode, InvokeNativeFunction invokeNode, TruffleString.FromByteArrayNode fromByteArrayNode, TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) { - byte[] buf = new byte[1024]; - invokeNode.call(this, PosixNativeFunction.call_gai_strerror, errorCode, buf, buf.length); - // TODO PyUnicode_DecodeLocale - return cStringToTruffleString(buf, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - /** - * Provides access to {@code struct addrinfo}. - * - * The layout of native {@code struct addrinfo} is as follows: - * - *
-     * {@code
-     *     struct addrinfo {
-     *         int              ai_flags;           // intData[0]
-     *         int              ai_family;          // intData[1]
-     *         int              ai_socktype;        // intData[2]
-     *         int              ai_protocol;        // intData[3]
-     *         socklen_t        ai_addrlen;         // intData[4]
-     *         struct sockaddr *ai_addr;            // data copied into socketAddress[]
-     *         char            *ai_canonname;       // longData[0]
-     *         struct addrinfo *ai_next;            // longData[1]
-     *     };
-     * }
-     * 
- * - * To avoid multiple NFI calls, we transfer the data in batch using arrays of {@code int}s and - * {@code long}s - int values are stored in {@code intData}, the {@code ai_canonname} and - * {@code ai_next} pointers are stored in {@code longData} and the socket address pointed to by - * {@code ai_addr} is copied into Java byte array {@code socketAddress}. We also cache two - * additional integers: - *
    - *
  • {@code intData[5]} contains {@code ai_addr->sa_family},
  • - *
  • {@code intData[6]} contains the length of {@code ai_canonname} if it is not {@code null} - *
  • - *
- * - * It is not clear whether it is guaranteed that {@code ai_family} and - * {@code ai_addr->sa_family} are always the same. We provide both and use the later when - * decoding the socket address. - */ - private static class AddrInfo { - private final int[] intData = new int[7]; - private final long[] longData = new long[2]; - private byte[] socketAddress; - - private void update(long ptr, NFIPosixSupport nfiPosixSupport, InvokeNativeFunction invokeNode) { - socketAddress = new byte[(int) nfiPosixSupport.getConstant(SIZEOF_STRUCT_SOCKADDR_STORAGE)]; - int res = invokeNode.callInt(nfiPosixSupport, PosixNativeFunction.get_addrinfo_members, ptr, intData, longData, - socketAddress); - if (res != 0) { - throw shouldNotReachHere("the length of ai_canonname does not fit into an int"); - } - } - - int getFlags() { - return intData[0]; - } - - int getFamily() { - return intData[1]; - } - - int getSockType() { - return intData[2]; - } - - int getProtocol() { - return intData[3]; - } - - int getAddrLen() { - return intData[4]; - } - - int getAddrFamily() { - return intData[5]; - } - - int getCanonNameLen() { - assert getCanonNamePtr() != 0; - return intData[6]; - } - - long getCanonNamePtr() { - return longData[0]; - } - - long getNextPtr() { - return longData[1]; - } - } - - @ExportLibrary(AddrInfoCursorLibrary.class) - protected static class AddrInfoCursorImpl implements AddrInfoCursor { - - private final NFIPosixSupport nfiPosixSupport; - private long head; - private AddrInfo info; - - AddrInfoCursorImpl(NFIPosixSupport nfiPosixSupport, long head, InvokeNativeFunction invokeNode) { - this.nfiPosixSupport = nfiPosixSupport; - this.head = head; - info = new AddrInfo(); - info.update(head, nfiPosixSupport, invokeNode); - } - - @ExportMessage - void release(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - checkReleased(); - invokeNode.call(nfiPosixSupport, PosixNativeFunction.call_freeaddrinfo, head); - head = 0; - } - - @ExportMessage - boolean next(@Shared("invoke") @Cached InvokeNativeFunction invokeNode) { - checkReleased(); - long nextPtr = info.getNextPtr(); - if (nextPtr == 0) { - return false; - } - info.update(nextPtr, nfiPosixSupport, invokeNode); - return true; - } - - @ExportMessage - int getFlags() { - checkReleased(); - return info.getFlags(); - } - - @ExportMessage - int getFamily() { - checkReleased(); - return info.getFamily(); - } - - @ExportMessage - int getSockType() { - checkReleased(); - return info.getSockType(); - } - - @ExportMessage - int getProtocol() { - checkReleased(); - return info.getProtocol(); - } - - @ExportMessage - Object getCanonName() { - checkReleased(); - long namePtr = info.getCanonNamePtr(); - if (namePtr == 0) { - return null; - } - int nameLen = info.getCanonNameLen(); - byte[] buf = new byte[nameLen]; - UNSAFE.copyMemory(null, namePtr, buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, nameLen); - return Buffer.wrap(buf); - } - - @ExportMessage - UniversalSockAddr getSockAddr() { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(nfiPosixSupport); - PythonUtils.arraycopy(info.socketAddress, 0, addr.data, 0, info.getAddrLen()); - addr.setFamily(info.getAddrFamily()); - addr.setLen(info.getAddrLen()); - return addr; - } - - private void checkReleased() { - if (head == 0) { - throw shouldNotReachHere("AddrInfoCursor has already been released"); - } - } - } - - @ExportMessage - UniversalSockAddr createUniversalSockAddrInet4(Inet4SockAddr src) { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - addr.setFamily(AF_INET.value); - ARRAY_ACCESSOR_BE.putShort(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT), (short) src.getPort()); - ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR) + getConstant(OFFSETOF_STRUCT_IN_ADDR_S_ADDR), src.getAddress()); - addr.setLen((int) getConstant(SIZEOF_STRUCT_SOCKADDR_IN)); - return addr; - } - - @ExportMessage - UniversalSockAddr createUniversalSockAddrInet6(Inet6SockAddr src) { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - addr.setFamily(AF_INET6.value); - ARRAY_ACCESSOR_BE.putShort(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT), (short) src.getPort()); - int addrOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR) + (int) getConstant(OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR); - PythonUtils.arraycopy(src.getAddress(), 0, addr.data, addrOffset, 16); - ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO), src.getFlowInfo()); - ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID), src.getScopeId()); - addr.setLen((int) getConstant(SIZEOF_STRUCT_SOCKADDR_IN6)); - return addr; - } - - @ExportMessage - UniversalSockAddr createUniversalSockAddrUnix(UnixSockAddr src) throws InvalidUnixSocketPathException { - UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); - addr.setFamily(AF_UNIX.value); - byte[] path = src.getPath(); - if (path.length > getConstant(SIZEOF_STRUCT_SOCKADDR_UN_SUN_PATH)) { - throw InvalidUnixSocketPathException.INSTANCE; - } - int len = path.length + (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH); - PythonUtils.arraycopy(path, 0, addr.data, (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH), path.length); - addr.setLen(len); - return addr; - } - - @ExportLibrary(UniversalSockAddrLibrary.class) - protected static class UniversalSockAddrImpl implements UniversalSockAddr { - - private final NFIPosixSupport nfiPosixSupport; - private final byte[] data; - private final int[] len = new int[]{0}; - - UniversalSockAddrImpl(NFIPosixSupport nfiPosixSupport) { - this.nfiPosixSupport = nfiPosixSupport; - this.data = new byte[(int) getConstant(SIZEOF_STRUCT_SOCKADDR_STORAGE)]; - } - - @ExportMessage - int getFamily() { - int offset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY); - int size = (int) getConstant(SIZEOF_STRUCT_SOCKADDR_SA_FAMILY); - if (getLen() >= offset + size) { - if (size == 1) { - return Byte.toUnsignedInt(data[offset]); - } else if (size == 2) { - return Short.toUnsignedInt(ARRAY_ACCESSOR.getShort(data, offset)); - } else if (size == 4) { - return ARRAY_ACCESSOR.getInt(data, offset); - } else { - throw CompilerDirectives.shouldNotReachHere("Unexpected sizeof(sa_family_t)"); - } - } else { - return AF_UNSPEC.value; - } - } - - void setFamily(int family) { - int offset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY); - int size = (int) getConstant(SIZEOF_STRUCT_SOCKADDR_SA_FAMILY); - if (size == 1) { - data[offset] = (byte) family; - } else if (size == 2) { - ARRAY_ACCESSOR.putShort(data, offset, (short) family); - } else if (size == 4) { - ARRAY_ACCESSOR.putInt(data, offset, family); - } else { - throw CompilerDirectives.shouldNotReachHere("Unexpected sizeof(sa_family_t)"); - } - } - - @ExportMessage - Inet4SockAddr asInet4SockAddr() { - if (getFamily() != AF_INET.value) { - throw CompilerDirectives.shouldNotReachHere("Only AF_INET socket address can be converted to Inet4SockAddr"); - } - if (getLen() != getConstant(SIZEOF_STRUCT_SOCKADDR_IN)) { - throw CompilerDirectives.shouldNotReachHere("Wrong size of socket addr struct in asInet4SockAddr"); - } - int port = Short.toUnsignedInt(ARRAY_ACCESSOR_BE.getShort(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT))); - int address = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR) + getConstant(OFFSETOF_STRUCT_IN_ADDR_S_ADDR)); - return new Inet4SockAddr(port, address); - } - - @ExportMessage - Inet6SockAddr asInet6SockAddr() { - if (getFamily() != AF_INET6.value) { - throw CompilerDirectives.shouldNotReachHere("Only AF_INET6 socket address can be converted to Inet6SockAddr"); - } - if (getLen() != getConstant(SIZEOF_STRUCT_SOCKADDR_IN6)) { - throw CompilerDirectives.shouldNotReachHere("Wrong size of socket addr struct in asInet6SockAddr"); - } - int port = Short.toUnsignedInt(ARRAY_ACCESSOR_BE.getShort(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT))); - int addrOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR) + (int) getConstant(OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR); - byte[] address = PythonUtils.arrayCopyOfRange(data, addrOffset, addrOffset + 16); - int flowInfo = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO)); - int scopeId = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID)); - return new Inet6SockAddr(port, address, flowInfo, scopeId); - } - - @ExportMessage - UnixSockAddr asUnixSockAddr() { - if (getFamily() != AF_UNIX.value) { - throw CompilerDirectives.shouldNotReachHere("Only AF_UNIX socket address can be converted to UnixSockAddr"); - } - int pathOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH); - byte[] pathBuf; - int linuxAddrLen = getLen() - pathOffset; - if (linuxAddrLen > 0 && data[pathOffset] == '\0') { - // Abstract Linux address - pathBuf = PythonUtils.arrayCopyOfRange(data, pathOffset, pathOffset + linuxAddrLen); - } else { - // Regular NULL-terminated string - int pathLen = ArrayUtils.indexOf(data, pathOffset, data.length, (byte) 0) - pathOffset; - assert pathLen >= 0; - pathBuf = PythonUtils.arrayCopyOfRange(data, pathOffset, pathOffset + pathLen); - } - return new UnixSockAddr(pathBuf); - } - - long getConstant(NFIPosixConstants constant) { - return nfiPosixSupport.getConstant(constant); - } - - int getLen() { - return len[0]; - } - - void setLen(int len) { - this.len[0] = len; - } - - } - - @ExportMessage - long semOpen(Object name, int openFlags, int mode, int value, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - Object ptr = invokeNode.call(this, PosixNativeFunction.call_sem_open, pathToCString(name), openFlags, mode, value); - if (invokeNode.getResultInterop().isNull(ptr)) { - throw getErrnoAndThrowPosixException(invokeNode); - } - try { - return invokeNode.getResultInterop().asPointer(ptr); - } catch (UnsupportedMessageException e) { - throw CompilerDirectives.shouldNotReachHere(e); - } - } - - @ExportMessage - void semClose(long handle, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_close, handle); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - void semUnlink(Object name, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_unlink, pathToCString(name)); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - private static final UnsupportedPosixFeatureException NO_SEM_GETVALUE_EXCEPTION = new UnsupportedPosixFeatureException("sem_getvalue is not available on the current platform"); - - @ExportMessage - int semGetValue(long handle, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - /* - * msimacek: It works on Linux, and it doesn't work on Darwin. It might work on some other - * Unix-likes, but it's hard to check, so let's assume it only works on Linux for now - */ - if (PythonLanguage.getPythonOS() != PythonOS.PLATFORM_LINUX) { - throw NO_SEM_GETVALUE_EXCEPTION; - } - int[] value = new int[1]; - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_getvalue, handle, value); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - return value[0]; - } - - @ExportMessage - void semPost(long handle, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_post, handle); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - void semWait(long handle, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_wait, handle); - if (res < 0) { - throw getErrnoAndThrowPosixException(invokeNode); - } - } - - @ExportMessage - boolean semTryWait(long handle, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_trywait, handle); - if (res < 0) { - int errno = getErrno(invokeNode); - if (errno == OSErrorEnum.EAGAIN.getNumber()) { - return false; - } - throw newPosixException(invokeNode, errno); - } - return true; - } - - @ExportMessage - boolean semTimedWait(long handle, long deadlineNs, - @Bind Node node, - @CachedLibrary("this") PosixSupportLibrary thisLib, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_LINUX) { - int res = invokeNode.callInt(this, PosixNativeFunction.call_sem_timedwait, handle, deadlineNs); - if (res < 0) { - int errno = getErrno(invokeNode); - if (errno == OSErrorEnum.ETIMEDOUT.getNumber()) { - return false; - } - throw newPosixException(invokeNode, errno); - } - return true; - } else { - long deadlineMs = deadlineNs / 1_000_000; - while (true) { - if (thisLib.semTryWait(this, handle)) { - return true; - } - long currentMs = System.currentTimeMillis(); - if (currentMs > deadlineMs) { - return false; - } - long delayMs = Math.min(deadlineMs - currentMs, 20); - TruffleSafepoint.setBlockedThreadInterruptible(node, Thread::sleep, delayMs); - } - } - } - - @ExportMessage - @SuppressWarnings("static-method") - public PwdResult getpwuid(long uid, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - return getpw(PosixNativeFunction.call_getpwuid_r, uid, invokeNode, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - @ExportMessage - @SuppressWarnings("static-method") - public PwdResult getpwnam(Object name, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - return getpw(PosixNativeFunction.call_getpwname_r, pathToCString(name), invokeNode, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - @ExportMessage - @SuppressWarnings("static-method") - public boolean hasGetpwentries() { - return true; - } - - @ExportMessage - @SuppressWarnings("static-method") - public PwdResult[] getpwentries( - @Shared("invoke") @Cached InvokeNativeFunction invokeNode, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - // Note: this is not thread safe, so potentially problematic while running multiple contexts - // within one VM - int sysConfMax = getSysConfPwdSizeMax(invokeNode); - int initialBufferSize = sysConfMax == -1 ? 1024 : sysConfMax; - - ArrayList result = new ArrayList<>(); - invokeNode.call(this, PosixNativeFunction.call_setpwent); - long[] bufferSize = new long[1]; - long[] output = new long[PWD_OUTPUT_LEN]; - byte[] buffer = new byte[initialBufferSize]; - try { - while (true) { - Object pwPtr = invokeNode.call(this, PosixNativeFunction.call_getpwent, bufferSize); - if (invokeNode.getResultInterop().isNull(pwPtr)) { - break; - } - if (bufferSize[0] < 0 || bufferSize[0] > PWD_BUFFER_MAX_SIZE) { - throw outOfMemoryPosixError(); - } - if (buffer.length < bufferSize[0]) { - buffer = new byte[(int) bufferSize[0]]; - } - int code = invokeNode.callInt(this, PosixNativeFunction.get_getpwent_data, pwPtr, buffer, buffer.length, output); - if (code != 0) { - throw CompilerDirectives.shouldNotReachHere("get_getpwent_data failed"); - } - result.add(createPwdResult(buffer, output, fromByteArrayNode, switchEncodingFromUtf8Node)); - } - } finally { - invokeNode.call(this, PosixNativeFunction.call_endpwent); - } - return toPwdResultArray(result); - } - - @TruffleBoundary - private static PwdResult[] toPwdResultArray(ArrayList result) { - return result.toArray(new PwdResult[0]); - } - - private PwdResult getpw(PosixNativeFunction pwfun, Object pwfunArg, InvokeNativeFunction invokeNode, TruffleString.FromByteArrayNode fromByteArrayNode, - TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - int sysConfMax = getSysConfPwdSizeMax(invokeNode); - int bufferSize = sysConfMax == -1 ? 1024 : sysConfMax; - while (bufferSize < PWD_BUFFER_MAX_SIZE) { - byte[] data = new byte[bufferSize]; - long[] output = new long[PWD_OUTPUT_LEN]; - int result = invokeNode.callInt(this, pwfun, pwfunArg, data, data.length, output); - if (result == -1) { - return null; - } - if (result == 0) { - return createPwdResult(data, output, fromByteArrayNode, switchEncodingFromUtf8Node); - } - if (result != OSErrorEnum.ERANGE.getNumber() || sysConfMax != -1) { - // no point in trying larger buffer if we got different error or the OS already told - // us that sysConfMax should be enough... - throw newPosixException(invokeNode, result); - } - bufferSize <<= 1; - } - throw outOfMemoryPosixError(); - } - - private static PwdResult createPwdResult(byte[] data, long[] output, TruffleString.FromByteArrayNode fromByteArrayNode, TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) - throws PosixException { - return new PwdResult( - extractZeroTerminatedString(data, output[0], fromByteArrayNode, switchEncodingFromUtf8Node), - output[1], output[2], - extractZeroTerminatedString(data, output[3], fromByteArrayNode, switchEncodingFromUtf8Node), - extractZeroTerminatedString(data, output[4], fromByteArrayNode, switchEncodingFromUtf8Node)); - } - - @ExportMessage - public int ioctlBytes(int fd, long request, byte[] arg, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_ioctl_bytes, fd, request, arg); - if (res < 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return res; - } - - @ExportMessage - public int ioctlInt(int fd, long request, int arg, - @Shared("invoke") @Cached InvokeNativeFunction invokeNode) throws PosixException { - int res = invokeNode.callInt(this, PosixNativeFunction.call_ioctl_int, fd, request, arg); - if (res < 0) { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - return res; - } - - private static TruffleString extractZeroTerminatedString(byte[] buffer, long longOffset, TruffleString.FromByteArrayNode fromByteArrayNode, - TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { - if (longOffset < 0 || longOffset >= buffer.length) { - throw outOfMemoryPosixError(); - } - int offset = (int) longOffset; - int end = ArrayUtils.indexOf(buffer, offset, buffer.length, (byte) 0); - if (end < 0) { - throw CompilerDirectives.shouldNotReachHere("Could not find the end of the string"); - } - // TODO PyUnicode_DecodeFSDefault - return createString(buffer, offset, end - offset, true, fromByteArrayNode, switchEncodingFromUtf8Node); - } - - private static PosixException outOfMemoryPosixError() throws PosixException { - throw new PosixErrnoException(OSErrorEnum.ENOMEM.getNumber(), OSErrorEnum.ENOMEM.getMessage()); - } - - private int sysConfPwdSizeMax = -1; - - private int getSysConfPwdSizeMax(InvokeNativeFunction invokeNode) throws PosixException { - if (CompilerDirectives.injectBranchProbability(SLOWPATH_PROBABILITY, sysConfPwdSizeMax == -1)) { - long sysConfMaxLong = invokeNode.callLong(this, PosixNativeFunction.get_sysconf_getpw_r_size_max); - if (sysConfMaxLong != -1 && (sysConfMaxLong < 0 || sysConfMaxLong > PWD_BUFFER_MAX_SIZE)) { - throw outOfMemoryPosixError(); - } - sysConfPwdSizeMax = (int) sysConfMaxLong; - } - return sysConfPwdSizeMax; - } - - // ------------------ - // Path conversions - - @ExportMessage - @SuppressWarnings("static-method") - public Object createPathFromString(TruffleString path, - @Shared("toUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingNode, - @Shared("tsCopyBytes") @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { - return checkPath(getStringBytes(path, switchEncodingNode, copyToByteArrayNode)); - } - - @ExportMessage - @SuppressWarnings("static-method") - public Object createPathFromBytes(byte[] path) { - return checkPath(path); - } - - @ExportMessage - @SuppressWarnings("static-method") - public TruffleString getPathAsString(Object path, - @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, - @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { - Buffer result = (Buffer) path; - if (result.length > Integer.MAX_VALUE) { - // sanity check that it is safe to cast result.length to int, to be removed once - // we support large arrays - throw CompilerDirectives.shouldNotReachHere("Posix path cannot fit into a Java array"); - } - return createString(result.data, 0, (int) result.length, true, fromByteArrayNode, switchEncodingNode); - } - - @ExportMessage - @SuppressWarnings("static-method") - public Buffer getPathAsBytes(Object path) { - return (Buffer) path; - } - - private static TruffleString createString(byte[] src, int offset, int length, boolean copy, TruffleString.FromByteArrayNode fromByteArrayNode, - TruffleString.SwitchEncodingNode switchEncodingNode) { - // TODO PyUnicode_DecodeFSDefault - TruffleString utf8 = fromByteArrayNode.execute(src, offset, length, UTF_8, copy); - return switchEncodingNode.execute(utf8, TS_ENCODING); - } - - private static byte[] getStringBytes(TruffleString str, TruffleString.SwitchEncodingNode switchEncodingNode, TruffleString.CopyToByteArrayNode copyToByteArrayNode) { - // TODO replace getBytes with PyUnicode_FSConverter equivalent - TruffleString utf8 = switchEncodingNode.execute(str, UTF_8); - byte[] bytes = new byte[utf8.byteLength(UTF_8)]; - copyToByteArrayNode.execute(utf8, 0, bytes, 0, bytes.length, UTF_8); - return bytes; - } - - private static Buffer checkPath(byte[] path) { - for (byte b : path) { - if (b == 0) { - return null; - } - } - // TODO we keep a byte[] provided by the caller, who can potentially change it, making our - // check for embedded nulls pointless. Maybe we should copy it and while on it, might as - // well add the terminating null character, avoiding the copy we do later in pathToCString. - return Buffer.wrap(path); - } - - // ------------------ - // Objects/handles/pointers - - protected static class DirEntry { - final Buffer name; - final long ino; - final int type; - - DirEntry(Buffer name, long ino, int type) { - this.name = name; - this.ino = ino; - this.type = type; - } - - @Override - public String toString() { - return "DirEntry{" + - "name='" + new String(name.data, 0, (int) name.length) + "'" + - ", ino=" + ino + - ", type=" + type + - '}'; - } - } - - // ------------------ - // Helpers - - private int getErrno(InvokeNativeFunction invokeNode) { - return invokeNode.callInt(this, PosixNativeFunction.get_errno); - } - - private void setErrno(InvokeNativeFunction invokeNode, int errno) { - invokeNode.call(this, PosixNativeFunction.set_errno, errno); - } - - private PosixException getErrnoAndThrowPosixException(InvokeNativeFunction invokeNode) throws PosixException { - throw newPosixException(invokeNode, getErrno(invokeNode)); - } - - @TruffleBoundary - private PosixException newPosixException(InvokeNativeFunction invokeNode, int errno) throws PosixException { - throw new PosixErrnoException(errno, strerror(errno, invokeNode, TruffleString.FromByteArrayNode.getUncached(), TruffleString.SwitchEncodingNode.getUncached())); - } - - private Object wrap(long[] value) { - if (value == null) { - return PNone.NO_VALUE; - } else { - return value; - } - } - - private Object wrap(Timeval[] timeval) { - if (timeval == null) { - return PNone.NO_VALUE; - } else { - return new long[]{timeval[0].getSeconds(), timeval[0].getMicroseconds(), timeval[1].getSeconds(), timeval[1].getMicroseconds()}; - } - } - - private static long[] wrapItimerval(Timeval delay, Timeval interval) { - return new long[]{delay.getSeconds(), delay.getMicroseconds(), interval.getSeconds(), interval.getMicroseconds()}; - } - - private static Timeval[] unwrapTimeval(long[] timeval) { - return new Timeval[]{new Timeval(timeval[0], timeval[1]), new Timeval(timeval[2], timeval[3])}; - } - - private static TruffleString cStringToTruffleString(byte[] buf, TruffleString.FromByteArrayNode fromByteArrayNode, TruffleString.SwitchEncodingNode switchEncodingNode) { - return createString(buf, 0, findZero(buf), true, fromByteArrayNode, switchEncodingNode); - } - - private static int findZero(byte[] buf) { - for (int i = 0; i < buf.length; ++i) { - if (buf[i] == 0) { - return i; - } - } - return buf.length; - } - - private Object pathToCStringOrNull(Object path) { - return path == null ? PNone.NO_VALUE : bufferToCString((Buffer) path); - } - - private Object pathToCString(Object path) { - return bufferToCString((Buffer) path); - } - - private Object bufferToCString(Buffer path) { - return nullTerminate(path.data, (int) path.length); - } - - private Object stringToUTF8CString(TruffleString input, - @Cached TruffleString.SwitchEncodingNode switchEncodingToUtf8Node, - @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { - byte[] utf8 = getStringBytes(input, switchEncodingToUtf8Node, copyToByteArrayNode); - return nullTerminate(utf8, utf8.length); - } - - private static byte[] nullTerminate(byte[] str, int length) { - byte[] terminated = new byte[length + 1]; - PythonUtils.arraycopy(str, 0, terminated, 0, length); - return terminated; - } - - private static void checkBounds(byte[] buf, int offset, int length) { - if (length < 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IllegalArgumentException(); - } - if (offset < 0 || offset + length > buf.length) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - throw new IndexOutOfBoundsException(); - } - } - - @TruffleBoundary - private static void log(Level level, String fmt, Object... args) { - if (LOGGER.isLoggable(level)) { - LOGGER.log(level, String.format(fmt, args)); - } - } -} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixConstants.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixConstants.java similarity index 96% rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixConstants.java rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixConstants.java index ef20d53718..edb4fb39df 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NFIPosixConstants.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -40,7 +40,7 @@ */ package com.oracle.graal.python.runtime; -public enum NFIPosixConstants { +public enum NativePosixConstants { // start generated SIZEOF_STRUCT_SOCKADDR, SIZEOF_STRUCT_SOCKADDR_SA_FAMILY, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java new file mode 100644 index 0000000000..35ca5ebdab --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java @@ -0,0 +1,3427 @@ +/* + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +// skip GIL +package com.oracle.graal.python.runtime; + +import static com.oracle.graal.python.annotations.NativeSimpleType.POINTER; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT32; +import static com.oracle.graal.python.annotations.NativeSimpleType.SINT64; +import static com.oracle.graal.python.annotations.NativeSimpleType.VOID; +import static com.oracle.graal.python.nodes.StringLiterals.J_NFI_LANGUAGE; +import static com.oracle.graal.python.nodes.StringLiterals.T_NATIVE; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_IN_ADDR_S_ADDR; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY; +import static com.oracle.graal.python.runtime.NativePosixConstants.OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH; +import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_IN; +import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_IN6; +import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_SA_FAMILY; +import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_STORAGE; +import static com.oracle.graal.python.runtime.NativePosixConstants.SIZEOF_STRUCT_SOCKADDR_UN_SUN_PATH; +import static com.oracle.graal.python.runtime.PosixConstants.AF_INET; +import static com.oracle.graal.python.runtime.PosixConstants.AF_INET6; +import static com.oracle.graal.python.runtime.PosixConstants.AF_UNIX; +import static com.oracle.graal.python.runtime.PosixConstants.AF_UNSPEC; +import static com.oracle.graal.python.runtime.PosixConstants.HOST_NAME_MAX; +import static com.oracle.graal.python.runtime.PosixConstants.INET6_ADDRSTRLEN; +import static com.oracle.graal.python.runtime.PosixConstants.INET_ADDRSTRLEN; +import static com.oracle.graal.python.runtime.PosixConstants.L_ctermid; +import static com.oracle.graal.python.runtime.PosixConstants.NI_MAXHOST; +import static com.oracle.graal.python.runtime.PosixConstants.NI_MAXSERV; +import static com.oracle.graal.python.runtime.PosixConstants.PATH_MAX; +import static com.oracle.graal.python.runtime.PosixConstants.WNOHANG; +import static com.oracle.graal.python.runtime.PosixConstants._POSIX_HOST_NAME_MAX; +import static com.oracle.graal.python.runtime.PosixSupportLibrary.POSIX_FILENAME_SEPARATOR; +import static com.oracle.graal.python.runtime.PosixSupportLibrary.UnsupportedPosixFeatureException; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; +import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR; +import static com.oracle.graal.python.util.PythonUtils.ARRAY_ACCESSOR_BE; +import static com.oracle.graal.python.util.PythonUtils.EMPTY_LONG_ARRAY; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; +import static com.oracle.truffle.api.CompilerDirectives.shouldNotReachHere; +import static com.oracle.truffle.api.strings.TruffleString.Encoding.UTF_8; + +import java.util.ArrayList; +import java.util.logging.Level; + +import org.graalvm.nativeimage.ImageInfo; + +import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.annotations.DowncallSignature; +import com.oracle.graal.python.annotations.PythonOS; +import com.oracle.graal.python.builtins.PythonBuiltinClassType; +import com.oracle.graal.python.builtins.objects.exception.OSErrorEnum; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.runtime.PosixSupportLibrary.AcceptResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.AddrInfoCursor; +import com.oracle.graal.python.runtime.PosixSupportLibrary.AddrInfoCursorLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.Buffer; +import com.oracle.graal.python.runtime.PosixSupportLibrary.GetAddrInfoException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet4SockAddr; +import com.oracle.graal.python.runtime.PosixSupportLibrary.Inet6SockAddr; +import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidAddressException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.InvalidUnixSocketPathException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.OpenPtyResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixErrnoException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PosixException; +import com.oracle.graal.python.runtime.PosixSupportLibrary.PwdResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.RecvfromResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.RusageResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.SelectResult; +import com.oracle.graal.python.runtime.PosixSupportLibrary.Timeval; +import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddr; +import com.oracle.graal.python.runtime.PosixSupportLibrary.UniversalSockAddrLibrary; +import com.oracle.graal.python.runtime.PosixSupportLibrary.UnixSockAddr; +import com.oracle.graal.python.runtime.nativeaccess.NativeLibrary; +import com.oracle.graal.python.runtime.nativeaccess.NativeLibraryLoadException; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemory; +import com.oracle.graal.python.util.OverflowException; +import com.oracle.graal.python.util.PythonUtils; +import com.oracle.truffle.api.ArrayUtils; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.TruffleFile; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.TruffleLogger; +import com.oracle.truffle.api.TruffleSafepoint; +import com.oracle.truffle.api.dsl.Bind; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.Cached.Shared; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.library.ExportLibrary; +import com.oracle.truffle.api.library.ExportMessage; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.strings.TruffleString; + +import sun.misc.Unsafe; + +/** + * Implementation that invokes the native POSIX support library through generated native-access + * downcalls. + */ +@ExportLibrary(PosixSupportLibrary.class) +public final class NativePosixSupport extends PosixSupport { + private static final String SUPPORTING_NATIVE_LIB_NAME = "posix"; + private static final Source NFI_WARMUP_SIGNATURE = Source.newBuilder(J_NFI_LANGUAGE, "with native ():void", "python-nfi-warmup").internal(true).build(); + + private static final int UNAME_BUF_LENGTH = 256; + private static final int DIRENT_NAME_BUF_LENGTH = 256; + private static final int PWD_OUTPUT_LEN = 5; + private static final int PWD_BUFFER_MAX_SIZE = Integer.MAX_VALUE >> 2; + private static final int STRERROR_BUF_LENGTH = 1024; + + private static final int MAX_READ = Integer.MAX_VALUE / 2; + + private static final TruffleLogger LOGGER = PythonLanguage.getLogger(NativePosixSupport.class); + + private static final Unsafe UNSAFE = PythonUtils.initUnsafe(); + + private static final Object CRYPT_LOCK = new Object(); + + abstract static class PosixNativeFunctionInvoker { + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32}) + abstract int init_constants(long out, int len); + + @DowncallSignature(returnType = SINT32) + abstract int get_errno(); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT32}) + abstract void set_errno(int errno); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT64, SINT32, SINT32, SINT32, SINT64}) + abstract long call_mmap(long length, int prot, int flags, int fd, long offset); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, SINT64}) + abstract int call_munmap(long address, long length); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT64, SINT64, SINT64}) + abstract void call_msync(long address, long offset, long length); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT32, POINTER, SINT32}) + abstract void call_strerror(int error, long buf, int buflen); + + @DowncallSignature(returnType = SINT64) + abstract long call_getpid(); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_umask(int mask); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32}) + abstract int call_openat(int dirFd, long pathname, int flags, int mode); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_close(int fd); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32, POINTER, SINT64}) + abstract long call_read(int fd, long buf, long count); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32, POINTER, SINT64}) + abstract long call_write(int fd, long buf, long count); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_dup(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT32}) + abstract int call_dup2(int oldfd, int newfd, int inheritable); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_pipe2(long pipefd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, POINTER, SINT32, POINTER, SINT32, SINT64, SINT64, POINTER}) + abstract int call_select(int nfds, long readfds, int readfdsLen, long writefds, int writefdsLen, long errfds, int errfdsLen, long timeoutSec, long timeoutUsec, long selected); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT64, SINT64}) + abstract int call_poll(int fd, int writing, long timeoutSec, long timeoutUsec); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32, SINT64, SINT32}) + abstract long call_lseek(int fd, long offset, int whence); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT64}) + abstract int call_ftruncate(int fd, long length); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64}) + abstract int call_truncate(long path, long length); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_fsync(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int call_flock(int fd, int operation); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT32, SINT32, SINT64, SINT64}) + abstract int call_fcntl_lock(int fd, int blocking, int lockType, int whence, long start, long length); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, POINTER}) + abstract int call_fstatat(int dirFd, long path, int followSymlinks, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_fstat(int fd, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int call_statvfs(long path, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_fstatvfs(int fd, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, POINTER, POINTER, POINTER, SINT32}) + abstract int call_uname(long sysname, long nodename, long release, long version, long machine, int size); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32}) + abstract int call_unlinkat(int dirFd, long pathname, int rmdir); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, POINTER, SINT32}) + abstract int call_linkat(int oldDirFd, long oldPath, int newDirFd, long newPath, int flags); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, POINTER}) + abstract int call_symlinkat(long target, int dirFd, long linkpath); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32}) + abstract int call_mkdirat(int dirFd, long pathname, int mode); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64}) + abstract int call_getcwd(long buf, long size); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_chdir(long path); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_fchdir(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_isatty(int fd); + + @DowncallSignature(returnType = SINT64, argumentTypes = {POINTER}) + abstract long call_opendir(long name); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32}) + abstract long call_fdopendir(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64}) + abstract int call_closedir(long dirp); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, POINTER, SINT64, POINTER}) + abstract int call_readdir(long dirp, long nameBuf, long nameBufSize, long out); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT64}) + abstract void call_rewinddir(long dirp); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER, SINT32}) + abstract int call_utimensat(int dirFd, long path, long timespec, int followSymlinks); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_futimens(int fd, long timespec); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_futimes(int fd, long timeval); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int call_lutimes(long filename, long timeval); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int call_utimes(long filename, long timeval); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, POINTER}) + abstract int call_renameat(int oldDirFd, long oldPath, int newDirFd, long newPath); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32, SINT32}) + abstract int call_faccessat(int dirFd, long path, int mode, int effectiveIds, int followSymlinks); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32}) + abstract int call_fchmodat(int dirFd, long path, int mode, int followSymlinks); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int call_fchmod(int fd, int mode); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT64, SINT64, SINT32}) + abstract int call_fchownat(int dirfd, long pathname, long owner, long group, int followSymlinks); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT64, SINT64}) + abstract int call_fchown(int fd, long owner, long group); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32, POINTER, POINTER, SINT64}) + abstract long call_readlinkat(int dirFd, long path, long buf, long size); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int get_inheritable(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int set_inheritable(int fd, int inheritable); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int get_blocking(int fd); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int set_blocking(int fd, int blocking); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int get_terminal_size(int fd, long size); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_raise(int signal); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_alarm(int seconds); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_getitimer(int which, long currentValue); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER}) + abstract int call_setitimer(int which, long newValue, long oldValue); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int signal_self(int signal); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, SINT32}) + abstract int call_kill(long pid, int signal); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, SINT32}) + abstract int call_killpg(long pgid, int signal); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT64, POINTER, SINT32}) + abstract long call_waitpid(long pid, long status, int options); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wcoredump(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wifcontinued(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wifstopped(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wifsignaled(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wifexited(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wexitstatus(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wtermsig(int status); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32}) + abstract int call_wstopsig(int status); + + @DowncallSignature(returnType = SINT64) + abstract long call_getuid(); + + @DowncallSignature(returnType = SINT64) + abstract long call_geteuid(); + + @DowncallSignature(returnType = SINT64) + abstract long call_getgid(); + + @DowncallSignature(returnType = SINT64) + abstract long call_getegid(); + + @DowncallSignature(returnType = SINT64) + abstract long call_getppid(); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT64}) + abstract long call_getpgid(long pid); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, SINT64}) + abstract int call_setpgid(long pid, long pgid); + + @DowncallSignature(returnType = SINT64) + abstract long call_getpgrp(); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT64}) + abstract long call_getsid(long pid); + + @DowncallSignature(returnType = SINT64) + abstract long call_setsid(); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, POINTER}) + abstract int call_getgroups(long size, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_getrusage(int who, long out); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_openpty(long outvars); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_ctermid(long buf); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT32}) + abstract int call_setenv(long name, long value, int overwrite); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_unsetenv(long name); + + @DowncallSignature(returnType = SINT32, argumentTypes = { + POINTER, POINTER, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, + SINT32, SINT32, SINT32, SINT32, SINT32, POINTER, SINT64 + }) + abstract int fork_exec(long data, long offsets, int offsetsLen, int argsPos, int envPos, int cwdPos, int stdinRdFd, int stdinWrFd, int stdoutRdFd, int stdoutWrFd, int stderrRdFd, + int stderrWrFd, int errPipeRdFd, int errPipeWrFd, int closeFds, int restoreSignals, int callSetsid, long fdsToKeep, long fdsToKeepLen); + + @DowncallSignature(returnType = VOID, argumentTypes = {POINTER, POINTER, SINT32}) + abstract void call_execv(long data, long offsets, int offsetsLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_system(long pathname); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, POINTER, SINT32, POINTER}) + abstract int call_getpwuid_r(long uid, long buffer, int bufferSize, long output); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT32, POINTER}) + abstract int call_getpwname_r(long name, long buffer, int bufferSize, long output); + + @DowncallSignature(returnType = VOID) + abstract void call_setpwent(); + + @DowncallSignature(returnType = VOID) + abstract void call_endpwent(); + + @DowncallSignature(returnType = POINTER, argumentTypes = {POINTER}) + abstract long call_getpwent(long bufferSize); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT32, POINTER}) + abstract int get_getpwent_data(long p, long buffer, int bufferSize, long output); + + @DowncallSignature(returnType = SINT64) + abstract long get_sysconf_getpw_r_size_max(); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT32}) + abstract int call_socket(int family, int type, int protocol); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER}) + abstract int call_accept(int sockfd, long addr, long addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32}) + abstract int call_bind(int sockfd, long addr, int addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32}) + abstract int call_connect(int sockfd, long addr, int addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int call_listen(int sockfd, int backlog); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER}) + abstract int call_getpeername(int sockfd, long addr, long addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER}) + abstract int call_getsockname(int sockfd, long addr, long addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32}) + abstract int call_send(int sockfd, long buf, int len, int flags); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32, SINT32, POINTER, SINT32}) + abstract int call_sendto(int sockfd, long buf, int offset, int len, int flags, long addr, int addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32}) + abstract int call_recv(int sockfd, long buf, int len, int flags); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, SINT32, SINT32, SINT32, POINTER, POINTER}) + abstract int call_recvfrom(int sockfd, long buf, int offset, int len, int flags, long srcAddr, long addrLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32}) + abstract int call_shutdown(int sockfd, int how); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT32, POINTER, POINTER}) + abstract int call_getsockopt(int sockfd, int level, int optname, long buf, long bufLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT32, SINT32, POINTER, SINT32}) + abstract int call_setsockopt(int sockfd, int level, int optname, long buf, int bufLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_inet_addr(long src); + + @DowncallSignature(returnType = SINT64, argumentTypes = {POINTER}) + abstract long call_inet_aton(long src); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER}) + abstract int call_inet_ntoa(int src, long dst); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER}) + abstract int call_inet_pton(int family, long src, long dst); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, POINTER, POINTER, SINT32}) + abstract int call_inet_ntop(int family, long src, long dst, int dstSize); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64}) + abstract int call_gethostname(long buf, long bufLen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT32, POINTER, SINT32, POINTER, SINT32, SINT32}) + abstract int call_getnameinfo(long addr, int addrLen, long hostBuf, int hostBufLen, long servBuf, int servBufLen, int flags); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER, SINT32, SINT32, SINT32, SINT32, POINTER}) + abstract int call_getaddrinfo(long node, long service, int family, int sockType, int protocol, int flags, long ptr); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT64}) + abstract void call_freeaddrinfo(long ptr); + + @DowncallSignature(returnType = VOID, argumentTypes = {SINT32, POINTER, SINT32}) + abstract void call_gai_strerror(int error, long buf, int buflen); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT64, POINTER, POINTER, POINTER}) + abstract int get_addrinfo_members(long ptr, long intData, long longData, long addr); + + @DowncallSignature(returnType = POINTER, argumentTypes = {POINTER, SINT32, SINT32, SINT32}) + abstract long call_sem_open(long name, int openFlags, int mode, int value); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_sem_close(long handle); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_sem_unlink(long name); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, POINTER}) + abstract int call_sem_getvalue(long handle, long value); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_sem_post(long handle); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_sem_wait(long handle); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER}) + abstract int call_sem_trywait(long handle); + + @DowncallSignature(returnType = SINT32, argumentTypes = {POINTER, SINT64}) + abstract int call_sem_timedwait(long handle, long deadlineNs); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT64, POINTER}) + abstract int call_ioctl_bytes(int fd, long request, long buffer); + + @DowncallSignature(returnType = SINT32, argumentTypes = {SINT32, SINT64, SINT32}) + abstract int call_ioctl_int(int fd, long request, int arg); + + @DowncallSignature(returnType = SINT64, argumentTypes = {SINT32}) + abstract long call_sysconf(int name); + + @TruffleBoundary + static String getLibPath(PythonContext context) { + String libPythonName = PythonContext.getSupportLibName(SUPPORTING_NATIVE_LIB_NAME); + TruffleFile homePath = context.getEnv().getInternalTruffleFile(context.getCAPIHome().toJavaStringUncached()); + TruffleFile file = homePath.resolve(libPythonName); + return file.getPath(); + } + + @TruffleBoundary + static NativeLibrary loadNativeLibrary(PythonContext context) { + String path = getLibPath(context); + try { + return context.ensureNativeContext().loadLibrary(path, PosixConstants.RTLD_LOCAL.value); + } catch (NativeLibraryLoadException e) { + throw new UnsupportedOperationException(String.format(""" + Could not load posix support library from path '%s'. Troubleshooting:\s + Check permissions of the file.""", path), e); + } + } + } + + abstract static class CryptNativeFunctionInvoker { + @DowncallSignature(returnType = POINTER, argumentTypes = {POINTER, POINTER}) + abstract long crypt(long word, long salt); + + @TruffleBoundary + static NativeLibrary loadNativeLibrary(PythonContext context) { + if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_DARWIN) { + return context.ensureNativeContext().getDefaultLibrary(); + } + /* + * We don't want to link the posix support library against libcrypt, because it might + * not be available on the target Linux system and would make the whole support library + * fail to load. Load it dynamically on demand instead. + */ + try { + return context.ensureNativeContext().loadLibrary("libcrypt.so", PosixConstants.RTLD_LOCAL.value); + } catch (NativeLibraryLoadException e) { + throw new UnsupportedOperationException("Could not load crypt support library 'libcrypt.so'.", e); + } + } + } + + private final PythonContext context; + private final TruffleString nativeBackend; + private final PosixNativeFunctionInvoker posixNativeFunctionInvoker; + private final CryptNativeFunctionInvoker cryptNativeFunctionInvoker; + @CompilationFinal(dimensions = 1) private long[] constantValues; + + public NativePosixSupport(PythonContext context, TruffleString nativeBackend) { + assert nativeBackend.equalsUncached(T_NATIVE, TS_ENCODING); + this.context = context; + this.nativeBackend = nativeBackend; + this.posixNativeFunctionInvoker = new PosixNativeFunctionInvokerGen(context); + this.cryptNativeFunctionInvoker = new CryptNativeFunctionInvokerGen(context); + setEnv(context.getEnv()); + } + + long getConstant(NativePosixConstants constant) { + if (constantValues == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + long[] values = new long[NativePosixConstants.values().length]; + long nativeValues = NativeMemory.mallocLongArray(values.length); + try { + int result = posixNativeFunctionInvoker.init_constants(nativeValues, values.length); + if (result != 0) { + throw CompilerDirectives.shouldNotReachHere("Mismatched build of posix native library"); + } + NativeMemory.readLongArrayElements(nativeValues, 0, values, 0, values.length); + constantValues = values; + } finally { + NativeMemory.free(nativeValues); + } + } + return constantValues[constant.ordinal()]; + } + + @Override + public void setEnv(Env env) { + if (env.isPreInitialization()) { + return; + } + // Load NFI on the Python thread before any blocking native read can pin it. + // Workaround for GR-75767 + if (env.getInternalLanguages().containsKey(J_NFI_LANGUAGE)) { + try { + env.parseInternal(NFI_WARMUP_SIGNATURE).call(); + } catch (RuntimeException e1) { + LOGGER.log(Level.FINE, "Failed to eagerly initialize NFI warmup signature.", e1); + } + } + // Java NIO (and TruffleFile) do not expect/support changing native working directory since + // it is inherently thread-unsafe operation. It is not defined how NIO behaves when native + // cwd changes, thus we need to prevent TruffleFile from resolving relative paths using + // NIO by setting Truffle cwd to a know value. This cannot be done lazily in chdir() because + // native cwd is global, but Truffle cwd is per context. + // TruffleFile will be unaware of the real working directory and keep resolving against the + // original working directory. This should not matter since we do not use TruffleFile for + // ordinary I/O when using the native backend. + try { + TruffleFile truffleFile = context.getEnv().getInternalTruffleFile(".").getAbsoluteFile(); + context.getEnv().setCurrentWorkingDirectory(truffleFile); + } catch (Exception e) { + LOGGER.log(Level.WARNING, "Unable to change Truffle working directory", e); + } + } + + @ExportMessage + public TruffleString getBackend() { + return nativeBackend; + } + + @ExportMessage + public TruffleString strerror(int errorCode, + @Bind Node inliningTarget, + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) { + // From man pages: The GNU C Library uses a buffer of 1024 characters for strerror(). + // This buffer size therefore should be sufficient to avoid an ERANGE error when calling + // strerror_r(). + long buf = NativeMemory.mallocByteArray(STRERROR_BUF_LENGTH); + try { + posixNativeFunctionInvoker.call_strerror(errorCode, buf, STRERROR_BUF_LENGTH); + // TODO PyUnicode_DecodeLocale + return zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, buf); + } finally { + NativeMemory.free(buf); + } + } + + @ExportMessage + public long getpid() { + return posixNativeFunctionInvoker.call_getpid(); + } + + @ExportMessage + public int umask(int mask) throws PosixException { + int result = posixNativeFunctionInvoker.call_umask(mask); + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } + return result; + } + + @ExportMessage + public int openat(int dirFd, Object pathname, int flags, int mode) throws PosixException { + long pathnamePtr = pathToNativeCString(pathname); + try { + int fd = posixNativeFunctionInvoker.call_openat(dirFd, pathnamePtr, flags, mode); + if (fd < 0) { + throw getErrnoAndThrowPosixException(); + } + return fd; + } finally { + NativeMemory.free(pathnamePtr); + } + } + + @ExportMessage + public int close(int fd) throws PosixException { + final int rv = posixNativeFunctionInvoker.call_close(fd); + if (rv < 0) { + throw getErrnoAndThrowPosixException(); + } + return rv; + } + + @ExportMessage + public Buffer read(int fd, long length) throws PosixException { + long count = Math.min(length, MAX_READ); + Buffer buffer = Buffer.allocate(count); + long nativeBuffer = NativeMemory.mallocByteArrayOrNull(count); + try { + posixNativeFunctionInvoker.set_errno(0); + long n = posixNativeFunctionInvoker.call_read(fd, nativeBuffer, count); + if (n < 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuffer, 0, buffer.data, 0, (int) n); + return buffer.withLength(n); + } finally { + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public long write(int fd, Buffer data) throws PosixException { + long nativeBuffer = NativeMemory.mallocByteArrayOrNull(data.length); + try { + NativeMemory.writeByteArrayElements(nativeBuffer, 0, data.data, 0, (int) data.length); + posixNativeFunctionInvoker.set_errno(0); + long n = posixNativeFunctionInvoker.call_write(fd, nativeBuffer, data.length); + if (n < 0) { + throw getErrnoAndThrowPosixException(); + } + return n; + } finally { + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public int dup(int fd) throws PosixException { + int newFd = posixNativeFunctionInvoker.call_dup(fd); + if (newFd < 0) { + throw getErrnoAndThrowPosixException(); + } + return newFd; + } + + @ExportMessage + public int dup2(int fd, int fd2, boolean inheritable) throws PosixException { + int newFd = posixNativeFunctionInvoker.call_dup2(fd, fd2, inheritable ? 1 : 0); + if (newFd < 0) { + throw getErrnoAndThrowPosixException(); + } + return newFd; + } + + @ExportMessage + public boolean getInheritable(int fd) throws PosixException { + int result = posixNativeFunctionInvoker.get_inheritable(fd); + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } + return result != 0; + } + + @ExportMessage + public void setInheritable(int fd, boolean inheritable) throws PosixException { + if (posixNativeFunctionInvoker.set_inheritable(fd, inheritable ? 1 : 0) < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public int[] pipe() throws PosixException { + int[] fds = new int[2]; + long nativeFds = NativeMemory.mallocIntArray(fds.length); + try { + if (posixNativeFunctionInvoker.call_pipe2(nativeFds) != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readIntArrayElements(nativeFds, 0, fds, 0, fds.length); + return fds; + } finally { + NativeMemory.free(nativeFds); + } + } + + @ExportMessage + public SelectResult select(int[] readfds, int[] writefds, int[] errorfds, Timeval timeout) throws PosixException { + int largestFD = findMax(readfds, -1); + largestFD = findMax(writefds, largestFD); + largestFD = findMax(errorfds, largestFD); + // This will be treated as boolean array (output parameter), each item indicating if given + // FD was selected or not + byte[] selected = new byte[readfds.length + writefds.length + errorfds.length]; + int nfds = largestFD == -1 ? 0 : largestFD + 1; + long secs = -1, usecs = -1; + if (timeout != null) { + secs = timeout.getSeconds(); + usecs = timeout.getMicroseconds(); + } + long nativeReadFds = NULLPTR; + long nativeWriteFds = NULLPTR; + long nativeErrorFds = NULLPTR; + long nativeSelected = NULLPTR; + try { + nativeReadFds = NativeMemory.copyToNativeIntArrayOrNull(readfds); + nativeWriteFds = NativeMemory.copyToNativeIntArrayOrNull(writefds); + nativeErrorFds = NativeMemory.copyToNativeIntArrayOrNull(errorfds); + nativeSelected = NativeMemory.mallocByteArrayOrNull(selected.length); + int result = posixNativeFunctionInvoker.call_select(nfds, + nativeReadFds, readfds.length, + nativeWriteFds, writefds.length, + nativeErrorFds, errorfds.length, + secs, usecs, nativeSelected); + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } + if (selected.length > 0) { + NativeMemory.readByteArrayElements(nativeSelected, 0, selected, 0, selected.length); + } + } finally { + NativeMemory.free(nativeSelected); + NativeMemory.free(nativeErrorFds); + NativeMemory.free(nativeWriteFds); + NativeMemory.free(nativeReadFds); + } + return new SelectResult( + selectFillInResult(readfds, selected, 0), + selectFillInResult(writefds, selected, readfds.length), + selectFillInResult(errorfds, selected, readfds.length + writefds.length)); + + } + + private static boolean[] selectFillInResult(int[] fds, byte[] selected, int selectedOffset) { + boolean[] res = new boolean[fds.length]; + for (int i = 0; i < fds.length; i++) { + res[i] = selected[selectedOffset + i] != 0; + } + return res; + } + + private static int findMax(int[] items, int currentMax) { + int max = currentMax; + for (int item : items) { + if (item > max) { + max = item; + } + } + return max; + } + + @ExportMessage + public boolean poll(int fd, boolean forWriting, Timeval timeout) throws PosixException { + long secs = -1, usecs = -1; + if (timeout != null) { + secs = timeout.getSeconds(); + usecs = timeout.getMicroseconds(); + } + int result = posixNativeFunctionInvoker.call_poll(fd, forWriting ? 1 : 0, secs, usecs); + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } + if (result == 0) { + return false; + } else { + return true; + } + } + + @ExportMessage + public long lseek(int fd, long offset, int how) throws PosixException { + long res = posixNativeFunctionInvoker.call_lseek(fd, offset, how); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return res; + } + + @ExportMessage + public void ftruncate(int fd, long length) throws PosixException { + int res = posixNativeFunctionInvoker.call_ftruncate(fd, length); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public void truncate(Object path, long length) throws PosixException { + long pathPtr = pathToNativeCString(path); + try { + int res = posixNativeFunctionInvoker.call_truncate(pathPtr, length); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public void fsync(int fd) throws PosixException { + int res = posixNativeFunctionInvoker.call_fsync(fd); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + void flock(int fd, int operation) throws PosixException { + int res = posixNativeFunctionInvoker.call_flock(fd, operation); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + void fcntlLock(int fd, boolean blocking, int lockType, int whence, long start, long length) throws PosixException { + int res = posixNativeFunctionInvoker.call_fcntl_lock(fd, blocking ? 1 : 0, lockType, whence, start, length); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public boolean getBlocking(int fd) throws PosixException { + int result = posixNativeFunctionInvoker.get_blocking(fd); + if (result < 0) { + throw getErrnoAndThrowPosixException(); + } + return result != 0; + } + + @ExportMessage + public void setBlocking(int fd, boolean blocking) throws PosixException { + if (posixNativeFunctionInvoker.set_blocking(fd, blocking ? 1 : 0) < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public int[] getTerminalSize(int fd) throws PosixException { + int[] size = new int[2]; + long nativeSize = NativeMemory.mallocIntArray(size.length); + try { + if (posixNativeFunctionInvoker.get_terminal_size(fd, nativeSize) != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readIntArrayElements(nativeSize, 0, size, 0, size.length); + return size; + } finally { + NativeMemory.free(nativeSize); + } + } + + @ExportMessage + public long sysconf(int name) throws PosixException { + long result = posixNativeFunctionInvoker.call_sysconf(name); + if (result == -1) { + int errno = posixNativeFunctionInvoker.get_errno(); + if (errno != 0) { + throw newPosixException(errno); + } + } + return result; + } + + @ExportMessage + public long[] fstatat(int dirFd, Object pathname, boolean followSymlinks) throws PosixException { + long[] out = new long[13]; + long nativeOut = NULLPTR; + long pathnamePtr = NULLPTR; + try { + nativeOut = NativeMemory.mallocLongArray(out.length); + pathnamePtr = pathToNativeCString(pathname); + int res = posixNativeFunctionInvoker.call_fstatat(dirFd, pathnamePtr, followSymlinks ? 1 : 0, nativeOut); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readLongArrayElements(nativeOut, 0, out, 0, out.length); + return out; + } finally { + NativeMemory.free(pathnamePtr); + NativeMemory.free(nativeOut); + } + } + + @ExportMessage + public long[] fstat(int fd) throws PosixException { + long[] out = new long[13]; + long nativeOut = NativeMemory.mallocLongArray(out.length); + try { + int res = posixNativeFunctionInvoker.call_fstat(fd, nativeOut); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readLongArrayElements(nativeOut, 0, out, 0, out.length); + return out; + } finally { + NativeMemory.free(nativeOut); + } + } + + @ExportMessage + public long[] statvfs(Object path) throws PosixException { + long[] out = new long[11]; + long nativeOut = NULLPTR; + long pathPtr = NULLPTR; + try { + nativeOut = NativeMemory.mallocLongArray(out.length); + pathPtr = pathToNativeCString(path); + int res = posixNativeFunctionInvoker.call_statvfs(pathPtr, nativeOut); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readLongArrayElements(nativeOut, 0, out, 0, out.length); + return out; + } finally { + NativeMemory.free(pathPtr); + NativeMemory.free(nativeOut); + } + } + + @ExportMessage + public long[] fstatvfs(int fd) throws PosixException { + long[] out = new long[11]; + long nativeOut = NativeMemory.mallocLongArray(out.length); + try { + int res = posixNativeFunctionInvoker.call_fstatvfs(fd, nativeOut); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readLongArrayElements(nativeOut, 0, out, 0, out.length); + return out; + } finally { + NativeMemory.free(nativeOut); + } + } + + @ExportMessage + public Object[] uname( + @Bind Node inliningTarget, + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) throws PosixException { + long sysPtr = NULLPTR; + long nodePtr = NULLPTR; + long relPtr = NULLPTR; + long verPtr = NULLPTR; + long machinePtr = NULLPTR; + try { + sysPtr = NativeMemory.mallocByteArray(UNAME_BUF_LENGTH); + nodePtr = NativeMemory.mallocByteArray(UNAME_BUF_LENGTH); + relPtr = NativeMemory.mallocByteArray(UNAME_BUF_LENGTH); + verPtr = NativeMemory.mallocByteArray(UNAME_BUF_LENGTH); + machinePtr = NativeMemory.mallocByteArray(UNAME_BUF_LENGTH); + int res = posixNativeFunctionInvoker.call_uname(sysPtr, nodePtr, relPtr, verPtr, machinePtr, UNAME_BUF_LENGTH); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + return new Object[]{ + // TODO PyUnicode_DecodeFSDefault + zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, sysPtr), + zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, nodePtr), + zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, relPtr), + zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, verPtr), + zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, machinePtr) + }; + } finally { + NativeMemory.free(machinePtr); + NativeMemory.free(verPtr); + NativeMemory.free(relPtr); + NativeMemory.free(nodePtr); + NativeMemory.free(sysPtr); + } + } + + @ExportMessage + public void unlinkat(int dirFd, Object pathname, boolean rmdir) throws PosixException { + long pathnamePtr = pathToNativeCString(pathname); + try { + int result = posixNativeFunctionInvoker.call_unlinkat(dirFd, pathnamePtr, rmdir ? 1 : 0); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathnamePtr); + } + } + + @ExportMessage + public void linkat(int oldFdDir, Object oldPath, int newFdDir, Object newPath, int flags) throws PosixException { + long oldPathPtr = NULLPTR; + long newPathPtr = NULLPTR; + try { + oldPathPtr = pathToNativeCString(oldPath); + newPathPtr = pathToNativeCString(newPath); + int result = posixNativeFunctionInvoker.call_linkat(oldFdDir, oldPathPtr, newFdDir, newPathPtr, flags); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(newPathPtr); + NativeMemory.free(oldPathPtr); + } + } + + @ExportMessage + public void symlinkat(Object target, int linkpathDirFd, Object linkpath) throws PosixException { + long targetPtr = NULLPTR; + long linkpathPtr = NULLPTR; + try { + targetPtr = pathToNativeCString(target); + linkpathPtr = pathToNativeCString(linkpath); + int result = posixNativeFunctionInvoker.call_symlinkat(targetPtr, linkpathDirFd, linkpathPtr); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(linkpathPtr); + NativeMemory.free(targetPtr); + } + } + + @ExportMessage + public void mkdirat(int dirFd, Object pathname, int mode) throws PosixException { + long pathnamePtr = pathToNativeCString(pathname); + try { + int result = posixNativeFunctionInvoker.call_mkdirat(dirFd, pathnamePtr, mode); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathnamePtr); + } + } + + @ExportMessage + public Object getcwd() throws PosixException { + for (int bufLen = 1024;; bufLen += 1024) { + Buffer buffer = Buffer.allocate(bufLen); + long nativeBuffer = NativeMemory.mallocByteArray(bufLen); + try { + int n = posixNativeFunctionInvoker.call_getcwd(nativeBuffer, bufLen); + if (n == 0) { + NativeMemory.readByteArrayElements(nativeBuffer, 0, buffer.data, 0, bufLen); + buffer = buffer.withLength(findZero(buffer.data)); + return buffer; + } + int errno = posixNativeFunctionInvoker.get_errno(); + if (errno != OSErrorEnum.ERANGE.getNumber()) { + throw newPosixException(errno); + } + } finally { + NativeMemory.free(nativeBuffer); + } + } + } + + @ExportMessage + public void chdir(Object path) throws PosixException { + long pathPtr = pathToNativeCString(path); + try { + int result = posixNativeFunctionInvoker.call_chdir(pathPtr); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public void fchdir(int fd) throws PosixException { + int result = posixNativeFunctionInvoker.call_fchdir(fd); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public boolean isatty(int fd) { + return posixNativeFunctionInvoker.call_isatty(fd) != 0; + } + + @ExportMessage + public Object opendir(Object path) throws PosixException { + long pathPtr = pathToNativeCString(path); + try { + long ptr = posixNativeFunctionInvoker.call_opendir(pathPtr); + if (ptr == 0) { + throw getErrnoAndThrowPosixException(); + } + return ptr; + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public Object fdopendir(int fd) throws PosixException { + long ptr = posixNativeFunctionInvoker.call_fdopendir(fd); + if (ptr == 0) { + throw getErrnoAndThrowPosixException(); + } + return ptr; + } + + @ExportMessage + public void closedir(Object dirStreamObj) throws PosixException { + int res = posixNativeFunctionInvoker.call_closedir(((Long) dirStreamObj).longValue()); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public Object readdir(Object dirStreamObj) throws PosixException { + Buffer name = Buffer.allocate(DIRENT_NAME_BUF_LENGTH); + long[] out = new long[2]; + long dirStream = ((Long) dirStreamObj).longValue(); + long nativeName = NULLPTR; + long nativeOut = NULLPTR; + try { + nativeName = NativeMemory.mallocByteArray(DIRENT_NAME_BUF_LENGTH); + nativeOut = NativeMemory.mallocLongArray(out.length); + int result; + do { + result = posixNativeFunctionInvoker.call_readdir(dirStream, nativeName, DIRENT_NAME_BUF_LENGTH, nativeOut); + if (result != 0) { + NativeMemory.readByteArrayElements(nativeName, 0, name.data, 0, name.data.length); + } + } while (result != 0 && name.data[0] == '.' && (name.data[1] == 0 || (name.data[1] == '.' && name.data[2] == 0))); + if (result != 0) { + NativeMemory.readLongArrayElements(nativeOut, 0, out, 0, out.length); + return new DirEntry(name.withLength(findZero(name.data)), out[0], (int) out[1]); + } + } finally { + NativeMemory.free(nativeOut); + NativeMemory.free(nativeName); + } + int errno = posixNativeFunctionInvoker.get_errno(); + if (errno == 0) { + return null; + } + throw newPosixException(errno); + } + + @ExportMessage + public void rewinddir(Object dirStreamObj) { + posixNativeFunctionInvoker.call_rewinddir(((Long) dirStreamObj).longValue()); + } + + @ExportMessage + @SuppressWarnings("static-method") + public Object dirEntryGetName(Object dirEntryObj) { + DirEntry dirEntry = (DirEntry) dirEntryObj; + return dirEntry.name; + } + + @ExportMessage + public static class DirEntryGetPath { + @Specialization(guards = "endsWithSlash(scandirPath)") + static Buffer withSlash(@SuppressWarnings("unused") NativePosixSupport receiver, DirEntry dirEntry, Object scandirPath) { + Buffer scandirPathBuffer = (Buffer) scandirPath; + int pathLen = scandirPathBuffer.data.length; + int nameLen = (int) dirEntry.name.length; + byte[] buf = new byte[pathLen + nameLen]; + PythonUtils.arraycopy(scandirPathBuffer.data, 0, buf, 0, pathLen); + PythonUtils.arraycopy(dirEntry.name.data, 0, buf, pathLen, nameLen); + return Buffer.wrap(buf); + } + + @Specialization(guards = "!endsWithSlash(scandirPath)") + static Buffer withoutSlash(@SuppressWarnings("unused") NativePosixSupport receiver, DirEntry dirEntry, Object scandirPath) { + Buffer scandirPathBuffer = (Buffer) scandirPath; + int pathLen = scandirPathBuffer.data.length; + int nameLen = (int) dirEntry.name.length; + byte[] buf = new byte[pathLen + 1 + nameLen]; + PythonUtils.arraycopy(scandirPathBuffer.data, 0, buf, 0, pathLen); + buf[pathLen] = POSIX_FILENAME_SEPARATOR; + PythonUtils.arraycopy(dirEntry.name.data, 0, buf, pathLen + 1, nameLen); + return Buffer.wrap(buf); + } + + protected static boolean endsWithSlash(Object path) { + Buffer b = (Buffer) path; + return b.data[b.data.length - 1] == POSIX_FILENAME_SEPARATOR; + } + } + + @ExportMessage + @SuppressWarnings("static-method") + public long dirEntryGetInode(Object dirEntry) { + DirEntry entry = (DirEntry) dirEntry; + return entry.ino; + } + + @ExportMessage + @SuppressWarnings("static-method") + public int dirEntryGetType(Object dirEntryObj) { + DirEntry dirEntry = (DirEntry) dirEntryObj; + return dirEntry.type; + } + + @ExportMessage + public void utimensat(int dirFd, Object pathname, long[] timespec, boolean followSymlinks) throws PosixException { + assert PosixConstants.HAVE_UTIMENSAT.value; + assert timespec == null || timespec.length == 4; + long pathnamePtr = NULLPTR; + long timespecPtr = NULLPTR; + try { + pathnamePtr = pathToNativeCString(pathname); + timespecPtr = timespec == null ? NULLPTR : NativeMemory.copyToNativeLongArray(timespec); + int ret = posixNativeFunctionInvoker.call_utimensat(dirFd, pathnamePtr, timespecPtr, followSymlinks ? 1 : 0); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(timespecPtr); + NativeMemory.free(pathnamePtr); + } + } + + @ExportMessage + public void futimens(int fd, long[] timespec) throws PosixException { + assert PosixConstants.HAVE_FUTIMENS.value; + assert timespec == null || timespec.length == 4; + long timespecPtr = timespec == null ? NULLPTR : NativeMemory.copyToNativeLongArray(timespec); + try { + int ret = posixNativeFunctionInvoker.call_futimens(fd, timespecPtr); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(timespecPtr); + } + } + + @ExportMessage + public void futimes(int fd, Timeval[] timeval) throws PosixException { + assert timeval == null || timeval.length == 2; + long timevalPtr = copyTimevalArrayToNativeOrNull(timeval); + try { + int ret = posixNativeFunctionInvoker.call_futimes(fd, timevalPtr); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(timevalPtr); + } + } + + @ExportMessage + public void lutimes(Object filename, Timeval[] timeval) throws PosixException { + assert timeval == null || timeval.length == 2; + long filenamePtr = NULLPTR; + long timevalPtr = NULLPTR; + try { + filenamePtr = pathToNativeCString(filename); + timevalPtr = copyTimevalArrayToNativeOrNull(timeval); + int ret = posixNativeFunctionInvoker.call_lutimes(filenamePtr, timevalPtr); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(timevalPtr); + NativeMemory.free(filenamePtr); + } + } + + @ExportMessage + public void utimes(Object filename, Timeval[] timeval) throws PosixException { + assert timeval == null || timeval.length == 2; + long filenamePtr = NULLPTR; + long timevalPtr = NULLPTR; + try { + filenamePtr = pathToNativeCString(filename); + timevalPtr = copyTimevalArrayToNativeOrNull(timeval); + int ret = posixNativeFunctionInvoker.call_utimes(filenamePtr, timevalPtr); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(timevalPtr); + NativeMemory.free(filenamePtr); + } + } + + @ExportMessage + public void renameat(int oldDirFd, Object oldPath, int newDirFd, Object newPath) throws PosixException { + long oldPathPtr = NULLPTR; + long newPathPtr = NULLPTR; + try { + oldPathPtr = pathToNativeCString(oldPath); + newPathPtr = pathToNativeCString(newPath); + int ret = posixNativeFunctionInvoker.call_renameat(oldDirFd, oldPathPtr, newDirFd, newPathPtr); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(newPathPtr); + NativeMemory.free(oldPathPtr); + } + } + + @ExportMessage + public boolean faccessat(int dirFd, Object path, int mode, boolean effectiveIds, boolean followSymlinks) { + long pathPtr = pathToNativeCString(path); + int ret; + try { + ret = posixNativeFunctionInvoker.call_faccessat(dirFd, pathPtr, mode, effectiveIds ? 1 : 0, followSymlinks ? 1 : 0); + } finally { + NativeMemory.free(pathPtr); + } + if (ret != 0 && LOGGER.isLoggable(Level.FINE)) { + log(Level.FINE, "faccessat return value: %d, errno: %d", ret, posixNativeFunctionInvoker.get_errno()); + } + return ret == 0; + } + + @ExportMessage + public void fchmodat(int dirFd, Object path, int mode, boolean followSymlinks) throws PosixException { + long pathPtr = pathToNativeCString(path); + try { + int ret = posixNativeFunctionInvoker.call_fchmodat(dirFd, pathPtr, mode, followSymlinks ? 1 : 0); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public void fchmod(int fd, int mode) throws PosixException { + int ret = posixNativeFunctionInvoker.call_fchmod(fd, mode); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public void fchownat(int dirFd, Object path, long owner, long group, boolean followSymlinks) throws PosixException { + long pathPtr = pathToNativeCString(path); + try { + int ret = posixNativeFunctionInvoker.call_fchownat(dirFd, pathPtr, owner, group, followSymlinks ? 1 : 0); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public void fchown(int fd, long owner, long group) throws PosixException { + int ret = posixNativeFunctionInvoker.call_fchown(fd, owner, group); + if (ret != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public Object readlinkat(int dirFd, Object path) throws PosixException { + Buffer buffer = Buffer.allocate(PATH_MAX.value); + long pathPtr = pathToNativeCString(path); + try { + long nativeBuffer = NativeMemory.mallocByteArrayOrNull(PATH_MAX.value); + try { + long n = posixNativeFunctionInvoker.call_readlinkat(dirFd, pathPtr, nativeBuffer, PATH_MAX.value); + if (n < 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuffer, 0, buffer.data, 0, (int) n); + return buffer.withLength(n); + } finally { + NativeMemory.free(nativeBuffer); + } + } finally { + NativeMemory.free(pathPtr); + } + } + + @ExportMessage + public void kill(long pid, int signal) throws PosixException { + int res = posixNativeFunctionInvoker.call_kill(pid, signal); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public void raise(int signal) throws PosixException { + int res = posixNativeFunctionInvoker.call_raise(signal); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public int alarm(int seconds) { + return posixNativeFunctionInvoker.call_alarm(seconds); + } + + @ExportMessage + public Timeval[] getitimer(int which) throws PosixException { + long nativeCurrentValue = NativeMemory.mallocLongArray(4); + try { + int res = posixNativeFunctionInvoker.call_getitimer(which, nativeCurrentValue); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + return unwrapTimeval(nativeCurrentValue); + } finally { + NativeMemory.free(nativeCurrentValue); + } + } + + @ExportMessage + public Timeval[] setitimer(int which, Timeval delay, Timeval interval) throws PosixException { + long nativeNewValue = NULLPTR; + long nativeOldValue = NULLPTR; + try { + nativeNewValue = wrapItimerval(delay, interval); + nativeOldValue = NativeMemory.mallocLongArray(4); + int res = posixNativeFunctionInvoker.call_setitimer(which, nativeNewValue, nativeOldValue); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + return unwrapTimeval(nativeOldValue); + } finally { + NativeMemory.free(nativeOldValue); + NativeMemory.free(nativeNewValue); + } + } + + @ExportMessage + public void signalSelf(int signal) throws PosixException { + if (!ImageInfo.inImageRuntimeCode()) { + throw new UnsupportedPosixFeatureException("self-signals are only supported in native standalone"); + } + int res = posixNativeFunctionInvoker.signal_self(signal); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public void killpg(long pgid, int signal) throws PosixException { + int res = posixNativeFunctionInvoker.call_killpg(pgid, signal); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public long[] waitpid(long pid, int options, + @Bind Node node) throws PosixException { + boolean hasNohang = (options & WNOHANG.getValueIfDefined()) != 0; + int subOptions = options | WNOHANG.getValueIfDefined(); + long nativeStatus = NativeMemory.callocIntArray(1); + try { + long res = posixNativeFunctionInvoker.call_waitpid(pid, nativeStatus, subOptions); + while (res == 0 && !hasNohang) { + TruffleSafepoint.setBlockedThreadInterruptible(node, (ignored) -> { + Thread.sleep(20); + }, null); + res = posixNativeFunctionInvoker.call_waitpid(pid, nativeStatus, subOptions); + } + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return new long[]{res, NativeMemory.readInt(nativeStatus)}; + } finally { + NativeMemory.free(nativeStatus); + } + } + + @ExportMessage + public boolean wcoredump(int status) { + return posixNativeFunctionInvoker.call_wcoredump(status) != 0; + } + + @ExportMessage + public boolean wifcontinued(int status) { + return posixNativeFunctionInvoker.call_wifcontinued(status) != 0; + } + + @ExportMessage + public boolean wifstopped(int status) { + return posixNativeFunctionInvoker.call_wifstopped(status) != 0; + } + + @ExportMessage + public boolean wifsignaled(int status) { + return posixNativeFunctionInvoker.call_wifsignaled(status) != 0; + } + + @ExportMessage + public boolean wifexited(int status) { + return posixNativeFunctionInvoker.call_wifexited(status) != 0; + } + + @ExportMessage + public int wexitstatus(int status) { + return posixNativeFunctionInvoker.call_wexitstatus(status); + } + + @ExportMessage + public int wtermsig(int status) { + return posixNativeFunctionInvoker.call_wtermsig(status); + } + + @ExportMessage + public int wstopsig(int status) { + return posixNativeFunctionInvoker.call_wstopsig(status); + } + + @ExportMessage + public long getuid() { + return posixNativeFunctionInvoker.call_getuid(); + } + + @ExportMessage + public long geteuid() { + return posixNativeFunctionInvoker.call_geteuid(); + } + + @ExportMessage + public long getgid() { + return posixNativeFunctionInvoker.call_getgid(); + } + + @ExportMessage + public long getegid() { + return posixNativeFunctionInvoker.call_getegid(); + } + + @ExportMessage + public long getppid() { + return posixNativeFunctionInvoker.call_getppid(); + } + + @ExportMessage + public void setpgid(long pid, long pgid) throws PosixException { + int res = posixNativeFunctionInvoker.call_setpgid(pid, pgid); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public long getpgid(long pid) throws PosixException { + long res = posixNativeFunctionInvoker.call_getpgid(pid); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return res; + } + + @ExportMessage + public long getpgrp() { + return posixNativeFunctionInvoker.call_getpgrp(); + } + + @ExportMessage + public long getsid(long pid) throws PosixException { + long res = posixNativeFunctionInvoker.call_getsid(pid); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return res; + } + + @ExportMessage + public long setsid() throws PosixException { + long res = posixNativeFunctionInvoker.call_setsid(); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return res; + } + + @ExportMessage + public long[] getgroups() throws PosixException { + // The first call gets us the number of groups, so we can allocate the output array + int res = posixNativeFunctionInvoker.call_getgroups(0, NULLPTR); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + if (res == 0) { + return EMPTY_LONG_ARRAY; + } + long[] groups = new long[res]; + long nativeGroups = NativeMemory.mallocLongArray(groups.length); + try { + res = posixNativeFunctionInvoker.call_getgroups(groups.length, nativeGroups); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readLongArrayElements(nativeGroups, 0, groups, 0, groups.length); + return groups; + } finally { + NativeMemory.free(nativeGroups); + } + } + + @ExportMessage + public RusageResult getrusage(int who) throws PosixException { + long nativeResult = NativeMemory.mallocLongArray(16); + try { + int res = posixNativeFunctionInvoker.call_getrusage(who, nativeResult); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return new RusageResult( + Double.longBitsToDouble(NativeMemory.readLongArrayElement(nativeResult, 0)), + Double.longBitsToDouble(NativeMemory.readLongArrayElement(nativeResult, 1)), + NativeMemory.readLongArrayElement(nativeResult, 2), + NativeMemory.readLongArrayElement(nativeResult, 3), + NativeMemory.readLongArrayElement(nativeResult, 4), + NativeMemory.readLongArrayElement(nativeResult, 5), + NativeMemory.readLongArrayElement(nativeResult, 6), + NativeMemory.readLongArrayElement(nativeResult, 7), + NativeMemory.readLongArrayElement(nativeResult, 8), + NativeMemory.readLongArrayElement(nativeResult, 9), + NativeMemory.readLongArrayElement(nativeResult, 10), + NativeMemory.readLongArrayElement(nativeResult, 11), + NativeMemory.readLongArrayElement(nativeResult, 12), + NativeMemory.readLongArrayElement(nativeResult, 13), + NativeMemory.readLongArrayElement(nativeResult, 14), + NativeMemory.readLongArrayElement(nativeResult, 15)); + } finally { + NativeMemory.free(nativeResult); + } + } + + @ExportMessage + public OpenPtyResult openpty() throws PosixException { + long nativeOutvars = NativeMemory.mallocIntArray(2); + try { + int res = posixNativeFunctionInvoker.call_openpty(nativeOutvars); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + return new OpenPtyResult( + NativeMemory.readIntArrayElement(nativeOutvars, 0), + NativeMemory.readIntArrayElement(nativeOutvars, 1)); + } finally { + NativeMemory.free(nativeOutvars); + } + } + + @ExportMessage + public TruffleString ctermid( + @Bind Node inliningTarget, + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) throws PosixException { + long nativeBuf = NativeMemory.mallocByteArray(L_ctermid.value); + try { + int res = posixNativeFunctionInvoker.call_ctermid(nativeBuf); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + // TODO PyUnicode_DecodeFSDefault + return zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, nativeBuf); + } finally { + NativeMemory.free(nativeBuf); + } + } + + @ExportMessage + public void setenv(Object name, Object value, boolean overwrite) throws PosixException { + long namePtr = NULLPTR; + long valuePtr = NULLPTR; + try { + namePtr = pathToNativeCString(name); + valuePtr = pathToNativeCString(value); + int res = posixNativeFunctionInvoker.call_setenv(namePtr, valuePtr, overwrite ? 1 : 0); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(valuePtr); + NativeMemory.free(namePtr); + } + } + + @ExportMessage + public void unsetenv(Object name) throws PosixException { + long namePtr = pathToNativeCString(name); + try { + int res = posixNativeFunctionInvoker.call_unsetenv(namePtr); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(namePtr); + } + } + + @ExportMessage + public int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd, + int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep) throws PosixException { + + // The following strings and string arrays need to be present in the native function: + // - char** of executable names ('\0'-terminated strings with an extra NULL at the end) + // - char** of arguments ('\0'-terminated strings with an extra NULL at the end) + // - an optional char** of env variables ('\0'-terminated strings with an extra NULL at the + // end), must distinguish between NULL (child inherits env) and an empty array (child gets + // empty env) + // - an optional char* cwd ('\0'-terminated string or NULL) + // We do this by concatenating all strings (including their terminating '\0' characters) + // into one large byte buffer (which becomes 'char *') and pass an additional array of + // offsets to mark where the individual strings begin. To prevent memory allocation + // in C (and related free()), we reuse this array of integer offsets as an array of + // C-strings (char **). For this reason, the array of offsets is allocated as long[]. + // In the offsets array we mark the places where NULL should be with a special value -1. + // All that is left is to let the native function know where in the offsets array the + // individual string arrays begin: + // - executable names are always at index 0 + // - argsPos is the index in the offsets array pointing to the first argument + // - envPos is either -1 or an index in the offsets array pointing to the first env string + // - cwdPos is either -1 or an index in the offsets array pointing to the cwd string + + // First we calculate the lengths of the offsets array and the string buffer (dataLen). + int offsetsLen; + int argsPos; + int envPos; + int cwdPos; + long dataLen; + + try { + offsetsLen = executables.length + 1; + dataLen = addLengthsOfCStrings(0, executables); + + argsPos = offsetsLen; + offsetsLen += args.length + 1; + dataLen = addLengthsOfCStrings(dataLen, args); + + if (env != null) { + envPos = offsetsLen; + offsetsLen += env.length + 1; + dataLen = addLengthsOfCStrings(dataLen, env); + } else { + envPos = -1; + } + + if (cwd != null) { + cwdPos = offsetsLen; + offsetsLen += 1; + // The +1 in the second argument can overflow only if the buffer contains 2^63-1 + // bytes, which is impossible since we are using Java arrays limited to 2^31-1. + dataLen = PythonUtils.addExact(dataLen, ((Buffer) cwd).length + 1L); + } else { + cwdPos = -1; + } + } catch (OverflowException e) { + throw newPosixException(OSErrorEnum.E2BIG.getNumber()); + } + + // This also guarantees that offsetsLen did not overflow: we add +1 to dataLen for each + // '\0', i.e. dataLen >= "number of strings" and offsetsLen < "number of strings" + 3 + // (3 accounts for the NULL terminating the executables, args and env arrays). + if (dataLen >= Integer.MAX_VALUE - 3) { + throw newPosixException(OSErrorEnum.E2BIG.getNumber()); + } + + byte[] data = new byte[(int) dataLen]; + long[] offsets = new long[offsetsLen]; + long offset = 0; + + offset = encodeCStringArray(data, offset, offsets, 0, executables); + offset = encodeCStringArray(data, offset, offsets, argsPos, args); + if (env != null) { + offset = encodeCStringArray(data, offset, offsets, envPos, env); + } + if (cwd != null) { + Buffer buf = (Buffer) cwd; + int strLen = (int) buf.length; + PythonUtils.arraycopy(buf.data, 0, data, (int) offset, strLen); + offsets[cwdPos] = offset; + offset += strLen + 1L; + } + assert offset == dataLen; + + long nativeData = NULLPTR; + long nativeOffsets = NULLPTR; + long nativeFdsToKeep = NULLPTR; + try { + nativeData = NativeMemory.copyToNativeByteArray(data); + nativeOffsets = NativeMemory.copyToNativeLongArray(offsets); + nativeFdsToKeep = NativeMemory.copyToNativeIntArrayOrNull(fdsToKeep); + int res = posixNativeFunctionInvoker.fork_exec(nativeData, nativeOffsets, offsets.length, argsPos, envPos, cwdPos, + stdinReadFd, stdinWriteFd, + stdoutReadFd, stdoutWriteFd, + stderrReadFd, stderrWriteFd, + errPipeReadFd, errPipeWriteFd, + closeFds ? 1 : 0, + restoreSignals ? 1 : 0, + callSetsid ? 1 : 0, + nativeFdsToKeep, fdsToKeep.length); + if (res == -1) { + throw getErrnoAndThrowPosixException(); + } + return res; + } finally { + NativeMemory.free(nativeFdsToKeep); + NativeMemory.free(nativeOffsets); + NativeMemory.free(nativeData); + } + } + + @ExportMessage + public void execv(Object pathname, Object[] args) throws PosixException { + + // The following strings and string arrays need to be present in the native function: + // - char* - the pathname ('\0'-terminated string) + // - char** of arguments ('\0'-terminated strings with an extra NULL at the end) + // We do this by concatenating all strings (including their terminating '\0' characters) + // into one large byte buffer (which becomes 'char *') and pass an additional array of + // offsets to mark where the individual strings begin. To prevent memory allocation + // in C (and related free()), we reuse this array of integer offsets as an array of + // C-strings (char **). For this reason, the array of offsets is allocated as long[]. + // In the offsets array we mark the places where NULL should be with a special value -1. + // - the pathname is always at index 0 + // - the arguments start at index 1 + + // First we calculate the lengths of the offsets array and the string buffer (dataLen). + int offsetsLen = 1 + args.length + 1; + long pathnameLen = ((Buffer) pathname).length; + long dataLen; + + try { + // The +1 can overflow only if the buffer contains 2^63-1 bytes, which is impossible + // since we are using Java arrays limited to 2^31-1. + dataLen = addLengthsOfCStrings(pathnameLen + 1L, args); + } catch (OverflowException e) { + throw newPosixException(OSErrorEnum.E2BIG.getNumber()); + } + + // This also guarantees that offsetsLen did not overflow: we add +1 to dataLen for each + // '\0', i.e. dataLen >= "number of strings" and offsetsLen == "number of strings" + 1 + // (1 accounts for the NULL terminating the args array). + // Also, dataLen > pathnameLen, so this check makes sure that the cast of pathnameLen to int + // below is safe. + if (dataLen >= Integer.MAX_VALUE - 1) { + throw newPosixException(OSErrorEnum.E2BIG.getNumber()); + } + + byte[] data = new byte[(int) dataLen]; + long[] offsets = new long[offsetsLen]; + + PythonUtils.arraycopy(((Buffer) pathname).data, 0, data, 0, (int) pathnameLen); + long offset = encodeCStringArray(data, pathnameLen + 1L, offsets, 1, args); + assert offset == dataLen; + + long nativeData = NULLPTR; + long nativeOffsets = NULLPTR; + try { + nativeData = NativeMemory.copyToNativeByteArray(data); + nativeOffsets = NativeMemory.copyToNativeLongArray(offsets); + posixNativeFunctionInvoker.call_execv(nativeData, nativeOffsets, offsets.length); + throw getErrnoAndThrowPosixException(); + } finally { + NativeMemory.free(nativeOffsets); + NativeMemory.free(nativeData); + } + } + + @ExportMessage + public int system(Object command) { + long commandPtr = pathToNativeCString(command); + try { + return posixNativeFunctionInvoker.call_system(commandPtr); + } finally { + NativeMemory.free(commandPtr); + } + } + + private static long addLengthsOfCStrings(long prevLen, Object[] src) throws OverflowException { + long len = prevLen; + for (Object o : src) { + len = PythonUtils.addExact(len, ((Buffer) o).length); + } + return PythonUtils.addExact(len, src.length); // add space for terminating '\0' + } + + /** + * Copies null-terminated strings to a buffer {@code data} starting at position {@code offset}, + * and stores the offset of each string to the {@code offsets} array starting at index + * {@code startPos}. + */ + private static long encodeCStringArray(byte[] data, long startOffset, long[] offsets, int startPos, Object[] src) { + // The code that calculates dataLen already checked that there is no overflow and that all + // offsets fit into an int. + long offset = startOffset; + for (int i = 0; i < src.length; ++i) { + Buffer buf = (Buffer) src[i]; + int strLen = (int) buf.length; + PythonUtils.arraycopy(buf.data, 0, data, (int) offset, strLen); + offsets[startPos + i] = offset; + offset += strLen + 1; // +1 for the terminating \0 character + } + offsets[startPos + src.length] = -1; // this will become NULL in C (the char* array + // needs to be terminated by a NULL) + return offset; + } + + private static final class MMapHandle { + private final long pointer; + private final long length; + + public MMapHandle(long pointer, long length) { + this.pointer = pointer; + this.length = length; + } + } + + @ExportMessage + public Object mmap(long length, int prot, int flags, int fd, long offset) throws PosixException { + long address = posixNativeFunctionInvoker.call_mmap(length, prot, flags, fd, offset); + if (address == 0) { + throw getErrnoAndThrowPosixException(); + } + return new MMapHandle(address, length); + } + + @ExportMessage + @SuppressWarnings("static-method") + public byte mmapReadByte(Object mmap, long index) { + MMapHandle handle = (MMapHandle) mmap; + if (index < 0 || index >= handle.length) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IndexOutOfBoundsException(); + } + return UNSAFE.getByte(handle.pointer + index); + } + + @ExportMessage + @SuppressWarnings("static-method") + public void mmapWriteByte(Object mmap, long index, byte value) { + MMapHandle handle = (MMapHandle) mmap; + checkIndexAndLen(handle, index, 1); + UNSAFE.putByte(handle.pointer + index, value); + } + + @ExportMessage + @SuppressWarnings("static-method") + public int mmapReadBytes(Object mmap, long index, byte[] bytes, int length) { + MMapHandle handle = (MMapHandle) mmap; + checkIndexAndLen(handle, index, length); + UNSAFE.copyMemory(null, handle.pointer + index, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, length); + return length; + } + + @ExportMessage + @SuppressWarnings("static-method") + public void mmapWriteBytes(Object mmap, long index, byte[] bytes, int length) { + MMapHandle handle = (MMapHandle) mmap; + checkIndexAndLen(handle, index, length); + UNSAFE.copyMemory(bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, handle.pointer + index, length); + } + + @ExportMessage + public void mmapFlush(Object mmap, long offset, long length) { + MMapHandle handle = (MMapHandle) mmap; + checkIndexAndLen(handle, offset, length); + posixNativeFunctionInvoker.call_msync(handle.pointer, offset, length); + } + + @ExportMessage + public void mmapUnmap(Object mmap, long length) throws PosixException { + MMapHandle handle = (MMapHandle) mmap; + if (length != handle.length) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IllegalArgumentException(); + } + int result = posixNativeFunctionInvoker.call_munmap(handle.pointer, length); + if (result != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + @SuppressWarnings("static-method") + public long mmapGetPointer(Object mmap) { + MMapHandle handle = (MMapHandle) mmap; + return handle.pointer; + } + + private static void checkIndexAndLen(MMapHandle handle, long index, long length) { + if (length < 0) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IllegalArgumentException(); + } + if (index < 0 || index + length > handle.length) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IndexOutOfBoundsException(); + } + } + + @ExportMessage + public int socket(int domain, int type, int protocol) throws PosixException { + int result = posixNativeFunctionInvoker.call_socket(domain, type, protocol); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + return result; + } + + @ExportMessage + public AcceptResult accept(int sockfd) throws PosixException { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + long nativeAddr = NULLPTR; + long nativeAddrLen = NULLPTR; + try { + nativeAddr = NativeMemory.mallocByteArray(addr.data.length); + nativeAddrLen = NativeMemory.mallocIntArray(1); + int result = posixNativeFunctionInvoker.call_accept(sockfd, nativeAddr, nativeAddrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + readNativeSockAddr(nativeAddr, nativeAddrLen, addr); + return new AcceptResult(result, addr); + } finally { + NativeMemory.free(nativeAddrLen); + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public void bind(int sockfd, UniversalSockAddr usa) throws PosixException { + UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; + int addrLen = addr.getLen(); + long nativeAddr = NULLPTR; + try { + nativeAddr = NativeMemory.copyToNativeByteArray(addr.data, 0, addrLen); + int result = posixNativeFunctionInvoker.call_bind(sockfd, nativeAddr, addrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public void connect(int sockfd, UniversalSockAddr usa) throws PosixException { + UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; + int addrLen = addr.getLen(); + long nativeAddr = NULLPTR; + try { + nativeAddr = NativeMemory.copyToNativeByteArray(addr.data, 0, addrLen); + int result = posixNativeFunctionInvoker.call_connect(sockfd, nativeAddr, addrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public void listen(int sockfd, int backlog) throws PosixException { + int result = posixNativeFunctionInvoker.call_listen(sockfd, backlog); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public UniversalSockAddr getpeername(int sockfd) throws PosixException { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + long nativeAddr = NULLPTR; + long nativeAddrLen = NULLPTR; + try { + nativeAddr = NativeMemory.mallocByteArray(addr.data.length); + nativeAddrLen = NativeMemory.mallocIntArray(1); + int result = posixNativeFunctionInvoker.call_getpeername(sockfd, nativeAddr, nativeAddrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + readNativeSockAddr(nativeAddr, nativeAddrLen, addr); + return addr; + } finally { + NativeMemory.free(nativeAddrLen); + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public UniversalSockAddr getsockname(int sockfd) throws PosixException { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + long nativeAddr = NULLPTR; + long nativeAddrLen = NULLPTR; + try { + nativeAddr = NativeMemory.mallocByteArray(addr.data.length); + nativeAddrLen = NativeMemory.mallocIntArray(1); + int result = posixNativeFunctionInvoker.call_getsockname(sockfd, nativeAddr, nativeAddrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + readNativeSockAddr(nativeAddr, nativeAddrLen, addr); + return addr; + } finally { + NativeMemory.free(nativeAddrLen); + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public int send(int sockfd, byte[] buf, int offset, int len, int flags) throws PosixException { + checkBounds(buf, offset, len); + long nativeBuffer = NativeMemory.mallocByteArrayOrNull(len); + try { + NativeMemory.writeByteArrayElements(nativeBuffer, 0, buf, offset, len); + posixNativeFunctionInvoker.set_errno(0); + int result = posixNativeFunctionInvoker.call_send(sockfd, nativeBuffer, len, flags); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + return result; + } finally { + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public int sendto(int sockfd, byte[] buf, int offset, int len, int flags, UniversalSockAddr usa) throws PosixException { + checkBounds(buf, offset, len); + UniversalSockAddrImpl destAddr = (UniversalSockAddrImpl) usa; + int destAddrLen = destAddr.getLen(); + long nativeBuffer = NULLPTR; + long nativeDestAddr = NULLPTR; + try { + nativeBuffer = NativeMemory.mallocByteArrayOrNull(len); + NativeMemory.writeByteArrayElements(nativeBuffer, 0, buf, offset, len); + nativeDestAddr = NativeMemory.mallocByteArrayOrNull(destAddrLen); + NativeMemory.writeByteArrayElements(nativeDestAddr, 0, destAddr.data, 0, destAddrLen); + posixNativeFunctionInvoker.set_errno(0); + int result = posixNativeFunctionInvoker.call_sendto(sockfd, nativeBuffer, 0, len, flags, nativeDestAddr, destAddrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + return result; + } finally { + NativeMemory.free(nativeDestAddr); + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public int recv(int sockfd, byte[] buf, int offset, int len, int flags) throws PosixException { + checkBounds(buf, offset, len); + long nativeBuffer = NativeMemory.mallocByteArrayOrNull(len); + try { + posixNativeFunctionInvoker.set_errno(0); + int result = posixNativeFunctionInvoker.call_recv(sockfd, nativeBuffer, len, flags); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuffer, 0, buf, offset, result); + return result; + } finally { + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public RecvfromResult recvfrom(int sockfd, byte[] buf, int offset, int len, int flags) throws PosixException { + checkBounds(buf, offset, len); + UniversalSockAddrImpl srcAddr = new UniversalSockAddrImpl(this); + long nativeBuffer = NULLPTR; + long nativeSrcAddr = NULLPTR; + long nativeAddrLen = NULLPTR; + try { + nativeBuffer = NativeMemory.mallocByteArrayOrNull(len); + nativeSrcAddr = NativeMemory.mallocByteArray(srcAddr.data.length); + nativeAddrLen = NativeMemory.mallocIntArray(1); + posixNativeFunctionInvoker.set_errno(0); + int result = posixNativeFunctionInvoker.call_recvfrom(sockfd, nativeBuffer, 0, len, flags, nativeSrcAddr, nativeAddrLen); + if (result == -1) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuffer, 0, buf, offset, result); + readNativeSockAddr(nativeSrcAddr, nativeAddrLen, srcAddr); + return new RecvfromResult(result, srcAddr); + } finally { + NativeMemory.free(nativeAddrLen); + NativeMemory.free(nativeSrcAddr); + NativeMemory.free(nativeBuffer); + } + } + + @ExportMessage + public void shutdown(int sockfd, int how) throws PosixException { + int res = posixNativeFunctionInvoker.call_shutdown(sockfd, how); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + public int getsockopt(int sockfd, int level, int optname, byte[] optval, int optlen) throws PosixException { + assert optlen >= 0 && optval.length >= optlen; + long nativeOptval = NULLPTR; + long nativeBufLen = NULLPTR; + try { + nativeOptval = NativeMemory.mallocByteArray(Math.max(optlen, 1)); + nativeBufLen = NativeMemory.mallocIntArray(1); + NativeMemory.writeInt(nativeBufLen, optlen); + int res = posixNativeFunctionInvoker.call_getsockopt(sockfd, level, optname, nativeOptval, nativeBufLen); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + int actualLen = NativeMemory.readInt(nativeBufLen); + if (actualLen < 0 || actualLen > optval.length) { + throw CompilerDirectives.shouldNotReachHere("Unexpected socket option length in getsockopt"); + } + if (actualLen > 0) { + NativeMemory.readByteArrayElements(nativeOptval, 0, optval, 0, actualLen); + } + return actualLen; + } finally { + NativeMemory.free(nativeBufLen); + NativeMemory.free(nativeOptval); + } + } + + @ExportMessage + public void setsockopt(int sockfd, int level, int optname, byte[] optval, int optlen) throws PosixException { + assert optlen >= 0 && optval.length >= optlen; + long nativeOptval = NULLPTR; + try { + nativeOptval = NativeMemory.copyToNativeByteArrayOrNull(optval, 0, optlen); + int res = posixNativeFunctionInvoker.call_setsockopt(sockfd, level, optname, nativeOptval, optlen); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(nativeOptval); + } + } + + @ExportMessage + public int inet_addr(Object src) { + long srcPtr = pathToNativeCString(src); + try { + return posixNativeFunctionInvoker.call_inet_addr(srcPtr); + } finally { + NativeMemory.free(srcPtr); + } + } + + @ExportMessage + public int inet_aton(Object src) throws InvalidAddressException { + long srcPtr = pathToNativeCString(src); + try { + long r = posixNativeFunctionInvoker.call_inet_aton(srcPtr); + if (r < 0) { + throw new InvalidAddressException(); + } + return (int) r; + } finally { + NativeMemory.free(srcPtr); + } + } + + @ExportMessage + public Object inet_ntoa(int src) { + Buffer buf = Buffer.allocate(INET_ADDRSTRLEN.value); + long nativeBuf = NativeMemory.mallocByteArray(INET_ADDRSTRLEN.value); + try { + int len = posixNativeFunctionInvoker.call_inet_ntoa(src, nativeBuf); + if (len > 0) { + NativeMemory.readByteArrayElements(nativeBuf, 0, buf.data, 0, len); + } + return buf.withLength(len); + } finally { + NativeMemory.free(nativeBuf); + } + } + + @ExportMessage + public byte[] inet_pton(int family, Object src) throws PosixException, InvalidAddressException { + byte[] buf = new byte[family == AF_INET.value ? 4 : 16]; + long srcPtr = NULLPTR; + long nativeBuf = NULLPTR; + try { + srcPtr = pathToNativeCString(src); + nativeBuf = NativeMemory.mallocByteArray(buf.length); + int res = posixNativeFunctionInvoker.call_inet_pton(family, srcPtr, nativeBuf); + // Rather unusually, the return value of 0 does not indicate success but is used by + // inet_pton to report invalid format of the address (without setting errno). + // Success is reported by returning 1. + if (res == 1) { + NativeMemory.readByteArrayElements(nativeBuf, 0, buf, 0, buf.length); + return buf; + } + if (res == 0) { + throw new InvalidAddressException(); + } + throw getErrnoAndThrowPosixException(); + } finally { + NativeMemory.free(nativeBuf); + NativeMemory.free(srcPtr); + } + } + + @ExportMessage + public Object inet_ntop(int family, byte[] src) throws PosixException { + if ((family == AF_INET.value && src.length < 4) || (family == AF_INET6.value && src.length < 16)) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IllegalArgumentException("Invalid length of IPv4/6 address"); + } + Buffer buf = Buffer.allocate(INET6_ADDRSTRLEN.value); + long nativeSrc = NULLPTR; + long nativeBuf = NULLPTR; + try { + nativeSrc = NativeMemory.copyToNativeByteArray(src); + nativeBuf = NativeMemory.mallocByteArray(INET6_ADDRSTRLEN.value); + int res = posixNativeFunctionInvoker.call_inet_ntop(family, nativeSrc, nativeBuf, INET6_ADDRSTRLEN.value); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuf, 0, buf.data, 0, buf.data.length); + return buf.withLength(findZero(buf.data)); + } finally { + NativeMemory.free(nativeBuf); + NativeMemory.free(nativeSrc); + } + } + + @ExportMessage + public Object gethostname() throws PosixException { + int maxLen = (HOST_NAME_MAX.defined ? HOST_NAME_MAX.getValueIfDefined() : _POSIX_HOST_NAME_MAX.value) + 1; + Buffer buf = Buffer.allocate(maxLen); + long nativeBuf = NativeMemory.mallocByteArray(maxLen); + try { + int res = posixNativeFunctionInvoker.call_gethostname(nativeBuf, maxLen); + if (res != 0) { + throw getErrnoAndThrowPosixException(); + } + NativeMemory.readByteArrayElements(nativeBuf, 0, buf.data, 0, buf.data.length); + return buf.withLength(findZero(buf.data)); + } finally { + NativeMemory.free(nativeBuf); + } + } + + @ExportMessage + public Object[] getnameinfo(UniversalSockAddr usa, int flags, + @Bind Node inliningTarget, + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) throws GetAddrInfoException { + Buffer host = Buffer.allocate(NI_MAXHOST.value); + Buffer serv = Buffer.allocate(NI_MAXSERV.value); + UniversalSockAddrImpl addr = (UniversalSockAddrImpl) usa; + long nativeAddr = NULLPTR; + long nativeHost = NULLPTR; + long nativeServ = NULLPTR; + try { + nativeAddr = NativeMemory.copyToNativeByteArray(addr.data, 0, addr.getLen()); + nativeHost = NativeMemory.mallocByteArray(NI_MAXHOST.value); + nativeServ = NativeMemory.mallocByteArray(NI_MAXSERV.value); + int res = posixNativeFunctionInvoker.call_getnameinfo(nativeAddr, addr.getLen(), nativeHost, NI_MAXHOST.value, nativeServ, NI_MAXSERV.value, flags); + if (res != 0) { + throw new GetAddrInfoException(res, gai_strerror(inliningTarget, res, zeroTerminatedUtf8ToTruffleStringNode)); + } + NativeMemory.readByteArrayElements(nativeHost, 0, host.data, 0, host.data.length); + NativeMemory.readByteArrayElements(nativeServ, 0, serv.data, 0, serv.data.length); + return new Object[]{ + host.withLength(findZero(host.data)), + serv.withLength(findZero(serv.data)), + }; + } finally { + NativeMemory.free(nativeServ); + NativeMemory.free(nativeHost); + NativeMemory.free(nativeAddr); + } + } + + @ExportMessage + public AddrInfoCursor getaddrinfo(Object node, Object service, int family, int sockType, int protocol, int flags, + @Bind Node inliningTarget, + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) throws GetAddrInfoException { + long nodePtr = NULLPTR; + long servicePtr = NULLPTR; + long nativePtr = NULLPTR; + try { + nodePtr = pathToNativeCStringOrNull(node); + servicePtr = pathToNativeCStringOrNull(service); + nativePtr = NativeMemory.mallocLongArray(1); + int res = posixNativeFunctionInvoker.call_getaddrinfo(nodePtr, servicePtr, family, sockType, protocol, flags, nativePtr); + if (res != 0) { + throw new GetAddrInfoException(res, gai_strerror(inliningTarget, res, zeroTerminatedUtf8ToTruffleStringNode)); + } + long head = NativeMemory.readLong(nativePtr); + assert head != 0; // getaddrinfo should return at least one result + return new AddrInfoCursorImpl(this, head); + } finally { + NativeMemory.free(nativePtr); + NativeMemory.free(servicePtr); + NativeMemory.free(nodePtr); + } + } + + @ExportMessage + public TruffleString crypt(TruffleString word, TruffleString salt, + @Bind Node raisingNode, + @Shared("toUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingToUtf8Node, + @Shared("tsCopyBytes") @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode, + @Shared("cString") @Cached NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) throws PosixException { + /* + * From the manpage: Upon successful completion, crypt returns a pointer to a string which + * encodes both the hashed passphrase, and the settings that were used to encode it. See + * crypt(5) for more detail on the format of hashed passphrases. crypt places its result in + * a static storage area, which will be overwritten by subsequent calls to crypt. It is not + * safe to call crypt from multiple threads simultaneously. Upon error, it may return a NULL + * pointer or a pointer to an invalid hash, depending on the implementation. + */ + long wordPtr = NULLPTR; + long saltPtr = NULLPTR; + try { + wordPtr = stringToNativeUTF8CString(word, switchEncodingToUtf8Node, copyToByteArrayNode); + saltPtr = stringToNativeUTF8CString(salt, switchEncodingToUtf8Node, copyToByteArrayNode); + // Note GIL is not enough as crypt is using global memory, we need a really global lock + synchronized (CRYPT_LOCK) { + long resultPtr; + try { + resultPtr = cryptNativeFunctionInvoker.crypt(wordPtr, saltPtr); + } catch (UnsupportedOperationException e) { + // Thrown by the generated invoker when CryptNativeFunction.loadNativeLibrary + // fails during its lazy library initialization path. + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw PRaiseNode.raiseStatic(raisingNode, PythonBuiltinClassType.SystemError, ErrorMessages.UNABLE_TO_LOAD_LIBCRYPT); + } + // CPython doesn't handle the case of "invalid hash" return specially and neither do + // we + if (resultPtr == 0) { + throw getErrnoAndThrowPosixException(); + } + return zeroTerminatedUtf8ToTruffleStringNode.execute(raisingNode, resultPtr); + } + } finally { + if (wordPtr != NULLPTR) { + NativeMemory.free(wordPtr); + } + if (saltPtr != NULLPTR) { + NativeMemory.free(saltPtr); + } + } + } + + private TruffleString gai_strerror(Node inliningTarget, int errorCode, NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode zeroTerminatedUtf8ToTruffleStringNode) { + long nativeBuf = NativeMemory.mallocByteArray(1024); + try { + posixNativeFunctionInvoker.call_gai_strerror(errorCode, nativeBuf, 1024); + // TODO PyUnicode_DecodeLocale + return zeroTerminatedUtf8ToTruffleStringNode.execute(inliningTarget, nativeBuf); + } finally { + NativeMemory.free(nativeBuf); + } + } + + /** + * Provides access to {@code struct addrinfo}. + * + * The layout of native {@code struct addrinfo} is as follows: + * + *
+     * {@code
+     *     struct addrinfo {
+     *         int              ai_flags;           // intData[0]
+     *         int              ai_family;          // intData[1]
+     *         int              ai_socktype;        // intData[2]
+     *         int              ai_protocol;        // intData[3]
+     *         socklen_t        ai_addrlen;         // intData[4]
+     *         struct sockaddr *ai_addr;            // data copied into socketAddress[]
+     *         char            *ai_canonname;       // longData[0]
+     *         struct addrinfo *ai_next;            // longData[1]
+     *     };
+     * }
+     * 
+ * + * To avoid multiple native calls, we transfer the data in batch using arrays of {@code int}s and + * {@code long}s - int values are stored in {@code intData}, the {@code ai_canonname} and + * {@code ai_next} pointers are stored in {@code longData} and the socket address pointed to by + * {@code ai_addr} is copied into Java byte array {@code socketAddress}. We also cache two + * additional integers: + *
    + *
  • {@code intData[5]} contains {@code ai_addr->sa_family},
  • + *
  • {@code intData[6]} contains the length of {@code ai_canonname} if it is not {@code null} + *
  • + *
+ * + * It is not clear whether it is guaranteed that {@code ai_family} and + * {@code ai_addr->sa_family} are always the same. We provide both and use the later when + * decoding the socket address. + */ + private static class AddrInfo { + private final int[] intData = new int[7]; + private final long[] longData = new long[2]; + private byte[] socketAddress; + + private void update(long ptr, NativePosixSupport nativePosixSupport) { + socketAddress = new byte[(int) nativePosixSupport.getConstant(SIZEOF_STRUCT_SOCKADDR_STORAGE)]; + long nativeIntData = NULLPTR; + long nativeLongData = NULLPTR; + long nativeSocketAddress = NULLPTR; + try { + nativeIntData = NativeMemory.mallocIntArray(intData.length); + nativeLongData = NativeMemory.mallocLongArray(longData.length); + nativeSocketAddress = NativeMemory.mallocByteArray(socketAddress.length); + int res = nativePosixSupport.posixNativeFunctionInvoker.get_addrinfo_members(ptr, nativeIntData, nativeLongData, nativeSocketAddress); + if (res != 0) { + throw shouldNotReachHere("the length of ai_canonname does not fit into an int"); + } + NativeMemory.readIntArrayElements(nativeIntData, 0, intData, 0, intData.length); + NativeMemory.readLongArrayElements(nativeLongData, 0, longData, 0, longData.length); + NativeMemory.readByteArrayElements(nativeSocketAddress, 0, socketAddress, 0, socketAddress.length); + } finally { + NativeMemory.free(nativeSocketAddress); + NativeMemory.free(nativeLongData); + NativeMemory.free(nativeIntData); + } + } + + int getFlags() { + return intData[0]; + } + + int getFamily() { + return intData[1]; + } + + int getSockType() { + return intData[2]; + } + + int getProtocol() { + return intData[3]; + } + + int getAddrLen() { + return intData[4]; + } + + int getAddrFamily() { + return intData[5]; + } + + int getCanonNameLen() { + assert getCanonNamePtr() != 0; + return intData[6]; + } + + long getCanonNamePtr() { + return longData[0]; + } + + long getNextPtr() { + return longData[1]; + } + } + + @ExportLibrary(AddrInfoCursorLibrary.class) + protected static class AddrInfoCursorImpl implements AddrInfoCursor { + + private final NativePosixSupport nativePosixSupport; + private long head; + private AddrInfo info; + + AddrInfoCursorImpl(NativePosixSupport nativePosixSupport, long head) { + this.nativePosixSupport = nativePosixSupport; + this.head = head; + info = new AddrInfo(); + info.update(head, nativePosixSupport); + } + + @ExportMessage + void release() { + checkReleased(); + nativePosixSupport.posixNativeFunctionInvoker.call_freeaddrinfo(head); + head = 0; + } + + @ExportMessage + boolean next() { + checkReleased(); + long nextPtr = info.getNextPtr(); + if (nextPtr == 0) { + return false; + } + info.update(nextPtr, nativePosixSupport); + return true; + } + + @ExportMessage + int getFlags() { + checkReleased(); + return info.getFlags(); + } + + @ExportMessage + int getFamily() { + checkReleased(); + return info.getFamily(); + } + + @ExportMessage + int getSockType() { + checkReleased(); + return info.getSockType(); + } + + @ExportMessage + int getProtocol() { + checkReleased(); + return info.getProtocol(); + } + + @ExportMessage + Object getCanonName() { + checkReleased(); + long namePtr = info.getCanonNamePtr(); + if (namePtr == 0) { + return null; + } + int nameLen = info.getCanonNameLen(); + byte[] buf = new byte[nameLen]; + UNSAFE.copyMemory(null, namePtr, buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, nameLen); + return Buffer.wrap(buf); + } + + @ExportMessage + UniversalSockAddr getSockAddr() { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(nativePosixSupport); + PythonUtils.arraycopy(info.socketAddress, 0, addr.data, 0, info.getAddrLen()); + addr.setFamily(info.getAddrFamily()); + addr.setLen(info.getAddrLen()); + return addr; + } + + private void checkReleased() { + if (head == 0) { + throw shouldNotReachHere("AddrInfoCursor has already been released"); + } + } + } + + @ExportMessage + UniversalSockAddr createUniversalSockAddrInet4(Inet4SockAddr src) { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + addr.setFamily(AF_INET.value); + ARRAY_ACCESSOR_BE.putShort(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT), (short) src.getPort()); + ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR) + getConstant(OFFSETOF_STRUCT_IN_ADDR_S_ADDR), src.getAddress()); + addr.setLen((int) getConstant(SIZEOF_STRUCT_SOCKADDR_IN)); + return addr; + } + + @ExportMessage + UniversalSockAddr createUniversalSockAddrInet6(Inet6SockAddr src) { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + addr.setFamily(AF_INET6.value); + ARRAY_ACCESSOR_BE.putShort(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT), (short) src.getPort()); + int addrOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR) + (int) getConstant(OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR); + PythonUtils.arraycopy(src.getAddress(), 0, addr.data, addrOffset, 16); + ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO), src.getFlowInfo()); + ARRAY_ACCESSOR_BE.putInt(addr.data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID), src.getScopeId()); + addr.setLen((int) getConstant(SIZEOF_STRUCT_SOCKADDR_IN6)); + return addr; + } + + @ExportMessage + UniversalSockAddr createUniversalSockAddrUnix(UnixSockAddr src) throws InvalidUnixSocketPathException { + UniversalSockAddrImpl addr = new UniversalSockAddrImpl(this); + addr.setFamily(AF_UNIX.value); + byte[] path = src.getPath(); + if (path.length > getConstant(SIZEOF_STRUCT_SOCKADDR_UN_SUN_PATH)) { + throw InvalidUnixSocketPathException.INSTANCE; + } + int len = path.length + (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH); + PythonUtils.arraycopy(path, 0, addr.data, (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH), path.length); + addr.setLen(len); + return addr; + } + + @ExportLibrary(UniversalSockAddrLibrary.class) + protected static class UniversalSockAddrImpl implements UniversalSockAddr { + + private final NativePosixSupport nativePosixSupport; + private final byte[] data; + private int len = 0; + + UniversalSockAddrImpl(NativePosixSupport nativePosixSupport) { + this.nativePosixSupport = nativePosixSupport; + this.data = new byte[(int) getConstant(SIZEOF_STRUCT_SOCKADDR_STORAGE)]; + } + + @ExportMessage + int getFamily() { + int offset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY); + int size = (int) getConstant(SIZEOF_STRUCT_SOCKADDR_SA_FAMILY); + if (getLen() >= offset + size) { + if (size == 1) { + return Byte.toUnsignedInt(data[offset]); + } else if (size == 2) { + return Short.toUnsignedInt(ARRAY_ACCESSOR.getShort(data, offset)); + } else if (size == 4) { + return ARRAY_ACCESSOR.getInt(data, offset); + } else { + throw CompilerDirectives.shouldNotReachHere("Unexpected sizeof(sa_family_t)"); + } + } else { + return AF_UNSPEC.value; + } + } + + void setFamily(int family) { + int offset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_SA_FAMILY); + int size = (int) getConstant(SIZEOF_STRUCT_SOCKADDR_SA_FAMILY); + if (size == 1) { + data[offset] = (byte) family; + } else if (size == 2) { + ARRAY_ACCESSOR.putShort(data, offset, (short) family); + } else if (size == 4) { + ARRAY_ACCESSOR.putInt(data, offset, family); + } else { + throw CompilerDirectives.shouldNotReachHere("Unexpected sizeof(sa_family_t)"); + } + } + + @ExportMessage + Inet4SockAddr asInet4SockAddr() { + if (getFamily() != AF_INET.value) { + throw CompilerDirectives.shouldNotReachHere("Only AF_INET socket address can be converted to Inet4SockAddr"); + } + if (getLen() != getConstant(SIZEOF_STRUCT_SOCKADDR_IN)) { + throw CompilerDirectives.shouldNotReachHere("Wrong size of socket addr struct in asInet4SockAddr"); + } + int port = Short.toUnsignedInt(ARRAY_ACCESSOR_BE.getShort(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_PORT))); + int address = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN_SIN_ADDR) + getConstant(OFFSETOF_STRUCT_IN_ADDR_S_ADDR)); + return new Inet4SockAddr(port, address); + } + + @ExportMessage + Inet6SockAddr asInet6SockAddr() { + if (getFamily() != AF_INET6.value) { + throw CompilerDirectives.shouldNotReachHere("Only AF_INET6 socket address can be converted to Inet6SockAddr"); + } + if (getLen() != getConstant(SIZEOF_STRUCT_SOCKADDR_IN6)) { + throw CompilerDirectives.shouldNotReachHere("Wrong size of socket addr struct in asInet6SockAddr"); + } + int port = Short.toUnsignedInt(ARRAY_ACCESSOR_BE.getShort(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_PORT))); + int addrOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_ADDR) + (int) getConstant(OFFSETOF_STRUCT_IN6_ADDR_S6_ADDR); + byte[] address = PythonUtils.arrayCopyOfRange(data, addrOffset, addrOffset + 16); + int flowInfo = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_FLOWINFO)); + int scopeId = ARRAY_ACCESSOR_BE.getInt(data, getConstant(OFFSETOF_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID)); + return new Inet6SockAddr(port, address, flowInfo, scopeId); + } + + @ExportMessage + UnixSockAddr asUnixSockAddr() { + if (getFamily() != AF_UNIX.value) { + throw CompilerDirectives.shouldNotReachHere("Only AF_UNIX socket address can be converted to UnixSockAddr"); + } + int pathOffset = (int) getConstant(OFFSETOF_STRUCT_SOCKADDR_UN_SUN_PATH); + byte[] pathBuf; + int linuxAddrLen = getLen() - pathOffset; + if (linuxAddrLen > 0 && data[pathOffset] == '\0') { + // Abstract Linux address + pathBuf = PythonUtils.arrayCopyOfRange(data, pathOffset, pathOffset + linuxAddrLen); + } else { + // Regular NULL-terminated string + int pathLen = ArrayUtils.indexOf(data, pathOffset, data.length, (byte) 0) - pathOffset; + assert pathLen >= 0; + pathBuf = PythonUtils.arrayCopyOfRange(data, pathOffset, pathOffset + pathLen); + } + return new UnixSockAddr(pathBuf); + } + + long getConstant(NativePosixConstants constant) { + return nativePosixSupport.getConstant(constant); + } + + int getLen() { + return len; + } + + void setLen(int len) { + this.len = len; + } + + } + + @ExportMessage + long semOpen(Object name, int openFlags, int mode, int value) throws PosixException { + long namePtr = pathToNativeCString(name); + try { + long ptr = posixNativeFunctionInvoker.call_sem_open(namePtr, openFlags, mode, value); + if (ptr == NULLPTR) { + throw getErrnoAndThrowPosixException(); + } + return ptr; + } finally { + NativeMemory.free(namePtr); + } + } + + @ExportMessage + void semClose(long handle) throws PosixException { + int res = posixNativeFunctionInvoker.call_sem_close(handle); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + void semUnlink(Object name) throws PosixException { + long namePtr = pathToNativeCString(name); + try { + int res = posixNativeFunctionInvoker.call_sem_unlink(namePtr); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + } finally { + NativeMemory.free(namePtr); + } + } + + private static final UnsupportedPosixFeatureException NO_SEM_GETVALUE_EXCEPTION = new UnsupportedPosixFeatureException("sem_getvalue is not available on the current platform"); + + @ExportMessage + int semGetValue(long handle) throws PosixException { + /* + * msimacek: It works on Linux, and it doesn't work on Darwin. It might work on some other + * Unix-likes, but it's hard to check, so let's assume it only works on Linux for now + */ + if (PythonLanguage.getPythonOS() != PythonOS.PLATFORM_LINUX) { + throw NO_SEM_GETVALUE_EXCEPTION; + } + long nativeValue = NativeMemory.mallocIntArray(1); + try { + int res = posixNativeFunctionInvoker.call_sem_getvalue(handle, nativeValue); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return NativeMemory.readInt(nativeValue); + } finally { + NativeMemory.free(nativeValue); + } + } + + @ExportMessage + void semPost(long handle) throws PosixException { + int res = posixNativeFunctionInvoker.call_sem_post(handle); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + void semWait(long handle) throws PosixException { + int res = posixNativeFunctionInvoker.call_sem_wait(handle); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + } + + @ExportMessage + boolean semTryWait(long handle) throws PosixException { + int res = posixNativeFunctionInvoker.call_sem_trywait(handle); + if (res < 0) { + int errno = posixNativeFunctionInvoker.get_errno(); + if (errno == OSErrorEnum.EAGAIN.getNumber()) { + return false; + } + throw newPosixException(errno); + } + return true; + } + + @ExportMessage + boolean semTimedWait(long handle, long deadlineNs, + @Bind Node node, + @CachedLibrary("this") PosixSupportLibrary thisLib) throws PosixException { + if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_LINUX) { + int res = posixNativeFunctionInvoker.call_sem_timedwait(handle, deadlineNs); + if (res < 0) { + int errno = posixNativeFunctionInvoker.get_errno(); + if (errno == OSErrorEnum.ETIMEDOUT.getNumber()) { + return false; + } + throw newPosixException(errno); + } + return true; + } else { + long deadlineMs = deadlineNs / 1_000_000; + while (true) { + if (thisLib.semTryWait(this, handle)) { + return true; + } + long currentMs = System.currentTimeMillis(); + if (currentMs > deadlineMs) { + return false; + } + long delayMs = Math.min(deadlineMs - currentMs, 20); + TruffleSafepoint.setBlockedThreadInterruptible(node, Thread::sleep, delayMs); + } + } + } + + @ExportMessage + @SuppressWarnings("static-method") + public PwdResult getpwuid(long uid, + @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { + return getpw(uid, NULLPTR, fromByteArrayNode, switchEncodingFromUtf8Node); + } + + @ExportMessage + @SuppressWarnings("static-method") + public PwdResult getpwnam(Object name, + @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { + long namePtr = pathToNativeCString(name); + try { + return getpw(-1, namePtr, fromByteArrayNode, switchEncodingFromUtf8Node); + } finally { + NativeMemory.free(namePtr); + } + } + + @ExportMessage + @SuppressWarnings("static-method") + public boolean hasGetpwentries() { + return true; + } + + @ExportMessage + @SuppressWarnings("static-method") + public PwdResult[] getpwentries( + @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { + // Note: this is not thread safe, so potentially problematic while running multiple contexts + // within one VM + int sysConfMax = getSysConfPwdSizeMax(); + int initialBufferSize = sysConfMax == -1 ? 1024 : sysConfMax; + + ArrayList result = new ArrayList<>(); + posixNativeFunctionInvoker.call_setpwent(); + long nativeBufferSize = NULLPTR; + long nativeOutput = NULLPTR; + int currentBufferSize = initialBufferSize; + long nativeBuffer = NULLPTR; + try { + nativeBufferSize = NativeMemory.mallocLongArray(1); + nativeOutput = NativeMemory.mallocLongArray(PWD_OUTPUT_LEN); + nativeBuffer = NativeMemory.mallocByteArray(currentBufferSize); + while (true) { + long pwPtr = posixNativeFunctionInvoker.call_getpwent(nativeBufferSize); + if (pwPtr == NULLPTR) { + break; + } + long bufferSize = NativeMemory.readLong(nativeBufferSize); + if (bufferSize < 0 || bufferSize > PWD_BUFFER_MAX_SIZE) { + throw outOfMemoryPosixError(); + } + if (currentBufferSize < bufferSize) { + NativeMemory.free(nativeBuffer); + currentBufferSize = (int) bufferSize; + nativeBuffer = NativeMemory.mallocByteArray(currentBufferSize); + } + int code = posixNativeFunctionInvoker.get_getpwent_data(pwPtr, nativeBuffer, currentBufferSize, nativeOutput); + if (code != 0) { + throw CompilerDirectives.shouldNotReachHere("get_getpwent_data failed"); + } + byte[] buffer = new byte[currentBufferSize]; + NativeMemory.readByteArrayElements(nativeBuffer, 0, buffer, 0, buffer.length); + long[] output = new long[PWD_OUTPUT_LEN]; + NativeMemory.readLongArrayElements(nativeOutput, 0, output, 0, output.length); + result.add(createPwdResult(buffer, output, fromByteArrayNode, switchEncodingFromUtf8Node)); + } + } finally { + posixNativeFunctionInvoker.call_endpwent(); + NativeMemory.free(nativeBuffer); + NativeMemory.free(nativeOutput); + NativeMemory.free(nativeBufferSize); + } + return toPwdResultArray(result); + } + + @TruffleBoundary + private static PwdResult[] toPwdResultArray(ArrayList result) { + return result.toArray(new PwdResult[0]); + } + + private PwdResult getpw(long uid, long namePtr, TruffleString.FromByteArrayNode fromByteArrayNode, + TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { + int sysConfMax = getSysConfPwdSizeMax(); + int bufferSize = sysConfMax == -1 ? 1024 : sysConfMax; + while (bufferSize < PWD_BUFFER_MAX_SIZE) { + long nativeData = NULLPTR; + long nativeOutput = NULLPTR; + try { + nativeData = NativeMemory.mallocByteArray(bufferSize); + nativeOutput = NativeMemory.mallocLongArray(PWD_OUTPUT_LEN); + int result = namePtr == NULLPTR + ? posixNativeFunctionInvoker.call_getpwuid_r(uid, nativeData, bufferSize, nativeOutput) + : posixNativeFunctionInvoker.call_getpwname_r(namePtr, nativeData, bufferSize, nativeOutput); + if (result == -1) { + return null; + } + if (result == 0) { + byte[] data = new byte[bufferSize]; + NativeMemory.readByteArrayElements(nativeData, 0, data, 0, data.length); + long[] output = new long[PWD_OUTPUT_LEN]; + NativeMemory.readLongArrayElements(nativeOutput, 0, output, 0, output.length); + return createPwdResult(data, output, fromByteArrayNode, switchEncodingFromUtf8Node); + } + if (result != OSErrorEnum.ERANGE.getNumber() || sysConfMax != -1) { + // no point in trying larger buffer if we got different error or the OS already + // told + // us that sysConfMax should be enough... + throw newPosixException(result); + } + } finally { + NativeMemory.free(nativeOutput); + NativeMemory.free(nativeData); + } + bufferSize <<= 1; + } + throw outOfMemoryPosixError(); + } + + private static PwdResult createPwdResult(byte[] data, long[] output, TruffleString.FromByteArrayNode fromByteArrayNode, TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) + throws PosixException { + return new PwdResult( + extractZeroTerminatedString(data, output[0], fromByteArrayNode, switchEncodingFromUtf8Node), + output[1], output[2], + extractZeroTerminatedString(data, output[3], fromByteArrayNode, switchEncodingFromUtf8Node), + extractZeroTerminatedString(data, output[4], fromByteArrayNode, switchEncodingFromUtf8Node)); + } + + @ExportMessage + public int ioctlBytes(int fd, long request, byte[] arg) throws PosixException { + long nativeArg = NULLPTR; + try { + nativeArg = NativeMemory.mallocByteArray(Math.max(arg.length, 1)); + if (arg.length > 0) { + NativeMemory.writeByteArrayElements(nativeArg, 0, arg, 0, arg.length); + } + int res = posixNativeFunctionInvoker.call_ioctl_bytes(fd, request, nativeArg); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + if (arg.length > 0) { + NativeMemory.readByteArrayElements(nativeArg, 0, arg, 0, arg.length); + } + return res; + } finally { + NativeMemory.free(nativeArg); + } + } + + @ExportMessage + public int ioctlInt(int fd, long request, int arg) throws PosixException { + int res = posixNativeFunctionInvoker.call_ioctl_int(fd, request, arg); + if (res < 0) { + throw getErrnoAndThrowPosixException(); + } + return res; + } + + private static TruffleString extractZeroTerminatedString(byte[] buffer, long longOffset, TruffleString.FromByteArrayNode fromByteArrayNode, + TruffleString.SwitchEncodingNode switchEncodingFromUtf8Node) throws PosixException { + if (longOffset < 0 || longOffset >= buffer.length) { + throw outOfMemoryPosixError(); + } + int offset = (int) longOffset; + int end = ArrayUtils.indexOf(buffer, offset, buffer.length, (byte) 0); + if (end < 0) { + throw CompilerDirectives.shouldNotReachHere("Could not find the end of the string"); + } + // TODO PyUnicode_DecodeFSDefault + return createString(buffer, offset, end - offset, true, fromByteArrayNode, switchEncodingFromUtf8Node); + } + + private static PosixException outOfMemoryPosixError() throws PosixException { + throw new PosixErrnoException(OSErrorEnum.ENOMEM.getNumber(), OSErrorEnum.ENOMEM.getMessage()); + } + + private int sysConfPwdSizeMax = -1; + + private int getSysConfPwdSizeMax() throws PosixException { + if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, sysConfPwdSizeMax == -1)) { + long sysConfMaxLong = posixNativeFunctionInvoker.get_sysconf_getpw_r_size_max(); + if (sysConfMaxLong != -1 && (sysConfMaxLong < 0 || sysConfMaxLong > PWD_BUFFER_MAX_SIZE)) { + throw outOfMemoryPosixError(); + } + sysConfPwdSizeMax = (int) sysConfMaxLong; + } + return sysConfPwdSizeMax; + } + + // ------------------ + // Path conversions + + @ExportMessage + @SuppressWarnings("static-method") + public Object createPathFromString(TruffleString path, + @Shared("toUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingNode, + @Shared("tsCopyBytes") @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { + return checkPath(getStringBytes(path, switchEncodingNode, copyToByteArrayNode)); + } + + @ExportMessage + @SuppressWarnings("static-method") + public Object createPathFromBytes(byte[] path) { + return checkPath(path); + } + + @ExportMessage + @SuppressWarnings("static-method") + public TruffleString getPathAsString(Object path, + @Shared("tsFromBytes") @Cached TruffleString.FromByteArrayNode fromByteArrayNode, + @Shared("fromUtf8") @Cached TruffleString.SwitchEncodingNode switchEncodingNode) { + Buffer result = (Buffer) path; + if (result.length > Integer.MAX_VALUE) { + // sanity check that it is safe to cast result.length to int, to be removed once + // we support large arrays + throw CompilerDirectives.shouldNotReachHere("Posix path cannot fit into a Java array"); + } + return createString(result.data, 0, (int) result.length, true, fromByteArrayNode, switchEncodingNode); + } + + @ExportMessage + @SuppressWarnings("static-method") + public Buffer getPathAsBytes(Object path) { + return (Buffer) path; + } + + private static TruffleString createString(byte[] src, int offset, int length, boolean copy, TruffleString.FromByteArrayNode fromByteArrayNode, + TruffleString.SwitchEncodingNode switchEncodingNode) { + // TODO PyUnicode_DecodeFSDefault + TruffleString utf8 = fromByteArrayNode.execute(src, offset, length, UTF_8, copy); + return switchEncodingNode.execute(utf8, TS_ENCODING); + } + + private static byte[] getStringBytes(TruffleString str, TruffleString.SwitchEncodingNode switchEncodingNode, TruffleString.CopyToByteArrayNode copyToByteArrayNode) { + // TODO replace getBytes with PyUnicode_FSConverter equivalent + TruffleString utf8 = switchEncodingNode.execute(str, UTF_8); + byte[] bytes = new byte[utf8.byteLength(UTF_8)]; + copyToByteArrayNode.execute(utf8, 0, bytes, 0, bytes.length, UTF_8); + return bytes; + } + + private static Buffer checkPath(byte[] path) { + for (byte b : path) { + if (b == 0) { + return null; + } + } + // TODO we keep a byte[] provided by the caller, who can potentially change it, making our + // check for embedded nulls pointless. Maybe we should copy it and while on it, might as + // well add the terminating null character, avoiding the copy we do later in pathToCString. + return Buffer.wrap(path); + } + + // ------------------ + // Objects/handles/pointers + + protected static class DirEntry { + final Buffer name; + final long ino; + final int type; + + DirEntry(Buffer name, long ino, int type) { + this.name = name; + this.ino = ino; + this.type = type; + } + + @Override + public String toString() { + return "DirEntry{" + + "name='" + new String(name.data, 0, (int) name.length) + "'" + + ", ino=" + ino + + ", type=" + type + + '}'; + } + } + + // ------------------ + // Helpers + + private PosixException getErrnoAndThrowPosixException() throws PosixException { + throw newPosixException(posixNativeFunctionInvoker.get_errno()); + } + + @TruffleBoundary + private PosixException newPosixException(int errno) throws PosixException { + throw new PosixErrnoException(errno, strerror(errno, null, NativeMemory.ZeroTerminatedUtf8ToTruffleStringNode.getUncached())); + } + + private static long copyTimevalArrayToNativeOrNull(Timeval[] timeval) { + return timeval == null ? NULLPTR : NativeMemory.copyToNativeLongArray(new long[]{timeval[0].getSeconds(), timeval[0].getMicroseconds(), timeval[1].getSeconds(), timeval[1].getMicroseconds()}); + } + + private static long wrapItimerval(Timeval delay, Timeval interval) { + long ptr = NativeMemory.mallocLongArray(4); + NativeMemory.writeLongArrayElement(ptr, 0, delay.getSeconds()); + NativeMemory.writeLongArrayElement(ptr, 1, delay.getMicroseconds()); + NativeMemory.writeLongArrayElement(ptr, 2, interval.getSeconds()); + NativeMemory.writeLongArrayElement(ptr, 3, interval.getMicroseconds()); + return ptr; + } + + private static void readNativeSockAddr(long nativeAddr, long nativeAddrLen, UniversalSockAddrImpl addr) { + int addrLen = NativeMemory.readInt(nativeAddrLen); + if (addrLen < 0 || addrLen > addr.data.length) { + throw CompilerDirectives.shouldNotReachHere("Unexpected socket address length"); + } + addr.setLen(addrLen); + if (addrLen > 0) { + NativeMemory.readByteArrayElements(nativeAddr, 0, addr.data, 0, addrLen); + } + } + + private static Timeval[] unwrapTimeval(long nativeTimeval) { + return new Timeval[]{ + new Timeval(NativeMemory.readLongArrayElement(nativeTimeval, 0), NativeMemory.readLongArrayElement(nativeTimeval, 1)), + new Timeval(NativeMemory.readLongArrayElement(nativeTimeval, 2), NativeMemory.readLongArrayElement(nativeTimeval, 3)) + }; + } + + private static int findZero(byte[] buf) { + for (int i = 0; i < buf.length; ++i) { + if (buf[i] == 0) { + return i; + } + } + return buf.length; + } + + private long pathToNativeCStringOrNull(Object path) { + return path == null ? NULLPTR : bufferToNativeCString((Buffer) path); + } + + private long pathToNativeCString(Object path) { + return bufferToNativeCString((Buffer) path); + } + + private static long bufferToNativeCString(Buffer path) { + return NativeMemory.copyToNativeZeroTerminatedByteArray(path.data, 0, (int) path.length); + } + + private long stringToNativeUTF8CString(TruffleString input, + @Cached TruffleString.SwitchEncodingNode switchEncodingToUtf8Node, + @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { + byte[] utf8 = getStringBytes(input, switchEncodingToUtf8Node, copyToByteArrayNode); + return NativeMemory.copyToNativeZeroTerminatedByteArray(utf8, 0, utf8.length); + } + + private static void checkBounds(byte[] buf, int offset, int length) { + if (length < 0) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IllegalArgumentException(); + } + if (offset < 0 || offset + length > buf.length) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + throw new IndexOutOfBoundsException(); + } + } + + @TruffleBoundary + private static void log(Level level, String fmt, Object... args) { + if (LOGGER.isLoggable(level)) { + LOGGER.log(level, String.format(fmt, args)); + } + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java index baa1a22992..7aaccb04d9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java @@ -348,7 +348,7 @@ public record OpenPtyResult(int masterFd, int slaveFd) { public abstract TruffleString ctermid(Object receiver) throws PosixException; - // note: this leaks memory in nfi backend and is not synchronized + // note: this leaks memory in native backend and is not synchronized // TODO is it worth synchronizing at least all accesses made through PosixSupportLibrary? public abstract void setenv(Object receiver, Object name, Object value, boolean overwrite) throws PosixException; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java index 5f8d75d561..7ae13c26b8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java @@ -1873,30 +1873,35 @@ private void initializePosixSupport() { // The resources field will be removed once all posix builtins go through PosixSupport TruffleString.EqualNode eqNode = TruffleString.EqualNode.getUncached(); boolean selectedJavaBackend = eqNode.execute(T_JAVA, option, TS_ENCODING); - if (PythonImageBuildOptions.WITHOUT_NATIVE_POSIX || selectedJavaBackend) { - if (!selectedJavaBackend) { - writeWarning("Native Posix backend selected, but it was excluded from the runtime, " + - "switching to Java backend."); - } + boolean selectedNativeBackend = eqNode.execute(T_NATIVE, option, TS_ENCODING); + if (selectedJavaBackend) { + result = new EmulatedPosixSupport(this); + } else if (selectedNativeBackend && PythonImageBuildOptions.WITHOUT_NATIVE_POSIX) { + writeWarning("Native Posix backend selected, but it was excluded from the runtime, " + + "switching to Java backend."); + result = new EmulatedPosixSupport(this); + } else if (selectedNativeBackend && !NativeAccessSupport.isAvailable()) { + writeWarning("Native Posix backend selected, but native access downcalls are not available in this runtime, " + + "switching to Java backend."); result = new EmulatedPosixSupport(this); - } else if (eqNode.execute(T_NATIVE, option, TS_ENCODING)) { + } else if (selectedNativeBackend) { if (env.isPreInitialization()) { EmulatedPosixSupport emulatedPosixSupport = new EmulatedPosixSupport(this); - NFIPosixSupport nativePosixSupport = new NFIPosixSupport(this, option); + NativePosixSupport nativePosixSupport = new NativePosixSupport(this, option); result = new PreInitPosixSupport(env, nativePosixSupport, emulatedPosixSupport); } else if (TruffleOptions.AOT) { // We always use a PreInitPosixSupport on SVM to keep the type of the posixSupport // field consistent for both pre-initialized and not-pre-initialized contexts so // that host inlining and PE see only one type, and also to avoid polymorphism when // calling library methods. - NFIPosixSupport nativePosixSupport = new NFIPosixSupport(this, option); + NativePosixSupport nativePosixSupport = new NativePosixSupport(this, option); result = new PreInitPosixSupport(env, nativePosixSupport, null); } else { if (!getOption(PythonOptions.RunViaLauncher)) { writeWarning("Native Posix backend is not fully supported when embedding. For example, standard I/O always uses file " + "descriptors 0, 1 and 2 regardless of stream redirection specified in Truffle environment"); } - result = new NFIPosixSupport(this, option); + result = new NativePosixSupport(this, option); } } else { throw new IllegalStateException(String.format("Wrong value for the PosixModuleBackend option: '%s'", option)); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupport.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupport.java index 2bc1692b5e..f0caf3e8d1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupport.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupport.java @@ -46,6 +46,8 @@ import org.graalvm.nativeimage.ImageInfo; +import com.oracle.graal.python.annotations.NativeSimpleType; + public abstract class NativeAccessSupport { private static final NativeAccessSupport INSTANCE = createImpl(); @@ -78,7 +80,7 @@ protected static Class asJavaType(NativeSimpleType type) { case SINT64 -> long.class; case FLOAT -> float.class; case DOUBLE -> double.class; - case RAW_POINTER -> long.class; + case POINTER -> long.class; }; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupportJdk21.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupportJdk21.java index 506e0f1029..d19c26e267 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupportJdk21.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeAccessSupportJdk21.java @@ -43,6 +43,8 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodType; +import com.oracle.graal.python.annotations.NativeSimpleType; + final class NativeAccessSupportJdk21 extends NativeAccessSupport { @Override protected Object createArenaImpl() { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeContext.java index 6c54eeda78..97e7aa5947 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeContext.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeContext.java @@ -44,6 +44,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; import com.oracle.graal.python.PythonLanguage; +import com.oracle.graal.python.annotations.NativeSimpleType; import com.oracle.graal.python.annotations.PythonOS; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -65,6 +66,7 @@ public final class NativeContext { private static final int FORMAT_MESSAGE_BUFFER_CHARS = 2048; private final ConcurrentLinkedQueue libraries = new ConcurrentLinkedQueue<>(); + private final NativeLibrary defaultLibrary; final Object arena; public static NativeContext create() { @@ -74,6 +76,7 @@ public static NativeContext create() { @TruffleBoundary NativeContext() { arena = NativeAccessSupport.createArena(); + defaultLibrary = isWindows() ? null : new NativeLibrary(this, getPosixDefaultLibraryHandle()); } public void close() { @@ -124,6 +127,15 @@ public NativeLibrary loadLibrary(String name, int flags) throws NativeLibraryLoa return library; } + public NativeLibrary getDefaultLibrary() { + CompilerAsserts.neverPartOfCompilation(); + if (defaultLibrary == null) { + throw new UnsupportedOperationException("Default library is only available on POSIX platforms."); + } + ensureLoader(); + return defaultLibrary; + } + @SuppressWarnings("static-method") long lookupOptionalSymbol(long library, String name) { // TODO(native-access) if logging enabled, keep track of ptr->name mappings @@ -144,18 +156,23 @@ private static boolean isWindows() { // TODO(native-access) platform-specific values for RTLD_* constants private static final int RTLD_LAZY = 1; private static final int RTLD_NOW = 2; + // RTLD_DEFAULT is a special dlsym() handle rather than a portable POSIX numeric constant. + // Linux libcs use ((void *) 0), while Darwin uses ((void *) -2). nativeaccess needs the raw + // handle to issue dlsym(RTLD_DEFAULT, ...) directly, so we hardcode the supported POSIX ABI + // values here instead of introducing another helper library just to fetch this constant. + private static final long RTLD_DEFAULT_LINUX = 0L; + private static final long RTLD_DEFAULT_DARWIN = -2L; - private static final MethodHandle DLOPEN = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.RAW_POINTER, NativeSimpleType.SINT32); + private static final MethodHandle DLOPEN = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.POINTER, NativeSimpleType.SINT32); private static final MethodHandle DLCLOSE = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32, NativeSimpleType.SINT64); - private static final MethodHandle DLSYM = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.SINT64, NativeSimpleType.RAW_POINTER); - private static final MethodHandle LOAD_LIBRARY_EX = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.RAW_POINTER, NativeSimpleType.RAW_POINTER, + private static final MethodHandle DLSYM = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.SINT64, NativeSimpleType.POINTER); + private static final MethodHandle LOAD_LIBRARY_EX = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.POINTER, NativeSimpleType.POINTER, NativeSimpleType.SINT32); private static final MethodHandle FREE_LIBRARY = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32, NativeSimpleType.SINT64); - private static final MethodHandle GET_PROC_ADDRESS = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.SINT64, NativeSimpleType.RAW_POINTER); + private static final MethodHandle GET_PROC_ADDRESS = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64, NativeSimpleType.SINT64, NativeSimpleType.POINTER); private static final MethodHandle GET_LAST_ERROR = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32); - private static final MethodHandle FORMAT_MESSAGE = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32, NativeSimpleType.SINT32, NativeSimpleType.RAW_POINTER, NativeSimpleType.SINT32, - NativeSimpleType.SINT32, - NativeSimpleType.RAW_POINTER, NativeSimpleType.SINT32, NativeSimpleType.RAW_POINTER); + private static final MethodHandle FORMAT_MESSAGE = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT32, NativeSimpleType.SINT32, NativeSimpleType.POINTER, NativeSimpleType.SINT32, + NativeSimpleType.SINT32, NativeSimpleType.POINTER, NativeSimpleType.SINT32, NativeSimpleType.POINTER); private static final MethodHandle DLERROR = NativeAccessSupport.createDowncallHandle(NativeSimpleType.SINT64); private static long dlopenPtr; @@ -255,4 +272,12 @@ private static String formatWindowsError(int errorCode) { NativeMemory.free(buffer); } } + + private static long getPosixDefaultLibraryHandle() { + return switch (PythonLanguage.getPythonOS()) { + case PLATFORM_LINUX -> RTLD_DEFAULT_LINUX; + case PLATFORM_DARWIN -> RTLD_DEFAULT_DARWIN; + default -> throw new UnsupportedOperationException("Default library is only available on POSIX platforms."); + }; + } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeFunctionPointer.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeFunctionPointer.java index 217975cfcd..e5e1e8df17 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeFunctionPointer.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeFunctionPointer.java @@ -40,6 +40,7 @@ */ package com.oracle.graal.python.runtime.nativeaccess; +import com.oracle.graal.python.annotations.NativeSimpleType; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; /** diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeMemory.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeMemory.java index dd6dca5e49..4c4b900748 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeMemory.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeMemory.java @@ -40,11 +40,23 @@ */ package com.oracle.graal.python.runtime.nativeaccess; +import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; + import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; +import com.oracle.graal.python.runtime.nativeaccess.NativeMemoryFactory.ZeroTerminatedUtf8ToTruffleStringNodeGen; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.GenerateCached; +import com.oracle.truffle.api.dsl.GenerateInline; +import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.NeverDefault; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.strings.TruffleString; +import com.oracle.truffle.api.strings.TruffleString.Encoding; import sun.misc.Unsafe; @@ -101,6 +113,11 @@ public static long mallocByteArray(long count) { return malloc(count); } + public static long mallocByteArrayOrNull(long count) { + assert count >= 0; + return count == 0 ? NULLPTR : mallocByteArray(count); + } + public static long callocByteArray(long count) { assert count > 0; return calloc(count); @@ -184,13 +201,46 @@ public static byte[] readByteArrayElements(long arrayPtr, long srcIndex, int cou } public static void readByteArrayElements(long arrayPtr, long srcIndex, byte[] dst, int dstIndex, int count) { + if (count == 0) { + return; + } UNSAFE.copyMemory(null, arrayPtr + srcIndex, dst, Unsafe.ARRAY_BYTE_BASE_OFFSET + (long) dstIndex, count); } public static void writeByteArrayElements(long arrayPtr, long dstIndex, byte[] src, int offset, int count) { + if (count == 0) { + return; + } UNSAFE.copyMemory(src, Unsafe.ARRAY_BYTE_BASE_OFFSET + (long) offset, null, arrayPtr + dstIndex, count); } + public static long copyToNativeByteArray(byte[] src) { + return copyToNativeByteArray(src, 0, src.length); + } + + public static long copyToNativeByteArray(byte[] src, int offset, int count) { + assert offset >= 0 && count > 0 && offset + count <= src.length; + long ptr = mallocByteArray(count); + writeByteArrayElements(ptr, 0, src, offset, count); + return ptr; + } + + public static long copyToNativeByteArrayOrNull(byte[] src) { + return copyToNativeByteArrayOrNull(src, 0, src.length); + } + + public static long copyToNativeByteArrayOrNull(byte[] src, int offset, int count) { + assert offset >= 0 && count >= 0 && offset + count <= src.length; + return count == 0 ? NULLPTR : copyToNativeByteArray(src, offset, count); + } + + public static long copyToNativeZeroTerminatedByteArray(byte[] src, int offset, int count) { + long ptr = mallocByteArray(count + 1L); + writeByteArrayElements(ptr, 0, src, offset, count); + writeByteArrayElement(ptr, count, (byte) 0); + return ptr; + } + public static void copyByteArray(long dstArray, long dstIndex, long srcArray, long srcIndex, long count) { memcpy(dstArray + dstIndex, srcArray + srcIndex, count); } @@ -231,6 +281,48 @@ public static void writeIntArrayElement(long arrayPtr, long index, int value) { writeInt(arrayPtr + index * Integer.BYTES, value); } + public static int[] readIntArrayElements(long arrayPtr, long srcIndex, int count) { + int[] result = new int[count]; + readIntArrayElements(arrayPtr, srcIndex, result, 0, count); + return result; + } + + public static void readIntArrayElements(long arrayPtr, long srcIndex, int[] dst, int dstIndex, int count) { + if (count == 0) { + return; + } + assert canMultiplyWithoutOverflow(srcIndex, Integer.BYTES); + UNSAFE.copyMemory(null, arrayPtr + srcIndex * Integer.BYTES, dst, Unsafe.ARRAY_INT_BASE_OFFSET + (long) dstIndex * Integer.BYTES, (long) count * Integer.BYTES); + } + + public static void writeIntArrayElements(long arrayPtr, long dstIndex, int[] src, int offset, int count) { + if (count == 0) { + return; + } + assert canMultiplyWithoutOverflow(dstIndex, Integer.BYTES); + UNSAFE.copyMemory(src, Unsafe.ARRAY_INT_BASE_OFFSET + (long) offset * Integer.BYTES, null, arrayPtr + dstIndex * Integer.BYTES, (long) count * Integer.BYTES); + } + + public static long copyToNativeIntArray(int[] src) { + return copyToNativeIntArray(src, 0, src.length); + } + + public static long copyToNativeIntArray(int[] src, int offset, int count) { + assert offset >= 0 && count > 0 && offset + count <= src.length; + long ptr = mallocIntArray(count); + writeIntArrayElements(ptr, 0, src, offset, count); + return ptr; + } + + public static long copyToNativeIntArrayOrNull(int[] src) { + return copyToNativeIntArrayOrNull(src, 0, src.length); + } + + public static long copyToNativeIntArrayOrNull(int[] src, int offset, int count) { + assert offset >= 0 && count >= 0 && offset + count <= src.length; + return count == 0 ? NULLPTR : copyToNativeIntArray(src, offset, count); + } + public static long readLong(long pointer) { return UNSAFE.getLong(pointer); } @@ -256,10 +348,41 @@ public static long[] readLongArrayElements(long arrayPtr, long srcIndex, int cou } public static void readLongArrayElements(long arrayPtr, long srcIndex, long[] dst, int dstIndex, int count) { + if (count == 0) { + return; + } assert canMultiplyWithoutOverflow(srcIndex, Long.BYTES); UNSAFE.copyMemory(null, arrayPtr + srcIndex * Long.BYTES, dst, Unsafe.ARRAY_LONG_BASE_OFFSET + (long) dstIndex * Long.BYTES, (long) count * Long.BYTES); } + public static void writeLongArrayElements(long arrayPtr, long dstIndex, long[] src, int offset, int count) { + if (count == 0) { + return; + } + assert canMultiplyWithoutOverflow(dstIndex, Long.BYTES); + UNSAFE.copyMemory(src, Unsafe.ARRAY_LONG_BASE_OFFSET + (long) offset * Long.BYTES, null, arrayPtr + dstIndex * Long.BYTES, (long) count * Long.BYTES); + } + + public static long copyToNativeLongArray(long[] src) { + return copyToNativeLongArray(src, 0, src.length); + } + + public static long copyToNativeLongArray(long[] src, int offset, int count) { + assert offset >= 0 && count > 0 && offset + count <= src.length; + long ptr = mallocLongArray(count); + writeLongArrayElements(ptr, 0, src, offset, count); + return ptr; + } + + public static long copyToNativeLongArrayOrNull(long[] src) { + return copyToNativeLongArrayOrNull(src, 0, src.length); + } + + public static long copyToNativeLongArrayOrNull(long[] src, int offset, int count) { + assert offset >= 0 && count >= 0 && offset + count <= src.length; + return count == 0 ? NULLPTR : copyToNativeLongArray(src, offset, count); + } + public static long readPtr(long pointer) { return UNSAFE.getLong(pointer); } @@ -342,6 +465,39 @@ static String zeroTerminatedUtf16ToJavaString(long ptr) { return new String(bytes, StandardCharsets.UTF_16LE); } + /** + * Converts a zero-terminated UTF-8 string in native memory to a managed {@link TruffleString}. + * + * The result is always materialized on the Java side so callers may free the native buffer + * immediately after the call returns. + */ + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class ZeroTerminatedUtf8ToTruffleStringNode extends Node { + + public abstract TruffleString execute(Node inliningTarget, long ptr); + + @TruffleBoundary + public static TruffleString executeUncached(long ptr) { + return ZeroTerminatedUtf8ToTruffleStringNodeGen.getUncached().execute(null, ptr); + } + + @Specialization + static TruffleString doPointer(long ptr, + @Cached(inline = false) TruffleString.FromZeroTerminatedNativePointerNode fromNativePointerNode, + @Cached(inline = false) TruffleString.SwitchEncodingNode switchEncodingNode, + @Cached(inline = false) TruffleString.AsManagedNode asManagedNode) { + TruffleString utf8 = fromNativePointerNode.execute8Bit(ptr, 0, Encoding.UTF_8, false); + return asManagedNode.execute(switchEncodingNode.execute(utf8, TS_ENCODING), TS_ENCODING); + } + + @NeverDefault + public static ZeroTerminatedUtf8ToTruffleStringNode getUncached() { + return ZeroTerminatedUtf8ToTruffleStringNodeGen.getUncached(); + } + } + private static boolean canMultiplyWithoutOverflow(long value, int stride) { assert value >= 0 : "Value must be non-negative"; assert stride > 0 && (stride & (stride - 1)) == 0 : "Stride must be a power of two"; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSignature.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSignature.java index 0de2e9c2e1..c7785d779d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSignature.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/nativeaccess/NativeSignature.java @@ -43,6 +43,7 @@ import java.lang.invoke.MethodHandle; import java.util.Arrays; +import com.oracle.graal.python.annotations.NativeSimpleType; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; public final class NativeSignature { diff --git a/graalpython/python-libposix/src/fork_exec.c b/graalpython/python-libposix/src/fork_exec.c index 1ddb1b54b3..9f941ec68f 100644 --- a/graalpython/python-libposix/src/fork_exec.c +++ b/graalpython/python-libposix/src/fork_exec.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2021, 2025, Oracle and/or its affiliates. +/* Copyright (c) 2021, 2026, Oracle and/or its affiliates. * Copyright (C) 1996-2020 Python Software Foundation * * Licensed under the PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 @@ -396,7 +396,7 @@ child_exec(char *const exec_array[], /* - * data, offsets, offsetsLen, argsPos, envPos, cwdPos - see comment in NFiPosixSupport.forkExec() + * data, offsets, offsetsLen, argsPos, envPos, cwdPos - see comment in NativePosixSupport.forkExec() * stdinRdFd - read end of the pipe for the child's stdin - closed by parent, dupped to fd 0 by child * stdinWrFd - write end of the pipe for the child's stdin - closed by child, written to by parent * stdoutRdFd - read end of the pipe for the child's stdout - closed by child, read from by parent diff --git a/graalpython/python-libposix/src/posix.c b/graalpython/python-libposix/src/posix.c index 5f4d23366f..12893a061c 100644 --- a/graalpython/python-libposix/src/posix.c +++ b/graalpython/python-libposix/src/posix.c @@ -40,7 +40,7 @@ */ // Helper functions that mostly delegate to POSIX functions -// These functions are called from NFIPosixSupport Java class using NFI +// These functions are called from NativePosixSupport Java class using native-access downcalls // This file uses GNU extensions. Functions that require non-GNU versions (e.g. strerror_r) // need to go to posix_no_gnu.c @@ -204,9 +204,13 @@ int32_t call_select(int32_t nfds, int32_t* readfds, int32_t readfdsLen, int32_t* writefds, int32_t writefdsLen, int32_t* errfds, int32_t errfdsLen, int64_t timeoutSec, int64_t timeoutUsec, int8_t* selected) { fd_set readfdsSet, writefdsSet, errfdsSet; + int32_t selectedLen = readfdsLen + writefdsLen + errfdsLen; fill_fd_set(&readfdsSet, readfds, readfdsLen); fill_fd_set(&writefdsSet, writefds, writefdsLen); fill_fd_set(&errfdsSet, errfds, errfdsLen); + if (selectedLen > 0) { + memset(selected, 0, selectedLen * sizeof(*selected)); + } struct timeval timeout = {timeoutSec, timeoutUsec}; @@ -732,25 +736,30 @@ int32_t call_getrusage(int32_t who, uint64_t* out) { int offset = 0; // POSIX prescribes only ru_utime and ru_stime members, macOS and Linux // have (at least) all those below -# define COPYVAL(v) memcpy(&out[offset++], &v, sizeof(v)) - COPYVAL(ru.ru_utime.tv_sec); - COPYVAL(ru.ru_stime.tv_sec); - COPYVAL(ru.ru_maxrss); - COPYVAL(ru.ru_ixrss); - COPYVAL(ru.ru_idrss); - COPYVAL(ru.ru_isrss); - COPYVAL(ru.ru_minflt); - COPYVAL(ru.ru_majflt); - COPYVAL(ru.ru_nswap); - COPYVAL(ru.ru_inblock); - COPYVAL(ru.ru_oublock); - COPYVAL(ru.ru_msgsnd); - COPYVAL(ru.ru_msgrcv); - COPYVAL(ru.ru_nsignals); - COPYVAL(ru.ru_nvcsw); - COPYVAL(ru.ru_nivcsw); +# define COPYDOUBLE(sec, usec) do { \ + double value = (double)(sec) + ((double)(usec) / 1000000.0); \ + memcpy(&out[offset++], &value, sizeof(value)); \ + } while (0) +# define COPYLONG(v) out[offset++] = (uint64_t)(int64_t)(v) + COPYDOUBLE(ru.ru_utime.tv_sec, ru.ru_utime.tv_usec); + COPYDOUBLE(ru.ru_stime.tv_sec, ru.ru_stime.tv_usec); + COPYLONG(ru.ru_maxrss); + COPYLONG(ru.ru_ixrss); + COPYLONG(ru.ru_idrss); + COPYLONG(ru.ru_isrss); + COPYLONG(ru.ru_minflt); + COPYLONG(ru.ru_majflt); + COPYLONG(ru.ru_nswap); + COPYLONG(ru.ru_inblock); + COPYLONG(ru.ru_oublock); + COPYLONG(ru.ru_msgsnd); + COPYLONG(ru.ru_msgrcv); + COPYLONG(ru.ru_nsignals); + COPYLONG(ru.ru_nvcsw); + COPYLONG(ru.ru_nivcsw); return 0; -# undef COPYVAL +# undef COPYLONG +# undef COPYDOUBLE #else return -1; #endif @@ -772,7 +781,7 @@ int32_t call_unsetenv(char *name) { return unsetenv(name); } -// See comment in NFiPosixSupport.execv() for the description of arguments +// See comment in NativePosixSupport.execv() for the description of arguments void call_execv(char *data, int64_t *offsets, int32_t offsetsLen) { // We reuse the memory allocated for offsets to avoid the need to allocate and reliably free another array char **strings = (char **) offsets; @@ -866,8 +875,8 @@ int32_t call_getsockname(int32_t sockfd, int8_t *addr, int32_t *addr_len) { } //TODO len should be size_t, retval should be ssize_t -int32_t call_send(int32_t sockfd, void *buf, int32_t offset, int32_t len, int32_t flags) { - return send(sockfd, buf + offset, len, flags); +int32_t call_send(int32_t sockfd, void *buf, int32_t len, int32_t flags) { + return send(sockfd, buf, len, flags); } int32_t call_sendto(int32_t sockfd, void *buf, int32_t offset, int32_t len, int32_t flags, int8_t *addr, int32_t addr_len) { @@ -876,8 +885,8 @@ int32_t call_sendto(int32_t sockfd, void *buf, int32_t offset, int32_t len, int3 return sendto(sockfd, buf + offset, len, flags, (struct sockaddr *) &sa, addr_len); } -int32_t call_recv(int32_t sockfd, void *buf, int32_t offset, int32_t len, int32_t flags) { - return recv(sockfd, buf + offset, len, flags); +int32_t call_recv(int32_t sockfd, void *buf, int32_t len, int32_t flags) { + return recv(sockfd, buf, len, flags); } int32_t call_recvfrom(int32_t sockfd, void *buf, int32_t offset, int32_t len, int32_t flags, int8_t *src_addr, int32_t *addr_len) { @@ -999,7 +1008,7 @@ void call_gai_strerror(int32_t error, char *buf, int32_t buflen) { } int32_t get_addrinfo_members(int64_t ptr, int32_t *intData, int64_t *longData, int8_t *addr) { - // see NFIPosixSupport.AddrInfo for description of the way data is transferred + // see NativePosixSupport.AddrInfo for description of the way data is transferred struct addrinfo *ai = (struct addrinfo *) ptr; memcpy(addr, ai->ai_addr, ai->ai_addrlen); diff --git a/graalpython/python-libposix/src/posix_no_gnu.c b/graalpython/python-libposix/src/posix_no_gnu.c index 88ef31bb2e..7b3c76ae3a 100644 --- a/graalpython/python-libposix/src/posix_no_gnu.c +++ b/graalpython/python-libposix/src/posix_no_gnu.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -40,7 +40,7 @@ */ // This source contains functions that require POSIX functions without GNU extensions -// These functions are called from NFIPosixSupport Java class using NFI +// These functions are called from NativePosixSupport Java class using native-access downcalls #include #include