Skip to content

Commit

Permalink
[Truffle] Use glob from Rubinius.
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisseaton committed Apr 15, 2015
1 parent 67d23f1 commit 3063622
Show file tree
Hide file tree
Showing 22 changed files with 1,268 additions and 140 deletions.
5 changes: 0 additions & 5 deletions spec/truffle/tags/core/file/constants/constants_tags.txt
@@ -1,10 +1,6 @@
fails:File::Constants::APPEND is defined
fails:File::Constants::CREAT is defined
fails:File::Constants::EXCL is defined
fails:File::Constants::FNM_CASEFOLD is defined
fails:File::Constants::FNM_DOTMATCH is defined
fails:File::Constants::FNM_NOESCAPE is defined
fails:File::Constants::FNM_PATHNAME is defined
fails:File::Constants::FNM_SYSCASE is defined
fails:File::Constants::LOCK_EX is defined
fails:File::Constants::LOCK_NB is defined
Expand All @@ -18,4 +14,3 @@ fails:File::Constants::WRONLY is defined
fails:File::Constants::NOCTTY is defined
fails:File::Constants::SYNC is defined
fails(windows):File::Constants::BINARY is defined
fails:File::Constants::FNM_EXTGLOB is defined
1 change: 0 additions & 1 deletion spec/truffle/tags/core/file/constants_tags.txt
@@ -1,4 +1,3 @@
fails:File::Constants matches mode constants
fails:File::Constants lock mode constants
fails:File::Constants File::LOCK_EX
fails:File::Constants File::LOCK_NB
Expand Down
1 change: 0 additions & 1 deletion spec/truffle/tags/core/file/file_tags.txt
@@ -1,4 +1,3 @@
fails:File includes File::Constants
windows:File.file? returns true if the named file exists and is a regular file.
windows:File.file? raises an ArgumentError if not passed one argument
windows:File.file? raises a TypeError if not passed a String type
45 changes: 0 additions & 45 deletions spec/truffle/tags/core/file/fnmatch_tags.txt
@@ -1,68 +1,23 @@
fails:File.fnmatch matches entire strings
fails:File.fnmatch does not match partial strings
fails:File.fnmatch does not support { } patterns by default
fails:File.fnmatch supports some { } patterns when File::FNM_EXTGLOB is passed
fails:File.fnmatch doesn't support some { } patterns even when File::FNM_EXTGLOB is passed
fails:File.fnmatch doesn't match an extra } when File::FNM_EXTGLOB is passed
fails:File.fnmatch matches when both FNM_EXTGLOB and FNM_PATHNAME are passed
fails:File.fnmatch matches a single character for each ? character
fails:File.fnmatch matches zero or more characters for each * character
fails:File.fnmatch matches ranges of characters using bracket expresions (e.g. [a-z])
fails:File.fnmatch matches ranges of characters using bracket expresions, taking case into account
fails:File.fnmatch does not match characters outside of the range of the bracket expresion
fails:File.fnmatch matches ranges of characters using exclusive bracket expresions (e.g. [^t] or [!t])
fails:File.fnmatch matches characters with a case sensitive comparison
fails:File.fnmatch matches characters with case insensitive comparison when flags includes FNM_CASEFOLD
fails:File.fnmatch doesn't match case sensitive characters on platfroms with case sensitive paths, when flags include FNM_SYSCASE
fails:File.fnmatch does not match '/' characters with ? or * when flags includes FNM_PATHNAME
fails:File.fnmatch does not match '/' characters inside bracket expressions when flags includes FNM_PATHNAME
fails:File.fnmatch matches literal ? or * in path when pattern includes \? or \*
fails:File.fnmatch matches literal character (e.g. 'a') in path when pattern includes escaped character (e.g. \a)
fails:File.fnmatch matches '\' characters in path when flags includes FNM_NOESACPE
fails:File.fnmatch escapes special characters inside bracket expression
fails:File.fnmatch does not match leading periods in filenames with wildcards by default
fails:File.fnmatch matches patterns with leading periods to dotfiles by default
fails:File.fnmatch matches leading periods in filenames when flags includes FNM_DOTMATCH
fails:File.fnmatch matches multiple directories with ** and *
fails:File.fnmatch matches multiple directories with ** when flags includes File::FNM_PATHNAME
fails:File.fnmatch returns false if '/' in pattern do not match '/' in path when flags includes FNM_PATHNAME
fails:File.fnmatch returns true if '/' in pattern match '/' in path when flags includes FNM_PATHNAME
fails:File.fnmatch accepts an object that has a #to_path method
fails:File.fnmatch raises a TypeError if the first and second arguments are not string-like
fails:File.fnmatch raises a TypeError if the third argument is not an Integer
fails:File.fnmatch does not raise a TypeError if the third argument can be coerced to an Integer
fails:File.fnmatch? matches entire strings
fails:File.fnmatch? does not match partial strings
fails:File.fnmatch? does not support { } patterns by default
fails:File.fnmatch? supports some { } patterns when File::FNM_EXTGLOB is passed
fails:File.fnmatch? doesn't support some { } patterns even when File::FNM_EXTGLOB is passed
fails:File.fnmatch? doesn't match an extra } when File::FNM_EXTGLOB is passed
fails:File.fnmatch? matches when both FNM_EXTGLOB and FNM_PATHNAME are passed
fails:File.fnmatch? matches a single character for each ? character
fails:File.fnmatch? matches zero or more characters for each * character
fails:File.fnmatch? matches ranges of characters using bracket expresions (e.g. [a-z])
fails:File.fnmatch? matches ranges of characters using bracket expresions, taking case into account
fails:File.fnmatch? does not match characters outside of the range of the bracket expresion
fails:File.fnmatch? matches ranges of characters using exclusive bracket expresions (e.g. [^t] or [!t])
fails:File.fnmatch? matches characters with a case sensitive comparison
fails:File.fnmatch? matches characters with case insensitive comparison when flags includes FNM_CASEFOLD
fails:File.fnmatch? doesn't match case sensitive characters on platfroms with case sensitive paths, when flags include FNM_SYSCASE
fails:File.fnmatch? does not match '/' characters with ? or * when flags includes FNM_PATHNAME
fails:File.fnmatch? does not match '/' characters inside bracket expressions when flags includes FNM_PATHNAME
fails:File.fnmatch? matches literal ? or * in path when pattern includes \? or \*
fails:File.fnmatch? matches literal character (e.g. 'a') in path when pattern includes escaped character (e.g. \a)
fails:File.fnmatch? matches '\' characters in path when flags includes FNM_NOESACPE
fails:File.fnmatch? escapes special characters inside bracket expression
fails:File.fnmatch? does not match leading periods in filenames with wildcards by default
fails:File.fnmatch? matches patterns with leading periods to dotfiles by default
fails:File.fnmatch? matches leading periods in filenames when flags includes FNM_DOTMATCH
fails:File.fnmatch? matches multiple directories with ** and *
fails:File.fnmatch? matches multiple directories with ** when flags includes File::FNM_PATHNAME
fails:File.fnmatch? returns false if '/' in pattern do not match '/' in path when flags includes FNM_PATHNAME
fails:File.fnmatch? returns true if '/' in pattern match '/' in path when flags includes FNM_PATHNAME
fails:File.fnmatch? accepts an object that has a #to_path method
fails:File.fnmatch? raises a TypeError if the first and second arguments are not string-like
fails:File.fnmatch? raises a TypeError if the third argument is not an Integer
fails:File.fnmatch? does not raise a TypeError if the third argument can be coerced to an Integer
fails(windows):File.fnmatch matches case sensitive characters on platfroms with case insensitive paths, when flags include FNM_SYSCASE
fails(windows):File.fnmatch? matches case sensitive characters on platfroms with case insensitive paths, when flags include FNM_SYSCASE
1 change: 0 additions & 1 deletion spec/truffle/tags/core/file/lchown_tags.txt

