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

Commits on Jun 23, 2016

  1. Copy the full SHA
    237f3e8 View commit details
  2. Copy the full SHA
    34c246d View commit details
  3. Copy the full SHA
    9e6aa8c View commit details
6 changes: 5 additions & 1 deletion test/truffle/compiler/pe/core/encoding_pe.rb
Original file line number Diff line number Diff line change
@@ -10,4 +10,8 @@
example "Encoding::UTF_16BE.ascii_compatible?", false

example "Encoding::ISO_2022_JP.dummy?", true
example "Encoding::UTF_8.dummy?", false
example "Encoding::UTF_8.dummy?", false

example "Encoding.compatible?('abc', 'def')", Encoding::UTF_8
example "Encoding.compatible?(Encoding::UTF_8, Encoding::US_ASCII)", Encoding::UTF_8
example "Encoding.compatible?(Encoding::UTF_8, Encoding::ASCII_8BIT)", nil
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.core.cast;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import org.jcodings.Encoding;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.RubyNode;

/**
* Take a Ruby object that has an encoding and extracts the Java-level encoding object.
*/
@NodeChild(value = "value", type = RubyNode.class)
public abstract class ToEncodingNode extends RubyNode {

public static ToEncodingNode create() {
return ToEncodingNodeGen.create(null);
}

public abstract Encoding executeToEncoding(Object value);

@Specialization(guards = "isRubyString(value)")
public Encoding stringToEncoding(DynamicObject value) {
return StringOperations.encoding(value);
}

@Specialization(guards = "isRubySymbol(value)")
public Encoding symbolToEncoding(DynamicObject value) {
return Layouts.SYMBOL.getRope(value).getEncoding();
}

@Specialization(guards = "isRubyRegexp(value)")
public Encoding regexpToEncoding(DynamicObject value) {
return Layouts.REGEXP.getRegex(value).getEncoding();
}

@Specialization(guards = "isRubyEncoding(value)")
public Encoding rubyEncodingToEncoding(DynamicObject value) {
return Layouts.ENCODING.getEncoding(value);
}

}
172 changes: 61 additions & 111 deletions truffle/src/main/java/org/jruby/truffle/core/encoding/EncodingNodes.java
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
import org.jcodings.Encoding;
import org.jcodings.EncodingDB;
import org.jcodings.specific.ASCIIEncoding;
import org.jcodings.specific.USASCIIEncoding;
import org.jcodings.specific.UTF8Encoding;
import org.jcodings.util.CaseInsensitiveBytesHash;
import org.jcodings.util.Hash;
@@ -33,6 +34,8 @@
import org.jruby.truffle.builtins.Primitive;
import org.jruby.truffle.builtins.PrimitiveArrayArgumentsNode;
import org.jruby.truffle.builtins.UnaryCoreMethodNode;
import org.jruby.truffle.core.cast.ToEncodingNode;
import org.jruby.truffle.core.cast.ToEncodingNodeGen;
import org.jruby.truffle.core.cast.ToStrNode;
import org.jruby.truffle.core.cast.ToStrNodeGen;
import org.jruby.truffle.core.rope.CodeRange;
@@ -77,23 +80,22 @@ protected static boolean isAsciiCompatible(DynamicObject encoding) {
@CoreMethod(names = "compatible?", needsSelf = false, onSingleton = true, required = 2)
public abstract static class CompatibleQueryNode extends CoreMethodArrayArgumentsNode {

@Child private ToEncodingNode toEncodingNode;

@Specialization(guards = {
"isRubyString(first)",
"isRubyString(second)",
"bothAreStrings(first, second)",
"firstEncoding == secondEncoding",
"extractEncoding(first) == firstEncoding",
"extractEncoding(second) == secondEncoding"
}, limit = "getCacheLimit()")
public DynamicObject isCompatibleStringStringCached(DynamicObject first, DynamicObject second,
@Cached("extractEncoding(first)") Encoding firstEncoding,
@Cached("extractEncoding(second)") Encoding secondEncoding,
@Cached("getEncoding(first)") Encoding firstEncoding,
@Cached("getEncoding(second)") Encoding secondEncoding,
@Cached("isCompatibleStringStringUncached(first, second)") DynamicObject rubyEncoding) {
return rubyEncoding;
}

@Specialization(guards = {
"isRubyString(first)", "isRubyString(second)"
}, contains = "isCompatibleStringStringCached")
@Specialization(guards = "bothAreStrings(first, second)", contains = "isCompatibleStringStringCached")
public DynamicObject isCompatibleStringStringUncached(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = compatibleEncodingForStrings(first, second);

@@ -104,114 +106,26 @@ public DynamicObject isCompatibleStringStringUncached(DynamicObject first, Dynam
}
}

@TruffleBoundary
@Specialization(guards = {"isRubyEncoding(first)", "isRubyEncoding(second)"})
public Object isCompatibleEncodingEncoding(DynamicObject first, DynamicObject second) {
final Encoding firstEncoding = EncodingOperations.getEncoding(first);
final Encoding secondEncoding = EncodingOperations.getEncoding(second);
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(firstEncoding, secondEncoding);

if (compatibleEncoding != null) {
return getContext().getEncodingManager().getRubyEncoding(compatibleEncoding);
} else {
return nil();
}
}

@TruffleBoundary
@Specialization(guards = {"isRubyString(first)", "isRubyRegexp(second)"})
public Object isCompatibleStringRegexp(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.STRING.getRope(first).getEncoding(), Layouts.REGEXP.getRegex(second).getEncoding());

if (compatibleEncoding != null) {
return getContext().getEncodingManager().getRubyEncoding(compatibleEncoding);
} else {
return nil();
}
}

@TruffleBoundary
@Specialization(guards = {"isRubyRegexp(first)", "isRubyString(second)"})
public Object isCompatibleRegexpString(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.REGEXP.getRegex(first).getEncoding(), Layouts.STRING.getRope(second).getEncoding());

if (compatibleEncoding != null) {
return getContext().getEncodingManager().getRubyEncoding(compatibleEncoding);
} else {
return nil();
}
}

