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

Commits on Feb 19, 2016

  1. Copy the full SHA
    ff6d532 View commit details
  2. Copy the full SHA
    4ee683b View commit details
  3. Make RubySymbol EncodingCabable so unmarshaling can set encoding.

    This makes me a little nervous, in that we're modifying a symbol
    looked up from the global table, and that could cause it to
    overwrite an existing symbol's encoding. However, the way symbol
    encodings are marshaled does not allow us to easily get the
    encoding right away. Will have to experiment and see if there's
    a real issue here.
    headius committed Feb 19, 2016
    Copy the full SHA
    2b90c8f View commit details
  4. Move debug logic for frozen strings out of IR.

    The original logic built and installed the debug info as a series
    of IR instructions. This allowed adding the feature without having
    to modify JIT, but was somewhat heavy in code. This change moves
    the debug check into the logic for FrozenString itself, and more
    efficiently compiles it into bytecode.
    headius committed Feb 19, 2016
    Copy the full SHA
    fe083aa View commit details
  5. There is no 1.9.

    headius committed Feb 19, 2016
    Copy the full SHA
    75f5b50 View commit details
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyException.java
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ private void setBacktrace(IRubyObject obj) {
} else if (obj instanceof RubyString) {
backtrace = RubyArray.newArray(getRuntime(), obj);
} else {
throw getRuntime().newTypeError("backtrace must be Array of String");
throw getRuntime().newTypeError("backtrace must be Array of String or a single String");
}
}

20 changes: 19 additions & 1 deletion core/src/main/java/org/jruby/RubySymbol.java
Original file line number Diff line number Diff line change
@@ -55,24 +55,28 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.FunctionalCachingCallSite;
import org.jruby.runtime.encoding.EncodingCapable;
import org.jruby.runtime.encoding.MarshalEncoding;
import org.jruby.runtime.marshal.UnmarshalStream;
import org.jruby.runtime.opto.OptoFactory;
import org.jruby.util.ByteList;
import org.jruby.util.PerlHash;
import org.jruby.util.SipHashInline;
import org.jruby.util.StringSupport;

import java.lang.ref.WeakReference;
import java.util.concurrent.locks.ReentrantLock;

import static org.jruby.util.StringSupport.CR_7BIT;
import static org.jruby.util.StringSupport.codeLength;
import static org.jruby.util.StringSupport.codePoint;
import static org.jruby.util.StringSupport.codeRangeScan;

