Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: c1191bd01aad
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 28f3c3dea7c8
Choose a head ref
  • 6 commits
  • 8 files changed
  • 1 contributor

Commits on Dec 15, 2016

  1. Copy the full SHA
    0f0b952 View commit details
  2. Copy the full SHA
    311d607 View commit details
  3. Copy the full SHA
    18821c2 View commit details
  4. Copy the full SHA
    d1bc8d9 View commit details
  5. Copy the full SHA
    e2ba3f5 View commit details
  6. Copy the full SHA
    28f3c3d View commit details
13 changes: 0 additions & 13 deletions spec/truffle/tags/core/kernel/exec_tags.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
fails:Kernel#exec raises an ArgumentError if the command includes a null byte
fails:Kernel#exec runs the specified command, replacing current process
fails:Kernel#exec sets the current directory when given the :chdir option
fails:Kernel#exec with a single argument subjects the specified command to shell expansion
fails:Kernel#exec with a single argument creates an argument array with shell parsing semantics for whitespace
fails:Kernel#exec (environment variables) sets environment variables in the child environment
fails:Kernel#exec (environment variables) unsets environment variables whose value is nil
fails:Kernel#exec (environment variables) coerces environment argument using to_hash
fails:Kernel#exec (environment variables) unsets other environment variables when given a true :unsetenv_others option
fails:Kernel#exec with a command array uses the first element as the command name and the second as the argv[0] value
fails:Kernel#exec with a command array coerces the argument using to_ary
fails:Kernel#exec with a command array raises an ArgumentError if the Array does not have exactly two elements
fails:Kernel#exec with an options Hash with Integer option keys maps the key to a file descriptor in the child that inherits the file descriptor from the parent specified by the value
slow:Kernel#exec raises Errno::ENOENT for an empty string
slow:Kernel#exec raises Errno::ENOENT for a command which does not exist
slow:Kernel#exec raises an ArgumentError if the command includes a null byte
16 changes: 0 additions & 16 deletions spec/truffle/tags/core/process/exec_tags.txt
Original file line number Diff line number Diff line change
@@ -1,19 +1,3 @@
fails:Process.exec raises Errno::ENOENT for a command which does not exist
fails:Process.exec raises an ArgumentError if the command includes a null byte
fails:Process.exec raises Errno::EACCES when the file does not have execute permissions
fails:Process.exec raises Errno::EACCES when passed a directory
fails:Process.exec runs the specified command, replacing current process
fails:Process.exec sets the current directory when given the :chdir option
fails:Process.exec with a single argument subjects the specified command to shell expansion
fails:Process.exec with a single argument creates an argument array with shell parsing semantics for whitespace
fails:Process.exec (environment variables) sets environment variables in the child environment
fails:Process.exec (environment variables) unsets environment variables whose value is nil
fails:Process.exec (environment variables) coerces environment argument using to_hash
fails:Process.exec (environment variables) unsets other environment variables when given a true :unsetenv_others option
fails:Process.exec with a command array uses the first element as the command name and the second as the argv[0] value
fails:Process.exec with a command array coerces the argument using to_ary
fails:Process.exec with a command array raises an ArgumentError if the Array does not have exactly two elements
fails:Process.exec with an options Hash with Integer option keys maps the key to a file descriptor in the child that inherits the file descriptor from the parent specified by the value
slow:Process.exec raises Errno::ENOENT for an empty string
slow:Process.exec raises Errno::ENOENT for a command which does not exist
slow:Process.exec raises an ArgumentError if the command includes a null byte
33 changes: 33 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/core/VMPrimitiveNodes.java
Original file line number Diff line number Diff line change
@@ -55,6 +55,7 @@
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.builtins.Primitive;
import org.jruby.truffle.builtins.PrimitiveArrayArgumentsNode;
import org.jruby.truffle.core.array.ArrayOperations;
import org.jruby.truffle.core.basicobject.BasicObjectNodes.ReferenceEqualNode;
import org.jruby.truffle.core.cast.NameToJavaStringNode;
import org.jruby.truffle.core.kernel.KernelNodes;
@@ -724,5 +725,37 @@ protected Object writeProgramName(DynamicObject name) {

}

@Primitive(name = "vm_exec", needsSelf = false)
public abstract static class VMSExecNode extends PrimitiveArrayArgumentsNode {

@TruffleBoundary
@Specialization(guards = { "isRubyString(path)", "isRubyArray(args)", "isRubyArray(env)" })
protected Object vmExec(DynamicObject path, DynamicObject args, DynamicObject env) {
final String convertedCommand = StringOperations.decodeUTF8(path).trim();
final String[] convertedCommandLine = convertToJava(args);
final String[] convertedEnv = convertToJava(env);

final int ret = posix().exec(convertedCommand, convertedCommandLine, convertedEnv);

if (ret == -1) {
throw new RaiseException(coreExceptions().errnoError(posix().errno(), this));
}

return null;
}

private String[] convertToJava(DynamicObject array) {
final Object[] javaArray = ArrayOperations.toObjectArray(array);
final String[] ret = new String[javaArray.length];

for (int i = 0; i < javaArray.length; i++) {
ret[i] = StringOperations.decodeUTF8((DynamicObject) javaArray[i]);
}

return ret;
}

}


}
Original file line number Diff line number Diff line change
@@ -38,7 +38,6 @@
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import jnr.constants.platform.Errno;
import org.jcodings.Encoding;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.Layouts;
@@ -111,7 +110,6 @@
import org.jruby.truffle.language.dispatch.DoesRespondDispatchHeadNode;
import org.jruby.truffle.language.dispatch.MissingBehavior;
import org.jruby.truffle.language.dispatch.RubyCallNode;
import org.jruby.truffle.language.globals.ReadGlobalVariableNode;
import org.jruby.truffle.language.globals.ReadGlobalVariableNodeGen;
import org.jruby.truffle.language.loader.CodeLoader;
import org.jruby.truffle.language.loader.RequireNode;
@@ -157,13 +155,10 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ProcessBuilder.Redirect;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static org.jruby.truffle.core.array.ArrayHelpers.getStore;