This file was deleted.

2 changes: 0 additions & 2 deletions spec/truffle/tags/core/file/open_tags.txt
@@ -1,5 +1,4 @@
fails:File.open opens the file (basic case)
fails:File.open opens a file when called with a block
fails:File.open opens with mode string
fails:File.open opens a file with mode num
fails:File.open opens a file with mode and permission as nil
Expand Down Expand Up @@ -51,7 +50,6 @@ fails:File.open opens a file for binary write
fails:File.open opens a file for read-write and truncate the file
fails:File.open opens a file for binary read-write starting at the beginning of the file
fails:File.open opens a file for binary read-write and truncate the file
fails:File.open raises a TypeError if passed a filename that is not a String or Integer type
fails:File.open raises a SystemCallError if passed an invalid Integer type
fails:File.open raises an ArgumentError if passed an invalid string for mode
fails:File.open defaults external_encoding to ASCII-8BIT for binary modes
Expand Down
79 changes: 0 additions & 79 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/DirNodes.java
Expand Up @@ -113,85 +113,6 @@ public boolean exists(RubyString path) {

}

@CoreMethod(names = {"glob", "[]"}, onSingleton = true, required = 1)
public abstract static class GlobNode extends CoreMethodNode {

public GlobNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public GlobNode(GlobNode prev) {
super(prev);
}

@Specialization
public RubyArray glob(RubyString glob) {
return glob(getContext(), glob.toString());
}

@TruffleBoundary
private static RubyArray glob(final RubyContext context, String glob) {
/*
* Globbing is quite complicated. We've implemented a subset of the functionality that
* satisfies MSpec, but it will likely break for anyone else.
*/

final RubyArray array = new RubyArray(context.getCoreLibrary().getArrayClass());
String absoluteGlob;

if (!glob.startsWith("/") && !org.jruby.RubyFile.startsWithDriveLetterOnWindows(glob)) {
absoluteGlob = new File(".", glob).getAbsolutePath();
} else {
absoluteGlob = glob;
}

// Get the first star
final int firstStar = absoluteGlob.indexOf('*');

// If there's no star, it behaves similarly to [glob if File.exist?(glob)].compact
if (firstStar == -1) {
if (new File(glob).exists()) {
array.slowPush(context.makeString(glob));
}
return array;
}

// Walk back from that to the first / before that star

int prefixLength = firstStar;

while (prefixLength > 0 && absoluteGlob.charAt(prefixLength) == File.separatorChar) {
System.out.println(String.format("char: %s; separator: %s", absoluteGlob.charAt(prefixLength), File.separatorChar));
prefixLength--;
}

final String prefix = absoluteGlob.substring(0, prefixLength - 1);

// Glob patterns must always use '/', even on Windows.
final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + absoluteGlob.substring(prefixLength).replace('\\', '/'));

try {
Files.walkFileTree(FileSystems.getDefault().getPath(prefix), new SimpleFileVisitor<Path>() {

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (matcher.matches(file)) {
array.slowPush(context.makeString(RubyFile.expandPath(context, file.toString())));
}

return FileVisitResult.CONTINUE;
}

});
} catch (IOException e) {
throw new RuntimeException(e);
}

