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: 70e7046d1be7
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: fa59ae97709a
Choose a head ref
  • 3 commits
  • 3 files changed
  • 1 contributor

Commits on Feb 29, 2016

  1. Copy the full SHA
    a24830b View commit details
  2. Copy the full SHA
    dfee824 View commit details
  3. Copy the full SHA
    fa59ae9 View commit details
1 change: 1 addition & 0 deletions truffle/src/main/java/org/jruby/truffle/RubyLanguage.java
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@
public class RubyLanguage extends TruffleLanguage<RubyContext> {

public static final String MIME_TYPE = "application/x-ruby";
public static final String EXTENSION = ".rb";

private RubyLanguage() {
}
Original file line number Diff line number Diff line change
@@ -89,7 +89,7 @@ public Object execute(ParserContext parserContext,
new Object[]{}));
}

@CompilerDirectives.TruffleBoundary
@TruffleBoundary
public Object inline(Node currentNode, String expression, Object... arguments) {
final Frame frame = Truffle.getRuntime().getCurrentFrame().getFrame(FrameInstance.FrameAccess.MATERIALIZE, true);
return inline(currentNode, frame, expression, arguments);
Original file line number Diff line number Diff line change
@@ -14,12 +14,11 @@
import com.oracle.truffle.api.source.Source;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.RubyLanguage;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.array.ArrayOperations;
import org.jruby.truffle.core.array.ArrayUtils;
import org.jruby.truffle.core.module.ModuleOperations;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.RubyConstant;
import org.jruby.truffle.language.RubyRootNode;
import org.jruby.truffle.language.control.RaiseException;
import org.jruby.truffle.language.methods.DeclarationContext;
@@ -36,149 +35,151 @@ public FeatureLoader(RubyContext context) {
this.context = context;
}

private enum RequireResult {
REQUIRED(true, true),
ALREADY_REQUIRED(true, false),
FAILED(false, false);

public final boolean success;
public final boolean firstRequire;
public boolean require(String feature, Node currentNode) {
final String featurePath = findFeature(feature);

RequireResult(boolean success, boolean firstRequire) {
this.success = success;
this.firstRequire = firstRequire;
if (featurePath == null) {
throw new RaiseException(context.getCoreLibrary().loadErrorCannotLoad(feature, currentNode));
}

return doRequire(featurePath, currentNode);
}

public boolean require(String feature, Node currentNode) {
final RubyConstant dataConstantBefore = ModuleOperations.lookupConstant(context, context.getCoreLibrary().getObjectClass(), "DATA");
private String findFeature(String feature) {
final String currentDirectory = context.getNativePlatform().getPosix().getcwd();

if (feature.startsWith("./")) {
final String cwd = context.getJRubyRuntime().getCurrentDirectory();
feature = cwd + "/" + feature.substring(2);
feature = currentDirectory + "/" + feature.substring(2);
} else if (feature.startsWith("../")) {
final String cwd = context.getJRubyRuntime().getCurrentDirectory();
feature = cwd.substring(0, cwd.lastIndexOf('/')) + "/" + feature.substring(3);
feature = currentDirectory.substring(0, currentDirectory.lastIndexOf('/')) + "/" + feature.substring(3);
}

if (feature.startsWith(SourceLoader.TRUFFLE_SCHEME)
|| feature.startsWith(SourceLoader.JRUBY_SCHEME)
|| new File(feature).isAbsolute()) {
return findFeatureWithAndWithoutExtension(feature);
}

try {
if (isAbsolutePath(feature)) {
// Try as a full path
for (Object pathObject : ArrayOperations.toIterable(context.getCoreLibrary().getLoadPath())) {
final String fileWithinPath = new File(pathObject.toString(), feature).getPath();
final String result = findFeatureWithAndWithoutExtension(fileWithinPath);

final RequireResult result = tryToRequireFileInPath(null, feature, currentNode);
if (result != null) {
return result;
}
}

if (result.success) {
return result.firstRequire;
}
} else {
// Try each load path in turn
return null;
}

for (Object pathObject : ArrayOperations.toIterable(context.getCoreLibrary().getLoadPath())) {
String loadPath = pathObject.toString();
if (!isAbsolutePath(loadPath)) {
loadPath = expandPath(context, loadPath);
}
private String findFeatureWithAndWithoutExtension(String path) {
final String withExtension = findFeatureWithExactPath(path + RubyLanguage.EXTENSION);

final RequireResult result = tryToRequireFileInPath(loadPath, feature, currentNode);
if (withExtension != null) {
return withExtension;
}

if (result.success) {
return result.firstRequire;
}
}
}
final String withoutExtension = findFeatureWithExactPath(path);

throw new RaiseException(context.getCoreLibrary().loadErrorCannotLoad(feature, currentNode));
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (dataConstantBefore == null) {
Layouts.MODULE.getFields(context.getCoreLibrary().getObjectClass()).removeConstant(context, currentNode, "DATA");
} else {
Layouts.MODULE.getFields(context.getCoreLibrary().getObjectClass()).setConstant(context, currentNode, "DATA", dataConstantBefore.getValue());
}
if (withoutExtension != null) {
return withoutExtension;
}

return null;
}

private RequireResult tryToRequireFileInPath(String path, String feature, Node currentNode) throws IOException {
String fullPath = new File(path, feature).getPath();
private String findFeatureWithExactPath(String path) {
if (path.startsWith(SourceLoader.TRUFFLE_SCHEME) || path.startsWith(SourceLoader.JRUBY_SCHEME)) {
return path;
}

final RequireResult firstAttempt = tryToRequireFile(fullPath + ".rb", currentNode);
final File file = new File(path);

if (firstAttempt.success) {
return firstAttempt;
if (!file.isFile()) {
return null;
}

return tryToRequireFile(fullPath, currentNode);
try {
if (file.isAbsolute()) {
return file.getCanonicalPath();
} else {
return new File(context.getNativePlatform().getPosix().getcwd(), file.getPath()).getCanonicalPath();
}
} catch (IOException e) {
return null;
}
}

private RequireResult tryToRequireFile(String path, Node currentNode) throws IOException {
// We expect '/' in various classpath URLs, so normalize Windows file paths to use '/'
path = path.replace('\\', '/');
final DynamicObject loadedFeatures = context.getCoreLibrary().getLoadedFeatures();

final String expandedPath;

if (!(path.startsWith(SourceLoader.TRUFFLE_SCHEME) || path.startsWith(SourceLoader.JRUBY_SCHEME))) {
final File file = new File(path);
private boolean doRequire(String expandedPath, Node currentNode) {
if (isFeatureLoaded(expandedPath)) {
return false;
}

assert file.isAbsolute();
final DynamicObject pathString = StringOperations.createString(context,
StringOperations.encodeRope(expandedPath, UTF8Encoding.INSTANCE));

if (!file.isFile()) {
return RequireResult.FAILED;
}
final Source source;

expandedPath = new File(expandPath(context, path)).getCanonicalPath();
} else {
expandedPath = path;
try {
source = context.getSourceCache().getSource(expandedPath);
} catch (IOException e) {
return false;
}

for (Object loaded : ArrayOperations.toIterable(loadedFeatures)) {
if (loaded.toString().equals(expandedPath)) {
return RequireResult.ALREADY_REQUIRED;
}
}
addToLoadedFeatures(pathString);

// TODO (nirvdrum 15-Jan-15): If we fail to load, we should remove the path from the loaded features because subsequent requires of the same statement may succeed.
final DynamicObject pathString = StringOperations.createString(context, StringOperations.encodeRope(expandedPath, UTF8Encoding.INSTANCE));
ArrayOperations.append(loadedFeatures, pathString);
try {
final RubyRootNode rootNode = context.getCodeLoader().parse(context.getSourceCache().getSource(expandedPath), UTF8Encoding.INSTANCE, ParserContext.TOP_LEVEL, null, true, currentNode);
context.getCodeLoader().execute(ParserContext.TOP_LEVEL, DeclarationContext.TOP_LEVEL, rootNode, null, context.getCoreLibrary().getMainObject());
final RubyRootNode rootNode = context.getCodeLoader().parse(
source,
UTF8Encoding.INSTANCE,
ParserContext.TOP_LEVEL,
null,
true,
currentNode);

context.getCodeLoader().execute(
ParserContext.TOP_LEVEL,
DeclarationContext.TOP_LEVEL,
rootNode, null,
context.getCoreLibrary().getMainObject());
} catch (RaiseException e) {
final Object[] store = (Object[]) Layouts.ARRAY.getStore(loadedFeatures);
final int length = Layouts.ARRAY.getSize(loadedFeatures);
for (int i = length - 1; i >= 0; i--) {
if (store[i] == pathString) {
ArrayUtils.arraycopy(store, i + 1, store, i, length - i - 1);
Layouts.ARRAY.setSize(loadedFeatures, length - 1);
break;
}
}
removeFromLoadedFeatures(pathString);
throw e;
} catch (IOException e) {
return RequireResult.FAILED;
}

return RequireResult.REQUIRED;
return true;
}

private boolean isAbsolutePath(String path) {
return path.startsWith(SourceLoader.TRUFFLE_SCHEME) || path.startsWith(SourceLoader.JRUBY_SCHEME) || new File(path).isAbsolute();
}
private boolean isFeatureLoaded(String feature) {
final DynamicObject loadedFeatures = context.getCoreLibrary().getLoadedFeatures();

for (Object loaded : ArrayOperations.toIterable(loadedFeatures)) {
if (loaded.toString().equals(feature)) {
return true;
}
}

private static String expandPath(RubyContext context, String fileName) {
String dir = new File(fileName).isAbsolute() ? null : context.getJRubyRuntime().getCurrentDirectory();
return expandPath(fileName, dir);
return false;
}

private static String expandPath(String fileName, String dir) {
/*
* TODO(cs): this isn't quite correct - I think we want to collapse .., but we don't want to
* resolve symlinks etc. This might be where we want to start borrowing JRuby's
* implementation, but it looks quite tied to their data structures.
*/
private void addToLoadedFeatures(DynamicObject feature) {
final DynamicObject loadedFeatures = context.getCoreLibrary().getLoadedFeatures();

ArrayOperations.append(loadedFeatures, feature);
}

return org.jruby.RubyFile.canonicalize(new File(dir, fileName).getAbsolutePath());
private void removeFromLoadedFeatures(DynamicObject feature) {
final DynamicObject loadedFeatures = context.getCoreLibrary().getLoadedFeatures();
final Object[] store = (Object[]) Layouts.ARRAY.getStore(loadedFeatures);
final int length = Layouts.ARRAY.getSize(loadedFeatures);

for (int i = length - 1; i >= 0; i--) {
if (store[i] == feature) {
ArrayUtils.arraycopy(store, i + 1, store, i, length - i - 1);
Layouts.ARRAY.setSize(loadedFeatures, length - 1);
break;
}
}
}

}