@CoreClass("Kernel")
public abstract class KernelNodes {
@@ -772,89 +767,6 @@ protected int getCacheLimit() {

}

@CoreMethod(names = "exec", isModuleFunction = true, required = 1, rest = true, unsafe = UnsafeGroup.PROCESSES)
public abstract static class ExecNode extends CoreMethodArrayArgumentsNode {

@Child private CallDispatchHeadNode toHashNode;

@Specialization
public Object exec(VirtualFrame frame, Object command, Object[] args) {
if (TruffleOptions.AOT) {
throw new UnsupportedOperationException("ProcessEnvironment.environment not supported with AOT.");
}

if (toHashNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toHashNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

final String[] commandLine = buildCommandLine(command, args);

final DynamicObject env = coreLibrary().getENV();
final DynamicObject envAsHash = (DynamicObject) toHashNode.call(frame, env, "to_hash");

exec(getContext(), envAsHash, commandLine);

return null;
}

@TruffleBoundary
private String[] buildCommandLine(Object command, Object[] args) {
final List<String> commandLine = new ArrayList<>(1 + args.length);
if (RubyGuards.isRubyArray(command)) {
// For handling: exec([cmdname, argv0], arg1, ...)
// argv0 not yet implemented
final Object[] store = (Object[]) getStore((DynamicObject) command);
commandLine.add(store[0].toString());
} else {
commandLine.add(command.toString());
}
for (int n = 0; n < args.length; n++) {
if (n == args.length - 1 && RubyGuards.isRubyHash(args[n])) {
break;
}
commandLine.add(args[n].toString());
}
final String[] result = new String[commandLine.size()];
return commandLine.toArray(result);
}

@TruffleBoundary
private void exec(RubyContext context, DynamicObject envAsHash, String[] commandLine) {
final ProcessBuilder builder = new ProcessBuilder(commandLine);
builder.inheritIO();

for (KeyValue keyValue : HashOperations.iterableKeyValues(envAsHash)) {
builder.environment().put(keyValue.getKey().toString(), keyValue.getValue().toString());
}

final Process process;

try {
process = builder.start();
} catch (IOException e) {
if (e.getMessage().contains("Permission denied")) {
throw new RaiseException(getContext().getCoreExceptions().errnoError(Errno.EACCES.intValue(), this));
} else if (e.getMessage().contains("No such file or directory")) {
throw new RaiseException(getContext().getCoreExceptions().errnoError(Errno.ENOENT.intValue(), this));
} else {
// TODO(cs): proper Ruby exception
throw new JavaException(e);
}
}

int exitCode = context.getThreadManager().runUntilResult(this, () -> process.waitFor());

/*
* We really do want to just exit here as opposed to throwing a MainExitException and tidying up, as we're
* pretending that we did exec and so replaced this process with a new one.
*/

System.exit(exitCode);
}

}

@CoreMethod(names = "freeze")
public abstract static class KernelFreezeNode extends CoreMethodArrayArgumentsNode {

Original file line number Diff line number Diff line change
@@ -94,6 +94,16 @@ public int dup(int descriptor) {

}

@CoreMethod(names = "dup2", isModuleFunction = true, required = 2, lowerFixnum = { 1, 2 }, unsafe = UnsafeGroup.IO)
public abstract static class Dup2Node extends CoreMethodArrayArgumentsNode {

@Specialization
public int dup2(int oldFd, int newFd) {
return posix().dup2(oldFd, newFd);
}

}

@CoreMethod(names = "fchmod", isModuleFunction = true, required = 2, lowerFixnum = {1, 2}, unsafe = UnsafeGroup.IO)
public abstract static class FchmodNode extends CoreMethodArrayArgumentsNode {

Original file line number Diff line number Diff line change
@@ -71,12 +71,12 @@ public int fchown(int fd, int user, int group) {

@Override
public int exec(String path, String... argv) {
return posix.exec(path, argv);
return posix.execv(path, argv);
}

@Override
public int exec(String path, String[] argv, String[] envp) {
return posix.exec(path, argv, envp);
return posix.execve(path, argv, envp);
}

@Override
5 changes: 5 additions & 0 deletions truffle/src/main/ruby/core/kernel.rb
Original file line number Diff line number Diff line change
@@ -221,6 +221,11 @@ def display(port=$>)
port.write self
end

def exec(*args)
Process.exec(*args)
end
module_function :exec

def exit(code=0)
Process.exit(code)
end
Loading