@TruffleBoundary
@Specialization(guards = {"isRubyRegexp(first)", "isRubyRegexp(second)"})
public Object isCompatibleRegexpRegexp(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.REGEXP.getRegex(first).getEncoding(), Layouts.REGEXP.getRegex(second).getEncoding());

if (compatibleEncoding != null) {
return getContext().getEncodingManager().getRubyEncoding(compatibleEncoding);
} else {
return nil();
}
}

@TruffleBoundary
@Specialization(guards = {"isRubyRegexp(first)", "isRubySymbol(second)"})
public Object isCompatibleRegexpSymbol(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.REGEXP.getRegex(first).getEncoding(), Layouts.SYMBOL.getRope(second).getEncoding());

if (compatibleEncoding != null) {
return getContext().getEncodingManager().getRubyEncoding(compatibleEncoding);
} else {
return nil();
}
}

@TruffleBoundary
@Specialization(guards = {"isRubySymbol(first)", "isRubyRegexp(second)"})
public Object isCompatibleSymbolRegexp(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.SYMBOL.getRope(first).getEncoding(), Layouts.REGEXP.getRegex(second).getEncoding());

if (compatibleEncoding != null) {
return getContext().getEncodingManager().getRubyEncoding(compatibleEncoding);
} else {
return nil();
}
}