return array;
}

}

@CoreMethod(names = "mkdir", needsSelf = false, onSingleton = true, required = 1)
public abstract static class MkdirNode extends CoreMethodNode {

Expand Down
Expand Up @@ -190,6 +190,18 @@ public Object isCompatible(RubySymbol first, RubySymbol second) {
}
}

@TruffleBoundary
@Specialization
public Object isCompatible(RubyString first, RubyEncoding second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(first.getByteList().getEncoding(), second.getEncoding());

if (compatibleEncoding != null) {
return RubyEncoding.getEncoding(compatibleEncoding);
} else {
return nil();
}
}

}

@RubiniusOnly
Expand Down
@@ -0,0 +1,141 @@
/*
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.rubinius;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.object.HiddenKey;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.objectstorage.ReadHeadObjectFieldNode;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNode;
import org.jruby.truffle.runtime.RubyCallStack;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.*;

import java.io.File;

public abstract class DirPrimitiveNodes {

// TODO CS 14-April-15 use a shape, properties and allocator

private static final HiddenKey contentsKey = new HiddenKey("contents");
private static final HiddenKey positionKey = new HiddenKey("position");

@RubiniusPrimitive(name = "dir_open")
public static abstract class DirOpenPrimitiveNode extends RubiniusPrimitiveNode {

@Child private WriteHeadObjectFieldNode writeContentsNode;
@Child private WriteHeadObjectFieldNode writePositionNode;

public DirOpenPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
writeContentsNode = new WriteHeadObjectFieldNode(contentsKey);
writePositionNode = new WriteHeadObjectFieldNode(positionKey);
}

public DirOpenPrimitiveNode(DirOpenPrimitiveNode prev) {
super(prev);
writeContentsNode = prev.writeContentsNode;
writePositionNode = prev.writePositionNode;
}

@CompilerDirectives.TruffleBoundary
@Specialization
public RubyNilClass open(RubyBasicObject dir, RubyString path, RubyNilClass encoding) {
final String[] contents = new File(path.toString()).list();

if (contents == null) {
throw new RaiseException(new RubyException(
getContext().getCoreLibrary().getEnoentClass(),
getContext().makeString("No such file or directory @ dir_initialize - " + path.toString()),
RubyCallStack.getBacktrace(this)));
}

writeContentsNode.execute(dir, contents);
writePositionNode.execute(dir, -2); // -2 for . and then ..

return nil();
}

}

@RubiniusPrimitive(name = "dir_read")
public static abstract class DirReadPrimitiveNode extends RubiniusPrimitiveNode {

@Child private ReadHeadObjectFieldNode readContentsNode;
@Child private ReadHeadObjectFieldNode readPositionNode;
@Child private WriteHeadObjectFieldNode writePositionNode;

public DirReadPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
readContentsNode = new ReadHeadObjectFieldNode(contentsKey);
readPositionNode = new ReadHeadObjectFieldNode(positionKey);
writePositionNode = new WriteHeadObjectFieldNode(positionKey);
}

public DirReadPrimitiveNode(DirReadPrimitiveNode prev) {
super(prev);
readContentsNode = prev.readContentsNode;
readPositionNode = prev.readPositionNode;
writePositionNode = prev.writePositionNode;
}

@CompilerDirectives.TruffleBoundary
@Specialization
public Object read(RubyBasicObject dir) {
final int position;

try {
position = readPositionNode.executeInteger(dir);
} catch (UnexpectedResultException e) {
throw new IllegalStateException();
}

writePositionNode.execute(dir, position + 1);

if (position == -2) {
return getContext().makeString(".");
} else if (position == -1) {
return getContext().makeString("..");
} else {
final String[] contents = (String[]) readContentsNode.execute(dir);

if (position < contents.length) {
return getContext().makeString(contents[position]);
} else {
return nil();
}
}
}

}

@RubiniusPrimitive(name = "dir_close")
public static abstract class DirClosePrimitiveNode extends RubiniusPrimitiveNode {

public DirClosePrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public DirClosePrimitiveNode(DirClosePrimitiveNode prev) {
super(prev);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public RubyNilClass open(RubyBasicObject dir) {
return nil();
}

}

}

0 comments on commit 3063622

Please sign in to comment.