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

Commits on Jan 26, 2016

  1. Invert StringLitera/FrozenString relationship.

    StringLiteral now aggregates a FrozenString, which is an
    ImmutableLiteral caching its deduplicated result. StringLiteral's
    logic is just to dup that string into a mutable form.
    
    This is part of work on #3491.
    headius committed Jan 26, 2016
    Copy the full SHA
    51742b9 View commit details
  2. DStr components can be fstrings.

    See #3491.
    headius committed Jan 26, 2016
    Copy the full SHA
    8425569 View commit details
  3. Copy the full SHA
    ae62abc View commit details
  4. Copy the full SHA
    3d81301 View commit details
15 changes: 13 additions & 2 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -1103,7 +1103,14 @@ public Operand buildCase(CaseNode caseNode) {
v2 = build(whenNode.getExpressionNodes());
}
} else {
addInstr(new EQQInstr(eqqResult, build(whenNode.getExpressionNodes()), value));
Operand expression = build(whenNode.getExpressionNodes());

// use frozen string for direct literal strings in `when`
if (expression instanceof StringLiteral) {
expression = ((StringLiteral) expression).frozenString;
}

addInstr(new EQQInstr(eqqResult, expression, value));
v1 = eqqResult;
v2 = manager.getTrue();
}
@@ -2148,6 +2155,10 @@ public Operand buildDot(final DotNode dotNode) {
private Operand dynamicPiece(Node pieceNode) {
Operand piece = build(pieceNode);

if (piece instanceof StringLiteral) {
piece = ((StringLiteral)piece).frozenString;
}

return piece == null ? manager.getNil() : piece;
}

@@ -3502,7 +3513,7 @@ public Operand buildWhile(final WhileNode whileNode) {
}

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

public Operand buildYield(YieldNode node) {
19 changes: 12 additions & 7 deletions core/src/main/java/org/jruby/ir/instructions/BacktickInstr.java
Original file line number Diff line number Diff line change
@@ -45,16 +45,21 @@ public static BacktickInstr decode(IRReaderDecoder d) {

@Override
public Object interpret(ThreadContext context, StaticScope currScope, DynamicScope currDynScope, IRubyObject self, Object[] temp) {
RubyString newString = context.runtime.newString();
RubyString xstr;

for (Operand p: getOperands()) {
RubyBasicObject piece = (RubyBasicObject) p.retrieve(context, self, currScope, currDynScope, temp);
newString.append19((piece instanceof RubyString) ? (RubyString) piece : piece.to_s());
}
if (getOperands().length == 1) {
xstr = (RubyString) getPieces()[0].retrieve(context, self, currScope, currDynScope, temp);
} else {
xstr = context.runtime.newString();
for (Operand p : getOperands()) {
RubyBasicObject piece = (RubyBasicObject) p.retrieve(context, self, currScope, currDynScope, temp);
xstr.append19((piece instanceof RubyString) ? (RubyString) piece : piece.to_s());
}

newString.setFrozen(true);
xstr.setFrozen(true);
}

return self.callMethod(context, "`", newString);
return self.callMethod(context, "`", xstr);
}

@Override
Original file line number Diff line number Diff line change
@@ -43,7 +43,7 @@ public Instr clone(CloneInfo ii) {
}

public boolean isSameEncodingAndCodeRange(RubyString str, StringLiteral newStr) {
return newStr.bytelist.getEncoding() == encoding && newStr.getCodeRange() == str.getCodeRange();
return newStr.getByteList().getEncoding() == encoding && newStr.getCodeRange() == str.getCodeRange();
}

@Override
@@ -64,7 +64,7 @@ public Object interpret(ThreadContext context, StaticScope currScope, DynamicSco
RubyString str = RubyString.newStringShared(context.runtime, bytes, StringSupport.CR_7BIT);
for (Operand p : getOperands()) {
if ((p instanceof StringLiteral) && (isSameEncodingAndCodeRange(str, (StringLiteral)p))) {
str.getByteList().append(((StringLiteral)p).bytelist);
str.getByteList().append(((StringLiteral)p).getByteList());
str.setCodeRange(((StringLiteral)p).getCodeRange());
} else {
IRubyObject pval = (IRubyObject)p.retrieve(context, self, currScope, currDynScope, temp);
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ir/instructions/CallBase.java
Original file line number Diff line number Diff line change
@@ -260,7 +260,7 @@ private boolean computeEvalFlag() {
Operand meth = getArg1();
if (!(meth instanceof StringLiteral)) return true; // We don't know

String name = ((StringLiteral) meth).string;
String name = ((StringLiteral) meth).getString();
// FIXME: ENEBO - Half of these are name and half mname?
return name.equals("call") || name.equals("eval") || mname.equals("module_eval") ||
mname.equals("class_eval") || mname.equals("instance_eval") || name.equals("send") ||
104 changes: 87 additions & 17 deletions core/src/main/java/org/jruby/ir/operands/FrozenString.java
Original file line number Diff line number Diff line change
@@ -3,44 +3,87 @@
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.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;

import java.nio.charset.UnsupportedCharsetException;
import java.util.List;

/**
* Represents a literal string value.
*
* This is not an immutable literal because I can gsub!,
* for example, and modify the contents of the string.
* This is not like a Java string.
*
* TODO: This really should be ImmutableLiteral so it can constant propagate, etc.
* Represents a frozen string value.
*/
public class FrozenString extends StringLiteral {
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;

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

@Override
public OperandType getOperandType() {
return OperandType.FROZEN_STRING;
protected FrozenString(String string, ByteList bytelist, int coderange) {
super();

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

/**
* IRBuild.buildGetDefinition returns a frozen string and this is for all intern'd Java strings.
*/
public FrozenString(String s) {
super(s);
this(s, ByteList.create(s));
}

private FrozenString(String string, ByteList byteList) {
super();

this.bytelist = byteList;
this.string = string;
this.coderange = StringSupport.CR_7BIT;
}

// If Encoding has an instance of a Charset can it ever raise unsupportedcharsetexception? because this
// helper called copes with charset == null...
private static String internedStringFromByteList(ByteList val) {
try {
return Helpers.byteListToString(val).intern();
} catch (UnsupportedCharsetException e) {
return val.toString().intern();
}
}

@Override
public Object retrieve(ThreadContext context, IRubyObject self, StaticScope currScope, DynamicScope currDynScope, Object[] temp) {
return context.runtime.freezeAndDedupString(RubyString.newString(context.runtime, bytelist, coderange));
public OperandType getOperandType() {
return OperandType.FROZEN_STRING;
}

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

@Override
public void addUsedVariables(List<Variable> l) {
/* Do nothing */
}

@Override
public int hashCode() {
return bytelist.hashCode();
}

@Override
@@ -50,15 +93,42 @@ public boolean equals(Object other) {

@Override
public String toString() {
return "frozen:\"" + bytelist + "\"";
return "frozen:\"" + string + "\"";
}

@Override
public Operand cloneForInlining(CloneInfo ii) {
return this;
}

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

@Override
public void visit(IRVisitor visitor) {
visitor.FrozenString(this);
}

public ByteList getByteList() {
return bytelist;
}

public String getString() {
return string;
}

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

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

public int getCodeRange() { return coderange; }
}
57 changes: 19 additions & 38 deletions core/src/main/java/org/jruby/ir/operands/StringLiteral.java
Original file line number Diff line number Diff line change
@@ -26,45 +26,25 @@
public class StringLiteral extends Operand {
public static final StringLiteral EMPTY_STRING = new StringLiteral("");

// 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 FrozenString frozenString;

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

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

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

// If Encoding has an instance of a Charset can it ever raise unsupportedcharsetexception? because this
// helper called copes with charset == null...
private static String internedStringFromByteList(ByteList val) {
try {
return Helpers.byteListToString(val).intern();
} catch (UnsupportedCharsetException e) {
return val.toString().intern();
}
this.frozenString = new FrozenString(string, bytelist, coderange);
}

public StringLiteral(String s) {
this(s, ByteList.create(s));
this.frozenString = new FrozenString(s);
}

private StringLiteral(String string, ByteList byteList) {
super();

this.bytelist = byteList;
this.string = string;
this.coderange = StringSupport.CR_7BIT;
}
private StringLiteral(FrozenString frozenString) {
this.frozenString = frozenString;
}

@Override
public OperandType getOperandType() {
@@ -83,17 +63,17 @@ public void addUsedVariables(List<Variable> l) {

@Override
public int hashCode() {
return bytelist.hashCode();
return frozenString.hashCode();
}

@Override
public boolean equals(Object other) {
return other instanceof StringLiteral && bytelist.equals(((StringLiteral) other).bytelist) && coderange == ((StringLiteral) other).coderange;
return other instanceof StringLiteral && frozenString.equals(((StringLiteral) other).frozenString);
}

@Override
public String toString() {
return "\"" + string + "\"";
return "strdup(" + frozenString.toString() + ")";
}

@Override
@@ -103,8 +83,8 @@ public Operand cloneForInlining(CloneInfo ii) {

@Override
public Object retrieve(ThreadContext context, IRubyObject self, StaticScope currScope, DynamicScope currDynScope, Object[] temp) {
// SSS FIXME: AST interpreter passes in a coderange argument.
return RubyString.newStringShared(context.runtime, bytelist, coderange);
RubyString string = (RubyString) frozenString.retrieve(context, self, currScope, currDynScope, temp);
return string.strDup(context.runtime);
}

@Override
@@ -113,23 +93,24 @@ public void visit(IRVisitor visitor) {
}

public ByteList getByteList() {
return bytelist;
return frozenString.getByteList();
}

public String getString() {
return string;
return frozenString.getString();
}

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

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

public int getCodeRange() { return coderange; }
public int getCodeRange() {
return frozenString.getCodeRange();
}
}