@TruffleBoundary
@Specialization(guards = {"isRubyString(first)", "isRubySymbol(second)"})
public Object isCompatibleStringSymbol(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = compatibleEncodingForRopes(StringOperations.rope(first), Layouts.SYMBOL.getRope(second));
// TODO (nirvdrum 22-Jun-16): Reorder these guards so the cheap check is first after the new Truffle DSL generator is live -- the current one has a bug that's mitigated by reordering the guards.
@Specialization(guards = {
"getEncoding(first) == firstEncoding",
"getEncoding(second) == secondEncoding",
"!bothAreStrings(first, second)"
}, limit = "getCacheLimit()")
public DynamicObject isCompatibleEncodingCached(DynamicObject first, DynamicObject second,
@Cached("getEncoding(first)") Encoding firstEncoding,
@Cached("getEncoding(second)") Encoding secondEncoding,
@Cached("getCompatibleEncoding(getContext(), firstEncoding, secondEncoding)") DynamicObject result) {

if (compatibleEncoding != null) {
return getContext().getEncodingManager().getRubyEncoding(compatibleEncoding);
} else {
return nil();
}
return result;
}

@TruffleBoundary
@Specialization(guards = {"isRubySymbol(first)", "isRubySymbol(second)"})
public Object isCompatibleSymbolSymbol(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = compatibleEncodingForRopes(Layouts.SYMBOL.getRope(first), Layouts.SYMBOL.getRope(second));
@Specialization(guards = "!bothAreStrings(first, second)", contains = "isCompatibleEncodingCached")
public DynamicObject isCompatibleEncodingUncached(DynamicObject first, DynamicObject second) {
final Encoding firstEncoding = getEncoding(first);
final Encoding secondEncoding = getEncoding(second);

if (compatibleEncoding != null) {
return getContext().getEncodingManager().getRubyEncoding(compatibleEncoding);
} else {
return nil();
}
}

@TruffleBoundary
@Specialization(guards = {"isRubyString(first)", "isRubyEncoding(second)"})
public Object isCompatibleStringEncoding(DynamicObject first, DynamicObject second) {
final Encoding compatibleEncoding = org.jruby.RubyEncoding.areCompatible(Layouts.STRING.getRope(first).getEncoding(), EncodingOperations.getEncoding(second));

if (compatibleEncoding != null) {
return getContext().getEncodingManager().getRubyEncoding(compatibleEncoding);
} else {
return nil();
}
return getCompatibleEncoding(getContext(), firstEncoding, secondEncoding);
}

public static Encoding compatibleEncodingForStrings(DynamicObject first, DynamicObject second) {
@@ -253,6 +167,29 @@ public static Encoding compatibleEncodingForRopes(Rope firstRope, Rope secondRop
return null;
}

@TruffleBoundary
public static Encoding areCompatible(Encoding enc1, Encoding enc2) {
if (enc1 == null || enc2 == null) return null;
if (enc1 == enc2) return enc1;

if (!enc1.isAsciiCompatible() || !enc2.isAsciiCompatible()) return null;

if (enc2 instanceof USASCIIEncoding) return enc1;
if (enc1 instanceof USASCIIEncoding) return enc2;

return null;
}

public static DynamicObject getCompatibleEncoding(RubyContext context, Encoding first, Encoding second) {
final Encoding compatibleEncoding = areCompatible(first, second);

if (compatibleEncoding != null) {
return context.getEncodingManager().getRubyEncoding(compatibleEncoding);
} else {
return context.getCoreLibrary().getNilObject();
}
}

@TruffleBoundary
private static boolean isAsciiOnly(Rope rope) {
return rope.getEncoding().isAsciiCompatible() && rope.getCodeRange() == CodeRange.CR_7BIT;
@@ -266,6 +203,19 @@ protected Encoding extractEncoding(DynamicObject string) {
return null;
}

protected static boolean bothAreStrings(DynamicObject first, DynamicObject second) {
return RubyGuards.isRubyString(first) && RubyGuards.isRubyString(second);
}

protected Encoding getEncoding(DynamicObject value) {
if (toEncodingNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
toEncodingNode = insert(ToEncodingNode.create());
}

return toEncodingNode.executeToEncoding(value);
}

protected int getCacheLimit() {
return getContext().getOptions().ENCODING_COMPATIBILE_QUERY_CACHE;
}