/**
* Represents a Ruby symbol (e.g. :bar)
*/
@JRubyClass(name="Symbol")
public class RubySymbol extends RubyObject implements MarshalEncoding, Constantizable {
public class RubySymbol extends RubyObject implements MarshalEncoding, EncodingCapable, Constantizable {
public static final long symbolHashSeedK0 = 5238926673095087190l;

private final String symbol;
@@ -94,6 +98,10 @@ private RubySymbol(Ruby runtime, String internedSymbol, ByteList symbolBytes) {
// assert internedSymbol == internedSymbol.intern() : internedSymbol + " is not interned";

this.symbol = internedSymbol;
if (codeRangeScan(symbolBytes.getEncoding(), symbolBytes) == CR_7BIT) {
symbolBytes = symbolBytes.dup();
symbolBytes.setEncoding(USASCIIEncoding.INSTANCE);
}
this.symbolBytes = symbolBytes;
this.id = runtime.allocSymbolId();

@@ -628,6 +636,16 @@ public static ByteList symbolBytesFromString(Ruby runtime, String internedSymbol
return new ByteList(ByteList.plain(internedSymbol), USASCIIEncoding.INSTANCE, false);
}

@Override
public Encoding getEncoding() {
return symbolBytes.getEncoding();
}

@Override
public void setEncoding(Encoding e) {
symbolBytes.setEncoding(e);
}

public static final class SymbolTable {
static final int DEFAULT_INITIAL_CAPACITY = 1 << 10; // *must* be power of 2!
static final int MAXIMUM_CAPACITY = 1 << 16; // enough for a 64k buckets; if you need more than this, something's wrong
30 changes: 7 additions & 23 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -1013,7 +1013,7 @@ public Operand buildCall(CallNode callNode) {
// Frozen string optimization: check for "string".freeze
if (receiverNode instanceof StrNode && callNode.getName().equals("freeze")) {
StrNode asString = (StrNode) receiverNode;
return new FrozenString(asString.getValue(), asString.getCodeRange());
return new FrozenString(asString.getValue(), asString.getCodeRange(), asString.getPosition().getFile(), asString.getPosition().getLine());
}

// Though you might be tempted to move this build into the CallInstr as:
@@ -3437,27 +3437,11 @@ public Operand buildSplat(SplatNode splatNode) {
public Operand buildStr(StrNode strNode) {
if (strNode instanceof FileNode) return new Filename();

Operand literal = strNode.isFrozen() && !manager.getInstanceConfig().isDebuggingFrozenStringLiteral() ?
new FrozenString(strNode.getValue(), strNode.getCodeRange()) :
new StringLiteral(strNode.getValue(), strNode.getCodeRange());

Operand result;

// This logic might be slightly different than MRI in cases. This is defined as any frozen string literal
// we be able to be debugged. In MRI, they only do this is the iseq mode is set to frozen-string-literal.
// So we might actually debug more instances of frozen string but this way seems more logical.
if (strNode.isFrozen() && manager.getInstanceConfig().isDebuggingFrozenStringLiteral()) {
Variable stringReference = createTemporaryVariable();
addInstr(new CopyInstr(stringReference, literal));
// FIXME: I think we want to hide this and not make user accessible? Seems like it could have utility to be user-exposed though.
// FIXME: Also we are manually dyn-dispatching to freeze and not calling it internally. Maybe also ok?
Operand[] pair = new Operand[] { new FrozenString(strNode.getPosition().getFile()), new Fixnum(strNode.getPosition().getLine()) };
addInstr(new PutFieldInstr(stringReference, RubyString.DEBUG_INFO_FIELD, new Array(pair)));
result = createTemporaryVariable();
addInstr(CallInstr.create(scope, (Variable) result, "freeze", stringReference, NO_ARGS, null));
} else {
result = copyAndReturnValue(literal);
}
Operand literal = strNode.isFrozen() ?
new FrozenString(strNode.getValue(), strNode.getCodeRange(), strNode.getPosition().getFile(), strNode.getPosition().getLine()) :
new StringLiteral(strNode.getValue(), strNode.getCodeRange(), strNode.getPosition().getFile(), strNode.getPosition().getLine());

Operand result = copyAndReturnValue(literal);

return result;
}
@@ -3590,7 +3574,7 @@ public Operand buildWhile(final WhileNode whileNode) {
}

public Operand buildXStr(XStrNode node) {
return addResultInstr(new BacktickInstr(createTemporaryVariable(), new Operand[] { new FrozenString(node.getValue(), node.getCodeRange())}));
return addResultInstr(new BacktickInstr(createTemporaryVariable(), new Operand[] { new FrozenString(node.getValue(), node.getCodeRange(), node.getPosition().getFile(), node.getPosition().getLine())}));
}

public Operand buildYield(YieldNode node) {
37 changes: 25 additions & 12 deletions core/src/main/java/org/jruby/ir/operands/FrozenString.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package org.jruby.ir.operands;

import org.jruby.RubyString;
import org.jruby.ir.IRVisitor;
import org.jruby.ir.persistence.IRReaderDecoder;
import org.jruby.ir.persistence.IRWriterEncoder;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.ir.transformations.inlining.CloneInfo;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ByteList;
import org.jruby.util.StringSupport;

@@ -22,23 +19,27 @@
public class FrozenString extends ImmutableLiteral {
// SSS FIXME: Pick one of bytelist or string, or add internal conversion methods to convert to the default representation

final public ByteList bytelist;
final public String string;
final public int coderange;
public final ByteList bytelist;
public final String string;
public final int coderange;
public final String file;
public final int line;

/**
* Used by persistence and by .freeze optimization
*/
public FrozenString(ByteList byteList, int cr) {
this(internedStringFromByteList(byteList), byteList, cr);
public FrozenString(ByteList byteList, int cr, String file, int line) {
this(internedStringFromByteList(byteList), byteList, cr, file, line);
}

protected FrozenString(String string, ByteList bytelist, int coderange) {
protected FrozenString(String string, ByteList bytelist, int coderange, String file, int line) {
super();

this.bytelist = bytelist;
this.coderange = coderange;
this.string = string;
this.file = file;
this.line = line;
}

/**
@@ -54,6 +55,8 @@ private FrozenString(String string, ByteList byteList) {
this.bytelist = byteList;
this.string = string;
this.coderange = StringSupport.CR_7BIT;
this.file = "<dummy>";
this.line = -1;
}

// If Encoding has an instance of a Charset can it ever raise unsupportedcharsetexception? because this
@@ -103,7 +106,7 @@ public Operand cloneForInlining(CloneInfo ii) {

@Override
public Object createCacheObject(ThreadContext context) {
return context.runtime.freezeAndDedupString(RubyString.newString(context.runtime, bytelist, coderange));
return IRRuntimeHelpers.newFrozenString(context, bytelist, coderange, file, line);
}

@Override
@@ -119,15 +122,25 @@ public String getString() {
return string;
}

public String getFile() {
return file;
}

public int getLine() {
return line;
}

@Override
public void encode(IRWriterEncoder e) {
super.encode(e);
e.encode(bytelist);
e.encode(coderange);
e.encode(file);
e.encode(line);
}

public static FrozenString decode(IRReaderDecoder d) {
return new FrozenString(d.decodeByteList(), d.decodeInt());
return new FrozenString(d.decodeByteList(), d.decodeInt(), d.decodeString(), d.decodeInt());
}

public int getCodeRange() { return coderange; }
8 changes: 4 additions & 4 deletions core/src/main/java/org/jruby/ir/operands/StringLiteral.java
Original file line number Diff line number Diff line change
@@ -28,14 +28,14 @@ public class StringLiteral extends Operand {

public final FrozenString frozenString;

public StringLiteral(ByteList val, int coderange) {
this.frozenString = new FrozenString(val, coderange);
public StringLiteral(ByteList val, int coderange, String file, int line) {
this.frozenString = new FrozenString(val, coderange, file, line);
}

protected StringLiteral(String string, ByteList bytelist, int coderange) {
protected StringLiteral(String string, ByteList bytelist, int coderange, String file, int line) {
super();

this.frozenString = new FrozenString(string, bytelist, coderange);
this.frozenString = new FrozenString(string, bytelist, coderange, file, line);
}

public StringLiteral(String s) {
22 changes: 20 additions & 2 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -1073,8 +1073,8 @@ public static final Type[] typesFromSignature(Signature signature) {
}

@JIT
public static RubyString newFrozenStringFromRaw(Ruby runtime, String str, String encoding, int cr) {
return runtime.freezeAndDedupString(RubyString.newString(runtime, newByteListFromRaw(runtime, str, encoding), cr));
public static RubyString newFrozenStringFromRaw(ThreadContext context, String str, String encoding, int cr, String file, int line) {
return newFrozenString(context, newByteListFromRaw(context.runtime, str, encoding), cr, file, line);
}

@JIT
@@ -1763,4 +1763,22 @@ public static Block prepareBlock(ThreadContext context, IRubyObject self, Dynami

return block;
}

@JIT
public static RubyString newFrozenString(ThreadContext context, ByteList bytelist, int coderange, String file, int line) {
Ruby runtime = context.runtime;

RubyString string = RubyString.newString(runtime, bytelist, coderange);

if (runtime.getInstanceConfig().isDebuggingFrozenStringLiteral()) {
// stuff location info into the string and then freeze it
RubyArray info = (RubyArray) runtime.newArray(runtime.newString(file).freeze(context), runtime.newFixnum(line)).freeze(context);
string.setInternalVariable(RubyString.DEBUG_INFO_FIELD, info);
string.setFrozen(true);
} else {
string = runtime.freezeAndDedupString(string);
}

return string;
}
}
12 changes: 6 additions & 6 deletions core/src/main/java/org/jruby/ir/targets/Bootstrap.java
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ public class Bootstrap {
private static final Logger LOG = LoggerFactory.getLogger("Bootstrap");
static final Lookup LOOKUP = MethodHandles.lookup();

public static CallSite string(Lookup lookup, String name, MethodType type, String value, String encodingName, int cr) {
public static CallSite string(Lookup lookup, String name, MethodType type, String value, String encodingName, int cr, String file, int line) {
Encoding encoding;
EncodingDB.Entry entry = EncodingDB.getEncodings().get(encodingName.getBytes());
if (entry == null) entry = EncodingDB.getAliases().get(encodingName.getBytes());
@@ -74,7 +74,7 @@ public static CallSite string(Lookup lookup, String name, MethodType type, Strin
MutableCallSite site = new MutableCallSite(type);
Binder binder = Binder
.from(RubyString.class, ThreadContext.class)
.insert(0, arrayOf(MutableCallSite.class, ByteList.class, int.class), site, byteList, cr);
.insert(0, arrayOf(MutableCallSite.class, ByteList.class, int.class, String.class, int.class), site, byteList, cr, file, line);
if (name.equals("frozen")) {
site.setTarget(binder.invokeStaticQuiet(lookup, Bootstrap.class, "frozenString"));
} else {
@@ -137,7 +137,7 @@ public static CallSite ivar(Lookup lookup, String name, MethodType type) throws
}

public static Handle string() {
return new Handle(Opcodes.H_INVOKESTATIC, p(Bootstrap.class), "string", sig(CallSite.class, Lookup.class, String.class, MethodType.class, String.class, String.class, int.class));
return new Handle(Opcodes.H_INVOKESTATIC, p(Bootstrap.class), "string", sig(CallSite.class, Lookup.class, String.class, MethodType.class, String.class, String.class, int.class, String.class, int.class));
}

public static Handle bytelist() {
@@ -168,7 +168,7 @@ public static Handle global() {
return new Handle(Opcodes.H_INVOKESTATIC, p(Bootstrap.class), "globalBootstrap", sig(CallSite.class, Lookup.class, String.class, MethodType.class));
}

public static RubyString string(MutableCallSite site, ByteList value, int cr, ThreadContext context) throws Throwable {
public static RubyString string(MutableCallSite site, ByteList value, int cr, String file, int line, ThreadContext context) throws Throwable {
MethodHandle handle = SmartBinder
.from(STRING_SIGNATURE)
.invoke(NEW_STRING_SHARED_HANDLE.apply("byteList", value))
@@ -179,8 +179,8 @@ public static RubyString string(MutableCallSite site, ByteList value, int cr, Th
return RubyString.newStringShared(context.runtime, value, cr);
}

public static RubyString frozenString(MutableCallSite site, ByteList value, int cr, ThreadContext context) throws Throwable {
RubyString frozen = context.runtime.freezeAndDedupString(RubyString.newStringShared(context.runtime, value, cr));
public static RubyString frozenString(MutableCallSite site, ByteList value, int cr, String file, int line, ThreadContext context) throws Throwable {
RubyString frozen = IRRuntimeHelpers.newFrozenString(context, value, cr, file, line);
MethodHandle handle = Binder.from(RubyString.class, ThreadContext.class)
.dropAll()
.constant(frozen);
Original file line number Diff line number Diff line change
@@ -292,7 +292,7 @@ public org.objectweb.asm.Label newLabel() {
*
* @param bl ByteList for the String to push
*/
public abstract void pushFrozenString(ByteList bl, int cr);
public abstract void pushFrozenString(ByteList bl, int cr, String path, int line);

/**
* Stack required: none
Original file line number Diff line number Diff line change
@@ -102,15 +102,17 @@ private String newFieldName(String baseName) {
*
* @param bl ByteList for the String to push
*/
public void pushFrozenString(final ByteList bl, final int cr) {
public void pushFrozenString(final ByteList bl, final int cr, final String file, final int line) {
cacheValuePermanently("fstring", RubyString.class, keyFor("fstring", bl), new Runnable() {
@Override
public void run() {
loadRuntime();
loadContext();
adapter.ldc(bl.toString());
adapter.ldc(bl.getEncoding().toString());
adapter.ldc(cr);
invokeIRHelper("newFrozenStringFromRaw", sig(RubyString.class, Ruby.class, String.class, String.class, int.class));
adapter.ldc(file);
adapter.ldc(line);
invokeIRHelper("newFrozenStringFromRaw", sig(RubyString.class, ThreadContext.class, String.class, String.class, int.class, String.class, int.class));
}
});
}
Original file line number Diff line number Diff line change
@@ -64,9 +64,9 @@ public void pushString(ByteList bl, int cr) {
adapter.invokedynamic("string", sig(RubyString.class, ThreadContext.class), Bootstrap.string(), new String(bl.bytes(), RubyEncoding.ISO), bl.getEncoding().toString(), cr);
}

public void pushFrozenString(ByteList bl, int cr) {
public void pushFrozenString(ByteList bl, int cr, String file, int line) {
loadContext();
adapter.invokedynamic("frozen", sig(RubyString.class, ThreadContext.class), Bootstrap.string(), new String(bl.bytes(), RubyEncoding.ISO), bl.getEncoding().toString(), cr);
adapter.invokedynamic("frozen", sig(RubyString.class, ThreadContext.class), Bootstrap.string(), new String(bl.bytes(), RubyEncoding.ISO), bl.getEncoding().toString(), cr, file, line);
}

public void pushByteList(ByteList bl) {
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -2181,7 +2181,7 @@ public void Fixnum(Fixnum fixnum) {

@Override
public void FrozenString(FrozenString frozen) {
jvmMethod().pushFrozenString(frozen.getByteList(), frozen.getCodeRange());
jvmMethod().pushFrozenString(frozen.getByteList(), frozen.getCodeRange(), frozen.getFile(), frozen.getLine());
}

@Override
Original file line number Diff line number Diff line change
@@ -365,7 +365,7 @@ public void defaultVariablesUnmarshal(IRubyObject object) throws IOException {

IRubyObject key = unmarshalObject(false);

if (runtime.is1_9() && object instanceof EncodingCapable) {
if (object instanceof EncodingCapable) {

EncodingCapable strObj = (EncodingCapable)object;

1 change: 1 addition & 0 deletions test/mri/excludes/TestClass.rb
Original file line number Diff line number Diff line change
@@ -5,5 +5,6 @@
exclude :test_method_redefinition, "needs investigation"
exclude :test_module_function, "needs investigation"
exclude :test_redefine_private_class, "needs investigation"
exclude :test_redefinition_mismatch, "parser issue with Japanese identifiers (#3679)"
exclude :test_singleton_class_message, "error says object instead of Class"
exclude :test_uninitialized, "needs investigation"