Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Do not persist filename in IR; require it when loading.
Browse files Browse the repository at this point in the history
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
1 parent b394b62 commit c1b87ef
Showing 11 changed files with 80 additions and 21 deletions.
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;
Original file line number Diff line number Diff line change
@@ -6,25 +6,32 @@
package org.jruby.runtime.load;

import org.jruby.Ruby;
import org.jruby.RubyFile;
import org.jruby.ir.IRScope;
import org.jruby.util.JRubyClassLoader;
import org.jruby.util.JRubyFile;
import org.jruby.util.OneShotClassLoader;
import org.objectweb.asm.ClassReader;

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) {
public static IRScope loadScriptFromFile(Ruby runtime, InputStream inStream, File resourcePath, String resourceName, boolean isAbsolute) {
InputStream in = null;
try {
in = new BufferedInputStream(inStream, 8192);
String name = normalizeSeps(resourceName);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[8196];
int read = 0;
@@ -40,10 +47,21 @@ public static IRScope loadScriptFromFile(Ruby runtime, InputStream inStream, Str

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

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()));
}

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) {
e.printStackTrace();

This comment has been minimized.

Copy link
@enebo

enebo Jul 6, 2015

Member

you probably want a hard failure here

// fall through
}

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

0 comments on commit c1b87ef

Please sign in to comment.