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

Commits on Jul 6, 2015

  1. Do not persist filename in IR; require it when loading.

    Fixes #3109
    
    Because compiled scripts may be moved around or loaded from
    locations different than when they were compiled, we must pass
    filename into the decoding process to allow the script to reflect
    the new filename.
    headius committed Jul 6, 2015
    Copy the full SHA
    32ef1f0 View commit details
  2. Copy the full SHA
    bce9d5c View commit details
8 changes: 3 additions & 5 deletions core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
@@ -541,12 +541,10 @@ public void runFromMain(InputStream inputStream, String filename) {

if (filename.endsWith(".class")) {
// we are presumably running a precompiled class; load directly
IRScope script = CompiledScriptLoader.loadScriptFromFile(this, inputStream, filename);
IRScope script = CompiledScriptLoader.loadScriptFromFile(this, inputStream, null, filename, false);
if (script == null) {
throw new MainExitException(1, "error: .class file specified is not a compiled JRuby script");
}
// FIXME: We need to be able to set the actual name for __FILE__ and friends to reflect it properly
// script.setFilename(filename);
runInterpreter(script);
return;
}
@@ -2687,7 +2685,7 @@ public ParseResult parseFile(String file, InputStream in, DynamicScope scope, in

try {
// Get IR from .ir file
return IRReader.load(getIRManager(), new IRReaderStream(getIRManager(), IRFileExpert.getIRPersistedFile(file)));
return IRReader.load(getIRManager(), new IRReaderStream(getIRManager(), IRFileExpert.getIRPersistedFile(file), new ByteList(file.getBytes())));
} catch (IOException e) {
// FIXME: What is something actually throws IOException
return parseFileAndGetAST(in, file, scope, lineNumber, false);
@@ -2708,7 +2706,7 @@ public ParseResult parseFileFromMain(String file, InputStream in, DynamicScope s
if (!RubyInstanceConfig.IR_READING) return parseFileFromMainAndGetAST(in, file, scope);

try {
return IRReader.load(getIRManager(), new IRReaderStream(getIRManager(), IRFileExpert.getIRPersistedFile(file)));
return IRReader.load(getIRManager(), new IRReaderStream(getIRManager(), IRFileExpert.getIRPersistedFile(file), new ByteList(file.getBytes())));
} catch (IOException e) {
System.out.println(e);
e.printStackTrace();
3 changes: 3 additions & 0 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -3298,6 +3298,9 @@ public Operand buildSplat(SplatNode splatNode) {
}

public Operand buildStr(StrNode strNode) {
if (strNode instanceof FileNode) {
return new Filename(strNode.getValue());
}
return copyAndReturnValue(new StringLiteral(strNode.getValue(), strNode.getCodeRange()));
}

30 changes: 30 additions & 0 deletions core/src/main/java/org/jruby/ir/operands/Filename.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.jruby.ir.operands;

import org.jruby.ir.persistence.IRReaderDecoder;
import org.jruby.ir.persistence.IRWriterEncoder;
import org.jruby.util.ByteList;

/**
* Represents the script's __FILE__. Isolated as its own operand because we need to be able to replace it when loading
* persisted IR from a location different than original script.
*/
public class Filename extends StringLiteral {
public Filename(ByteList filename) {
super(OperandType.FILENAME, filename, 0);
}

@Override
public boolean hasKnownValue() {
return false;
}

@Override
public void encode(IRWriterEncoder e) {
// we only do base encoding because filename must be provided while deserializing (#3109)
e.encode(getOperandType().getCoded());
}

public static Filename decode(IRReaderDecoder d) {
return new Filename(d.getFilename());
}
}
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ir/operands/OperandType.java
Original file line number Diff line number Diff line change
@@ -47,6 +47,7 @@ public enum OperandType {
WRAPPED_IR_CLOSURE((byte) 'w'),
FROZEN_STRING((byte) 'z'),
NULL_BLOCK((byte) 'o'),
FILENAME((byte) 'm')
;

private final byte coded;
Original file line number Diff line number Diff line change
@@ -66,4 +66,5 @@ public interface IRReaderDecoder {
public IRScope decodeScope();

public TemporaryVariableType decodeTemporaryVariableType();
public ByteList getFilename();
}
14 changes: 12 additions & 2 deletions core/src/main/java/org/jruby/ir/persistence/IRReaderStream.java
Original file line number Diff line number Diff line change
@@ -44,14 +44,17 @@ public class IRReaderStream implements IRReaderDecoder, IRPersistenceValues {
private IRManager manager;
private final List<IRScope> scopes = new ArrayList<>();
private IRScope currentScope = null; // FIXME: This is not thread-safe and more than a little gross
/** Filename to use for the script */
private final ByteList filename;

public IRReaderStream(IRManager manager, InputStream stream) {
public IRReaderStream(IRManager manager, InputStream stream, ByteList filename) {
ByteBuffer buf = readIntoBuffer(stream);
this.manager = manager;
this.buf = buf;
this.filename = filename;
}

public IRReaderStream(IRManager manager, File file) {
public IRReaderStream(IRManager manager, File file, ByteList filename) {
this.manager = manager;
ByteBuffer buf = null;
try (FileInputStream fis = new FileInputStream(file)){
@@ -61,6 +64,7 @@ public IRReaderStream(IRManager manager, File file) {
}

this.buf = buf;
this.filename = filename;
}

private ByteBuffer readIntoBuffer(InputStream stream) {
@@ -78,6 +82,11 @@ private ByteBuffer readIntoBuffer(InputStream stream) {
return buf;
}

@Override
public ByteList getFilename() {
return filename;
}

@Override
public ByteList decodeByteList() {
return new ByteList(decodeByteArray(), decodeEncoding());
@@ -424,6 +433,7 @@ public Operand decode(OperandType type) {
case BOOLEAN: return org.jruby.ir.operands.Boolean.decode(this);
case CURRENT_SCOPE: return CurrentScope.decode(this);
case DYNAMIC_SYMBOL: return DynamicSymbol.decode(this);
case FILENAME: return Filename.decode(this);
case FIXNUM: return Fixnum.decode(this);
case FLOAT: return org.jruby.ir.operands.Float.decode(this);
case FROZEN_STRING: return FrozenString.decode(this);
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -1400,9 +1400,9 @@ public static RefinedCachingCallSite newRefinedCachingCallSite(String name, Stri
}

@JIT
public static IRScope decodeScopeFromBytes(Ruby runtime, byte[] scopeBytes) {
public static IRScope decodeScopeFromBytes(Ruby runtime, byte[] scopeBytes, String filename) {
try {
return IRReader.load(runtime.getIRManager(), new IRReaderStream(runtime.getIRManager(), new ByteArrayInputStream(scopeBytes)));
return IRReader.load(runtime.getIRManager(), new IRReaderStream(runtime.getIRManager(), new ByteArrayInputStream(scopeBytes), new ByteList(filename.getBytes())));
} catch (IOException ioe) {
// should not happen for bytes
return null;
70 changes: 46 additions & 24 deletions core/src/main/java/org/jruby/runtime/load/CompiledScriptLoader.java
Original file line number Diff line number Diff line change
@@ -13,54 +13,76 @@

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;

import static org.jruby.RubyFile.canonicalize;
import static org.jruby.util.JRubyFile.normalizeSeps;

/**
* Load serialized IR from the .class file requested.
*/
public class CompiledScriptLoader {
public static IRScope loadScriptFromFile(Ruby runtime, InputStream inStream, String resourceName) {
InputStream in = null;
public static IRScope loadScriptFromFile(Ruby runtime, InputStream inStream, File resourcePath, String resourceName, boolean isAbsolute) {
String name = getFilenameFromPathAndName(resourcePath, resourceName, isAbsolute);
try {
in = new BufferedInputStream(inStream, 8192);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[8196];
int read = 0;
while ((read = in.read(buf)) != -1) {
baos.write(buf, 0, read);
}
buf = baos.toByteArray();
JRubyClassLoader jcl = runtime.getJRubyClassLoader();
OneShotClassLoader oscl = new OneShotClassLoader(jcl);

ClassReader cr = new ClassReader(buf);
String className = cr.getClassName().replace('/', '.');

Class clazz = oscl.defineClass(className, buf);
Class clazz = loadCompiledScriptFromClass(runtime, inStream);

try {
Method method = clazz.getMethod("loadIR", Ruby.class);
return (IRScope)method.invoke(null, runtime);
Method method = clazz.getMethod("loadIR", Ruby.class, String.class);
return (IRScope)method.invoke(null, runtime, name);
} catch (Exception e) {
// fall through
if (runtime.getDebug().isTrue()) {
e.printStackTrace();
}
throw runtime.newLoadError(name + " is not compiled Ruby; use java_import to load normal classes");
}

throw runtime.newLoadError("use `java_import' to load normal Java classes: "+className);
} catch (IOException e) {
throw runtime.newIOErrorFromException(e);
} catch (LinkageError le) {
if (runtime.getDebug().isTrue()) {
le.printStackTrace();
}
throw runtime.newLoadError("Linkage error loading compiled script; you may need to recompile '" + resourceName + "': " + le);
throw runtime.newLoadError("Linkage error loading compiled script; you may need to recompile '" + name + "': " + le);
} finally {
try {
in.close();
inStream.close();
} catch (IOException ioe) {
throw runtime.newIOErrorFromException(ioe);
}
}
}

private static Class loadCompiledScriptFromClass(Ruby runtime, InputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[8192];
int read;
while ((read = in.read(buf)) != -1) {
baos.write(buf, 0, read);
}
buf = baos.toByteArray();
JRubyClassLoader jcl = runtime.getJRubyClassLoader();
OneShotClassLoader oscl = new OneShotClassLoader(jcl);

ClassReader cr = new ClassReader(buf);
String className = cr.getClassName().replace('/', '.');

return oscl.defineClass(className, buf);
}

public static String getFilenameFromPathAndName(File resourcePath, String resourceName, boolean isAbsolute) {
String name = normalizeSeps(resourceName);
File path = resourcePath;

if(path != null && !isAbsolute) {
// Note: We use RubyFile's canonicalize rather than Java's,
// because Java's will follow symlinks and result in __FILE__
// being set to the target of the symlink rather than the
// filename provided.
name = normalizeSeps(canonicalize(path.getPath()));
}
return name;
}
}
14 changes: 2 additions & 12 deletions core/src/main/java/org/jruby/runtime/load/ExternalScript.java
Original file line number Diff line number Diff line change
@@ -48,25 +48,15 @@ public void load(Ruby runtime, boolean wrap) {
InputStream in = null;
try {
in = resource.getInputStream();
String name = normalizeSeps(resource.getName());
String name = resource.getName();

if (runtime.getInstanceConfig().getCompileMode().shouldPrecompileAll()) {
runtime.compileAndLoadFile(name, in, wrap);
} else {
java.io.File path = resource.getPath();

if(path != null && !resource.isAbsolute()) {
// Note: We use RubyFile's canonicalize rather than Java's,
// because Java's will follow symlinks and result in __FILE__
// being set to the target of the symlink rather than the
// filename provided.
name = normalizeSeps(canonicalize(path.getPath()));
}
name = CompiledScriptLoader.getFilenameFromPathAndName(resource.getPath(), name, resource.isAbsolute());

runtime.loadFile(name, new LoadServiceResourceInputStream(in), wrap);
}


} catch (IOException e) {
throw runtime.newIOErrorFromException(e);
} finally {
Original file line number Diff line number Diff line change
@@ -41,14 +41,12 @@ public JavaCompiledScript(LoadServiceResource resource) {

public void load(Ruby runtime, boolean wrap) {
try {
IRScope script = CompiledScriptLoader.loadScriptFromFile(runtime, resource.getInputStream(), resource.getName());
IRScope script = CompiledScriptLoader.loadScriptFromFile(runtime, resource.getInputStream(), resource.getPath(), resource.getName(), resource.isAbsolute());
if (script == null) {
// we're depending on the side effect of the load, which loads the class but does not turn it into a script
// I don't like it, but until we restructure the code a bit more, we'll need to quietly let it by here.
return;
}
// FIXME: We need to be able to set the actual name for __FILE__ and friends to reflect it properly (#3109)
// script.setFilename(resource.getName());
runtime.loadScope(script, wrap);
} catch (IOException e) {
throw runtime.newIOErrorFromException(e);
Original file line number Diff line number Diff line change
@@ -263,14 +263,12 @@ public void load(Ruby runtime, boolean wrap) {
InputStream is = null;
try {
is = new BufferedInputStream(resource.inputStream(), 32768);
IRScope script = CompiledScriptLoader.loadScriptFromFile(runtime, is, searchName);
IRScope script = CompiledScriptLoader.loadScriptFromFile(runtime, is, null, scriptName, false);

// Depending on the side-effect of the load, which loads the class but does not turn it into a script.
// I don't like it, but until we restructure the code a bit more, we'll need to quietly let it by here.
if (script == null) return;

// FIXME: We need to be able to set the actual name for __FILE__ and friends to reflect it properly (#3109)
// script.setFilename(scriptName);

runtime.loadScope(script, wrap);
} catch(IOException e) {
throw runtime.newLoadError("no such file to load -- " + searchName, searchName);
8 changes: 5 additions & 3 deletions lib/ruby/stdlib/jruby/compiler.rb
Original file line number Diff line number Diff line change
@@ -223,7 +223,8 @@ def compile_files_with_options(filenames, options = default_options)

main.ldc("ISO-8859-1")
main.invokevirtual("java/lang/String", "getBytes", "(Ljava/lang/String;)[B")
main.invokestatic("org/jruby/ir/runtime/IRRuntimeHelpers", "decodeScopeFromBytes", "(Lorg/jruby/Ruby;[B)Lorg/jruby/ir/IRScope;")
main.ldc(filename) # TODO: can we determine actual path to this class?
main.invokestatic("org/jruby/ir/runtime/IRRuntimeHelpers", "decodeScopeFromBytes", "(Lorg/jruby/Ruby;[BLjava/lang/String;)Lorg/jruby/ir/IRScope;")
main.invokevirtual("org/jruby/Ruby", "runInterpreter", "(Lorg/jruby/ParseResult;)Lorg/jruby/runtime/builtin/IRubyObject;")
main.voidreturn
main.end
@@ -232,7 +233,7 @@ def compile_files_with_options(filenames, options = default_options)
cls,
Opcodes::ACC_PUBLIC | Opcodes::ACC_STATIC,
"loadIR",
"(Lorg/jruby/Ruby;)Lorg/jruby/ir/IRScope;",
"(Lorg/jruby/Ruby;Ljava/lang/String;)Lorg/jruby/ir/IRScope;",
nil,
nil)
loadIR.start
@@ -242,7 +243,8 @@ def compile_files_with_options(filenames, options = default_options)

loadIR.ldc("ISO-8859-1")
loadIR.invokevirtual("java/lang/String", "getBytes", "(Ljava/lang/String;)[B")
loadIR.invokestatic("org/jruby/ir/runtime/IRRuntimeHelpers", "decodeScopeFromBytes", "(Lorg/jruby/Ruby;[B)Lorg/jruby/ir/IRScope;")
loadIR.aload(1)
loadIR.invokestatic("org/jruby/ir/runtime/IRRuntimeHelpers", "decodeScopeFromBytes", "(Lorg/jruby/Ruby;[BLjava/lang/String;)Lorg/jruby/ir/IRScope;")
loadIR.areturn
loadIR.end