Skip to content

Commit

Permalink
Showing 54 changed files with 1,651 additions and 1,090 deletions.
17 changes: 9 additions & 8 deletions core/src/main/java/org/jruby/Main.java
Original file line number Diff line number Diff line change
@@ -103,7 +103,7 @@ private Main(boolean hardExit) {
}

private static List<String> getDotfileDirectories() {
ArrayList<String> searchList = new ArrayList<String>();
final ArrayList<String> searchList = new ArrayList<>(4);
for (String homeProp : new String[] {"user.dir", "user.home"}) {
String home = SafePropertyAccessor.getProperty(homeProp);
if (home != null) searchList.add(home);
@@ -124,8 +124,11 @@ private static List<String> getDotfileDirectories() {
}

public static void processDotfile() {
final StringBuilder path = new StringBuilder();
for (String home : getDotfileDirectories()) {
File dotfile = new File(home + "/.jrubyrc");
path.setLength(0);
path.append(home).append("/.jrubyrc");
final File dotfile = new File(path.toString());
if (dotfile.exists()) loadJRubyProperties(dotfile);
}
}
@@ -144,14 +147,11 @@ private static void loadJRubyProperties(File dotfile) {
sysProps.put("jruby." + entry.getKey(), entry.getValue());
}
}
catch (IOException ioe) {
if (LOG.isDebugEnabled()) LOG.debug("exception loading " + dotfile, ioe);
}
catch (SecurityException se) {
if (LOG.isDebugEnabled()) LOG.debug("exception loading " + dotfile, se);
catch (IOException|SecurityException ex) {
if (LOG.isDebugEnabled()) LOG.debug("exception loading properties from: " + dotfile, ex);
}
finally {
if (fis != null) try {fis.close();} catch (Exception e) {}
if (fis != null) try { fis.close(); } catch (Exception e) {}
}
}

@@ -493,6 +493,7 @@ private void doPrintProperties() {
private void doPrintUsage(boolean force) {
if (config.getShouldPrintUsage() || force) {
config.getOutput().print(OutputStrings.getBasicUsageHelp());
config.getOutput().print(OutputStrings.getFeaturesHelp());
}
}

32 changes: 0 additions & 32 deletions core/src/main/java/org/jruby/RubyEncoding.java
Original file line number Diff line number Diff line change
@@ -166,38 +166,6 @@ public static Encoding areCompatible(IRubyObject obj1, IRubyObject obj2) {
return null;
}

public static Encoding areCompatible(CodeRangeable obj1, CodeRangeable obj2) {
Encoding enc1 = obj1.getByteList().getEncoding();
Encoding enc2 = obj2.getByteList().getEncoding();

if (enc1 == null || enc2 == null) return null;
if (enc1 == enc2) return enc1;

if (obj2.getByteList().getRealSize() == 0) return enc1;
if (obj1.getByteList().getRealSize() == 0) {
return enc1.isAsciiCompatible() && StringSupport.isAsciiOnly(obj2) ? enc1 : enc2;
}

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

int cr1 = obj1.scanForCodeRange();
int cr2 = obj2.scanForCodeRange();

return areCompatible(enc1, cr1, enc2, cr2);
}

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

// last block in rb_enc_compatible
public static Encoding areCompatible(Encoding enc1, int cr1, Encoding enc2, int cr2) {
if (cr1 != cr2) {
58 changes: 31 additions & 27 deletions core/src/main/java/org/jruby/ir/passes/CompilerPass.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package org.jruby.ir.passes;

import org.jruby.ir.IRScope;
import org.jruby.util.StringSupport;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
@@ -21,7 +24,10 @@
* guarantee (re)execution, then you should call invalidate().
*/
public abstract class CompilerPass {
public static List<Class<? extends CompilerPass>> NO_DEPENDENCIES = new ArrayList<Class<? extends CompilerPass>>();

static final Logger LOG = LoggerFactory.getLogger(CompilerPass.class);

protected static final List<Class<? extends CompilerPass>> NO_DEPENDENCIES = Collections.emptyList();

private List<CompilerPassListener> listeners = new ArrayList<CompilerPassListener>();

@@ -135,49 +141,47 @@ private Object makeSureDependencyHasRunOnce(Class<? extends CompilerPass> passCl

public static CompilerPass createPassInstance(Class<? extends CompilerPass> passClass) {
try {
return (CompilerPass) passClass.getDeclaredConstructor().newInstance();
} catch (InstantiationException ex) {
LoggerFactory.getLogger(CompilerPass.class).error(ex);
} catch (IllegalAccessException ex) {
LoggerFactory.getLogger(CompilerPass.class).error(ex);
} catch (IllegalArgumentException ex) {
LoggerFactory.getLogger(CompilerPass.class).error(ex);
} catch (InvocationTargetException ex) {
LoggerFactory.getLogger(CompilerPass.class).error(ex);
} catch (NoSuchMethodException ex) {
LoggerFactory.getLogger(CompilerPass.class).error(ex);
} catch (SecurityException ex) {
LoggerFactory.getLogger(CompilerPass.class).error(ex);
return passClass.getDeclaredConstructor().newInstance();
}
catch (NoSuchMethodException|IllegalAccessException|IllegalArgumentException ex) {
LOG.error("failed to create compiler pass: '" + passClass.getName() + "'", ex);
}
catch (InstantiationException|InvocationTargetException|SecurityException ex) {
LOG.error("failed to create compiler pass: '" + passClass.getName() + "'", ex);
}

return null;
}

public static CompilerPass createPassInstance(String passClassName) {
final String className = "org.jruby.ir.passes." + passClassName;
try {
String clazzName = "org.jruby.ir.passes." + passClassName;
Class<? extends CompilerPass> clazz =
(Class<? extends CompilerPass>) Class.forName(clazzName);
Class<? extends CompilerPass> clazz = (Class<? extends CompilerPass>) Class.forName(className);
return createPassInstance(clazz);
} catch (ClassNotFoundException ex) {
// FIXME: Do this in a nice way even if only for test code
System.out.println("No such pass: " + ex);
System.exit(-1);
}

catch (ClassNotFoundException ex) {
LOG.warn("skipping unknown compiler pass name: '" + className + "'");
}
return null;
}

public static List<CompilerPass> getPassesFromString(String passList, String defaultPassList) {
if (passList == null) passList = defaultPassList;

List<CompilerPass> passes = new ArrayList<CompilerPass>();
final List<CompilerPass> passes;

if (!passList.equals("")) {
for (String passClassName : passList.split(",")) {
passes.add(createPassInstance(passClassName));
if ( ! passList.isEmpty() ) {
List<String> split = StringSupport.split(passList, ',');
passes = new ArrayList<>(split.size());
for ( String passClassName : split ) {
if ( ! passClassName.isEmpty() ) {
CompilerPass pass = createPassInstance(passClassName);
if ( pass != null ) passes.add(pass);
}
}
}
else {
passes = new ArrayList<>(2);
}

return passes;
}
Original file line number Diff line number Diff line change
@@ -71,7 +71,7 @@ static ClassExtensionLibrary tryFind(Ruby runtime, String searchName) {
if (leftmostIdentifier == all.length) return null;

// make service name out of last element
String serviceName = buildServiceName(all[all.length - 1]);
CharSequence serviceName = buildServiceName(all[all.length - 1]);

// allocate once with plenty of space, to reduce object churn
StringBuilder classNameBuilder = new StringBuilder(searchName.length() * 2);
@@ -80,16 +80,14 @@ static ClassExtensionLibrary tryFind(Ruby runtime, String searchName) {
for (int i = all.length - 1; i >= leftmostIdentifier; i--) {
buildClassName(classNameBuilder, classFileBuilder, all, i, serviceName);

String classFileName = classFileBuilder.toString();

// bail out once if see a dash in the name
if (classFileName.contains("-")) return null;
if (classFileBuilder.indexOf("-", 0) >= 0) return null;

// look for the filename in classloader resources
URL resource = runtime.getJRubyClassLoader().getResource(classFileBuilder.toString());
if (resource == null) continue;

String className = classNameBuilder.toString();
final String className = classNameBuilder.toString();

try {
Class theClass = runtime.getJavaSupport().loadJavaClass(className);
@@ -99,7 +97,7 @@ static ClassExtensionLibrary tryFind(Ruby runtime, String searchName) {
continue;
} catch (UnsupportedClassVersionError ucve) {
if (runtime.isDebug()) ucve.printStackTrace();
throw runtime.newLoadError("JRuby ext built for wrong Java version in `" + className + "': " + ucve, className.toString());
throw runtime.newLoadError("JRuby ext built for wrong Java version in `" + className + "': " + ucve, className);
}
}

@@ -126,7 +124,7 @@ private static boolean isJavaIdentifier(String str) {
return true;
}

private static void buildClassName(StringBuilder nameBuilder, StringBuilder fileBuilder, String[] all, int i, String serviceName) {
private static void buildClassName(StringBuilder nameBuilder, StringBuilder fileBuilder, String[] all, int i, CharSequence serviceName) {
nameBuilder.setLength(0);
fileBuilder.setLength(0);
for (int j = i; j < all.length - 1; j++) {
@@ -137,15 +135,16 @@ private static void buildClassName(StringBuilder nameBuilder, StringBuilder file
fileBuilder.append(serviceName).append(".class");
}

private static String buildServiceName(String jarName) {
private static CharSequence buildServiceName(final String jarName) {
String[] last = jarName.split("_");
StringBuilder serviceName = new StringBuilder();
StringBuilder serviceName = new StringBuilder(jarName.length() + 7);
for (int i = 0, j = last.length; i < j; i++) {
if ("".equals(last[i])) break;
serviceName.append(Character.toUpperCase(last[i].charAt(0))).append(last[i].substring(1));
final String l = last[i];
if (l.length() == 0) break;
serviceName.append(Character.toUpperCase(l.charAt(0))).append(l.substring(1));
}
serviceName.append("Service");
return serviceName.toString();
return serviceName;
}

public ClassExtensionLibrary(String name, Class extension) {
118 changes: 55 additions & 63 deletions core/src/main/java/org/jruby/util/cli/ArgumentProcessor.java
Original file line number Diff line number Diff line change
@@ -28,15 +28,16 @@
***** END LICENSE BLOCK *****/
package org.jruby.util.cli;

import org.jruby.Ruby;
import org.jruby.RubyInstanceConfig;
import org.jruby.exceptions.MainExitException;
import org.jruby.runtime.profile.builtin.ProfileOutput;
import org.jruby.util.JRubyFile;
import org.jruby.util.FileResource;
import org.jruby.util.KCode;
import org.jruby.util.SafePropertyAccessor;
import org.jruby.util.StringSupport;
import org.jruby.util.func.Function2;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -64,23 +65,33 @@ public class ArgumentProcessor {
public static final String SEPARATOR = "(?<!jar:file|jar|file|classpath|uri:classloader|uri|http|https):";

private static final class Argument {
public final String originalValue;
public final String dashedValue;
public Argument(String value, boolean dashed) {
final String originalValue;
private String dashedValue;
Argument(String value, boolean dashed) {
this.originalValue = value;
this.dashedValue = dashed && !value.startsWith("-") ? "-" + value : value;
this.dashedValue = dashed ? null : value;
}

public String toString() {
final String getDashedValue() {
String dashedValue = this.dashedValue;
if ( dashedValue == null ) {
final String value = originalValue;
dashedValue = ! value.startsWith("-") ? ('-' + value) : value;
this.dashedValue = dashedValue;
}
return dashedValue;
}

public String toString() {
return getDashedValue();
}
}

private List<Argument> arguments;
private final List<Argument> arguments;
private int argumentIndex = 0;
private boolean processArgv;
private final boolean rubyOpts;
RubyInstanceConfig config;
final RubyInstanceConfig config;
private boolean endOfArguments = false;
private int characterIndex = 0;

@@ -92,12 +103,15 @@ public ArgumentProcessor(String[] arguments, RubyInstanceConfig config) {

public ArgumentProcessor(String[] arguments, boolean processArgv, boolean dashed, boolean rubyOpts, RubyInstanceConfig config) {
this.config = config;
this.arguments = new ArrayList<Argument>();
if (arguments != null && arguments.length > 0) {
this.arguments = new ArrayList<>(arguments.length);
for (String argument : arguments) {
this.arguments.add(new Argument(argument, dashed));
}
}
else {
this.arguments = new ArrayList<>(0);
}
this.processArgv = processArgv;
this.rubyOpts = rubyOpts;
}
@@ -159,7 +173,7 @@ private String getArgumentError(String additionalError) {
}

private void processArgument() {
String argument = arguments.get(argumentIndex).dashedValue;
String argument = arguments.get(argumentIndex).getDashedValue();

if (argument.length() == 1) {
// sole "-" means read from stdin and pass remaining args as ARGV
@@ -267,10 +281,7 @@ private void processArgument() {
grabValue(getArgumentError(" -J-cp must be followed by a path expression"));
}
break FOR;
case 'K':
// FIXME: No argument seems to work for -K in MRI plus this should not
// siphon off additional args 'jruby -K ~/scripts/foo'. Also better error
// processing.
case 'K': // @Deprecated TODO no longer relevant in Ruby 2.x
String eArg = grabValue(getArgumentError("provide a value for -K"));

config.setKCode(KCode.create(null, eArg));
@@ -436,7 +447,7 @@ private void processArgument() {
} else if (argument.startsWith("--debug=")) {
for (String debug : valueListFor(argument, "debug")) {
boolean all = debug.equals("all");
if (debug.equals("frozen-string-literal") || all) {
if (all || debug.equals("frozen-string-literal")) {
config.setDebuggingFrozenStringLiteral(true);
continue;
}
@@ -512,8 +523,9 @@ private void processArgument() {
config.setDebuggingFrozenStringLiteral(true);
break FOR;
} else if (argument.startsWith("--disable")) {
if (argument.equals("--disable")) {
characterIndex = argument.length();
final int len = argument.length();
if (len == "--disable".length()) {
characterIndex = len;
String feature = grabValue(getArgumentError("missing argument for --disable"), false);
argument = "--disable=" + feature;
}
@@ -522,8 +534,9 @@ private void processArgument() {
}
break FOR;
} else if (argument.startsWith("--enable")) {
if (argument.equals("--enable")) {
characterIndex = argument.length();
final int len = argument.length();
if (len == "--enable".length()) {
characterIndex = len;
String feature = grabValue(getArgumentError("missing argument for --enable"), false);
argument = "--enable=" + feature;
}
@@ -604,7 +617,7 @@ private void enableDisableFeature(String name, boolean enable) {
}
}

private String[] valueListFor(String argument, String key) {
private static String[] valueListFor(String argument, String key) {
int length = key.length() + 3; // 3 is from -- and = (e.g. --disable=)
String[] values = argument.substring(length).split(",");

@@ -613,29 +626,29 @@ private String[] valueListFor(String argument, String key) {
return values;
}

private void disallowedInRubyOpts(String option) {
private void disallowedInRubyOpts(CharSequence option) {
if (rubyOpts) {
throw new MainExitException(1, "jruby: invalid switch in RUBYOPT: " + option + " (RuntimeError)");
}
}

private void errorMissingEquals(String label) {
private static void errorMissingEquals(String label) {
MainExitException mee;
mee = new MainExitException(1, "missing argument for --" + label + "\n");
mee.setUsageError(true);
throw mee;
}

private void processEncodingOption(String value) {
String[] encodings = value.split(":", 3);
switch (encodings.length) {
List<String> encodings = StringSupport.split(value, ':', 3);
switch (encodings.size()) {
case 3:
throw new MainExitException(1, "extra argument for -E: " + encodings[2]);
throw new MainExitException(1, "extra argument for -E: " + encodings.get(2));
case 2:
config.setInternalEncoding(encodings[1]);
config.setInternalEncoding(encodings.get(1));
case 1:
config.setExternalEncoding(encodings[0]);
// Zero is impossible
config.setExternalEncoding(encodings.get(0));
// Zero is impossible
}
}

@@ -706,12 +719,12 @@ private String resolveScript(String scriptName) {
return null;
}

@Deprecated
public String resolveScriptUsingClassLoader(String scriptName) {
if(RubyInstanceConfig.defaultClassLoader().getResourceAsStream("bin/" + scriptName) != null){
if (RubyInstanceConfig.defaultClassLoader().getResourceAsStream("bin/" + scriptName) != null){
return "classpath:/bin/" + scriptName;
} else {
return null;
}
return null;
}

private String grabValue(String errorMessage) {
@@ -741,22 +754,10 @@ private String grabOptionalValue() {
return null;
}

private void logScriptResolutionSuccess(String path) {
if (RubyInstanceConfig.DEBUG_SCRIPT_RESOLUTION) {
config.getError().println("Found: " + path);
}
}

private void logScriptResolutionFailure(String path) {
if (RubyInstanceConfig.DEBUG_SCRIPT_RESOLUTION) {
config.getError().println("Searched: " + path);
}
}

private static final Set<String> KNOWN_PROPERTIES = new HashSet<>();
private static final Set<String> KNOWN_PROPERTIES = new HashSet<>(Options.PROPERTIES.size() + 16, 1);

static {
KNOWN_PROPERTIES.addAll(Options.getPropertyNames());
Options.addPropertyNames(KNOWN_PROPERTIES);
KNOWN_PROPERTIES.add("jruby.home");
KNOWN_PROPERTIES.add("jruby.script");
KNOWN_PROPERTIES.add("jruby.shell");
@@ -770,13 +771,13 @@ private void logScriptResolutionFailure(String path) {
KNOWN_PROPERTIES.add("jruby.stack.max");
}

private static final List<String> KNOWN_PROPERTY_PREFIXES = new ArrayList<>();
private static final List<String> KNOWN_PROPERTY_PREFIXES = new ArrayList<>(4);

static {
KNOWN_PROPERTY_PREFIXES.add("jruby.openssl.");
}

private void checkProperties() {
private static void checkProperties() {
for (String propertyName : System.getProperties().stringPropertyNames()) {
if (propertyName.startsWith("jruby.")) {
if (!isPropertySupported(propertyName)) {
@@ -786,7 +787,7 @@ private void checkProperties() {
}
}

private boolean isPropertySupported(String propertyName) {
private static boolean isPropertySupported(String propertyName) {
if (KNOWN_PROPERTIES.contains(propertyName)) {
return true;
}
@@ -803,7 +804,8 @@ private boolean isPropertySupported(String propertyName) {
private static final Map<String, Function2<Boolean, ArgumentProcessor, Boolean>> FEATURES;

static {
Map<String, Function2<Boolean, ArgumentProcessor, Boolean>> features = new HashMap<>();
Map<String, Function2<Boolean, ArgumentProcessor, Boolean>> features = new HashMap<>(12, 1);
Function2<Boolean, ArgumentProcessor, Boolean> function2;

features.put("all", new Function2<Boolean, ArgumentProcessor, Boolean>() {
public Boolean apply(ArgumentProcessor processor, Boolean enable) {
@@ -821,36 +823,26 @@ public Boolean apply(ArgumentProcessor processor, Boolean enable) {
return true;
}
});
features.put("did-you-mean", new Function2<Boolean, ArgumentProcessor, Boolean>() {
public Boolean apply(ArgumentProcessor processor, Boolean enable) {
processor.config.setDisableDidYouMean(!enable);
return true;
}
});
features.put("did_you_mean", new Function2<Boolean, ArgumentProcessor, Boolean>() {
features.put("did-you-mean", function2 = new Function2<Boolean, ArgumentProcessor, Boolean>() {
public Boolean apply(ArgumentProcessor processor, Boolean enable) {
processor.config.setDisableDidYouMean(!enable);
return true;
}
});
features.put("did_you_mean", function2); // alias
features.put("rubyopt", new Function2<Boolean, ArgumentProcessor, Boolean>() {
public Boolean apply(ArgumentProcessor processor, Boolean enable) {
processor.config.setDisableRUBYOPT(!enable);
return true;
}
});
features.put("frozen-string-literal", new Function2<Boolean, ArgumentProcessor, Boolean>() {
public Boolean apply(ArgumentProcessor processor, Boolean enable) {
processor.config.setFrozenStringLiteral(enable);
return true;
}
});
features.put("frozen_string_literal", new Function2<Boolean, ArgumentProcessor, Boolean>() {
features.put("frozen-string-literal", function2 = new Function2<Boolean, ArgumentProcessor, Boolean>() {
public Boolean apply(ArgumentProcessor processor, Boolean enable) {
processor.config.setFrozenStringLiteral(enable);
return true;
}
});
features.put("frozen_string_literal", function2); // alias

FEATURES = features;
}
18 changes: 10 additions & 8 deletions core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -51,7 +51,7 @@
* of the built-in structure.
*/
public class Options {
private static final List<Option> _loadedOptions = new ArrayList<Option>();
private static final List<Option> _loadedOptions = new ArrayList<>(240);
private static final boolean INVOKEDYNAMIC_DEFAULT = calculateInvokedynamicDefault();

// This section holds all Options for JRuby. They will be listed in the
@@ -248,6 +248,9 @@ public class Options {
public static final Option<Boolean> TRUFFLE_ROPE_LAZY_SUBSTRINGS = bool(TRUFFLE, "truffle.rope.lazy_substrings", true, "Indicates whether a substring operation on a rope should be performed lazily.");
public static final Option<Boolean> TRUFFLE_ROPE_PRINT_INTERN_STATS = bool(TRUFFLE, "truffle.rope.print_intern_stats", false, "Print interned rope stats at application exit.");

public static final Option<Integer> TRUFFLE_GLOBAL_VARIABLE_MAX_INVALIDATIONS = integer(TRUFFLE, "truffle.global_variable.max_invalidations", 10,
"Maximum number of times a global variable can be changed to be considered constant.");

public static final Option<Integer> TRUFFLE_DEFAULT_CACHE = integer(TRUFFLE, "truffle.default_cache", 8, "Default size for caches.");

public static final Option<Integer> TRUFFLE_METHOD_LOOKUP_CACHE = integer(TRUFFLE, "truffle.method_lookup.cache", TRUFFLE_DEFAULT_CACHE.load(), "Method lookup cache size.");
@@ -361,10 +364,7 @@ private static boolean calculateInvokedynamicDefault() {
return false;
}

private static enum SearchMode {
PREFIX,
CONTAINS
}
private enum SearchMode { PREFIX, CONTAINS }

public static void listPrefix(String prefix) {
list(SearchMode.PREFIX, prefix);
@@ -394,13 +394,15 @@ private static void list(SearchMode mode, String string) {
}

public static Set<String> getPropertyNames() {
final Set<String> propertyNames = new HashSet<String>();
final Set<String> propertyNames = new HashSet<>(PROPERTIES.size() + 1, 1);
addPropertyNames(propertyNames);
return Collections.unmodifiableSet(propertyNames);
}

static void addPropertyNames(final Set<String> propertyNames) {
for (Option option : PROPERTIES) {
propertyNames.add(option.propertyName());
}

return Collections.unmodifiableSet(propertyNames);
}

@Deprecated
59 changes: 31 additions & 28 deletions core/src/main/java/org/jruby/util/cli/OutputStrings.java
Original file line number Diff line number Diff line change
@@ -30,8 +30,8 @@ public static String getBasicUsageHelp() {
.append(" -J[java option] pass an option on to the JVM (e.g. -J-Xmx512m)\n")
.append(" use --properties to list JRuby properties\n")
.append(" run 'java -help' for a list of other Java options\n")
.append(" -Kkcode specifies code-set (e.g. -Ku for Unicode, -Ke for EUC and -Ks\n")
.append(" for SJIS)\n")
//.append(" -Kkcode specifies code-set (e.g. -Ku for Unicode, -Ke for EUC and -Ks\n")
//.append(" for SJIS)\n")
.append(" -l enable line ending processing\n")
.append(" -n assume 'while gets(); ... end' loop around your script\n")
.append(" -p assume loop like -n but print line also like sed\n")
@@ -65,34 +65,39 @@ public static String getBasicUsageHelp() {
.append(" --client use the non-optimizing \"client\" JVM\n")
.append(" (improves startup; default)\n")
.append(" --server use the optimizing \"server\" JVM (improves perf)\n")
.append(" --dev prioritize startup time over long term performance\n")
.append(" --manage enable remote JMX management and monitoring of the VM\n")
.append(" and JRuby\n")
.append(" --headless do not launch a GUI window, no matter what\n")
.append(" --dev prioritize startup time over long term performance\n")
.append(" --manage enable remote JMX management and monitoring of JVM and JRuby\n")
.append(" --bytecode show the JVM bytecode produced by compiling specified code\n")
.append(" --version print the version\n")
.append(" --disable-gems do not load RubyGems on startup\n");
.append(" --disable-gems do not load RubyGems on startup\n")
.append(" --enable=feature[,...], --disable=feature[,...]\n")
.append(" enable or disable features\n");

return sb.toString();
}

public static String getExtendedHelp() {
StringBuilder sb = new StringBuilder();
sb
.append("Extended options:\n")
.append(" -X-O run with ObjectSpace disabled (default; improves performance)\n")
.append(" -X+O run with ObjectSpace enabled (reduces performance)\n")
.append(" -X-C disable all compilation\n")
.append(" -X-CIR disable all compilation and use IR runtime\n")
.append(" -X+C force compilation of all scripts before they are run (except eval)\n")
.append(" -X+CIR force compilation and use IR runtime\n")
.append(" -X+JIR JIT compilation and use IR runtime\n")
.append(" -X+T use Truffle\n")
.append(" -Xclassic don't use Truffle, reversing the -X+T option\n")
.append(" -Xsubstring? list options that contain substring in their name\n")
.append(" -Xprefix... list options that are prefixed wtih prefix\n");
public static String getFeaturesHelp() { return
"Features:\n" +
" gems rubygems (default: " + (Options.CLI_RUBYGEMS_ENABLE.defaultValue() ? "enabled" : "disabled") + ")\n" +
" did_you_mean did_you_mean (default: " + (Options.CLI_DID_YOU_MEAN_ENABLE.defaultValue() ? "enabled" : "disabled") + ")\n" +
" rubyopt RUBYOPT environment variable (default: " + (Options.CLI_RUBYOPT_ENABLE.defaultValue() ? "enabled" : "disabled") + ")\n" +
" frozen-string-literal freeze all string literals (default: disabled)\n" ;
}

return sb.toString();
public static String getExtendedHelp() { return
"Extended options:\n" +
" -X-O run with ObjectSpace disabled (default; improves performance)\n" +
" -X+O run with ObjectSpace enabled (reduces performance)\n" +
" -X-C disable all compilation\n" +
" -X-CIR disable all compilation and use IR runtime\n" +
" -X+C force compilation of all scripts before they are run (except eval)\n" +
" -X+CIR force compilation and use IR runtime\n" +
" -X+JIR JIT compilation and use IR runtime\n" +
" -X+T use Truffle\n" +
" -Xclassic don't use Truffle, reversing the -X+T option\n" +
" -Xsubstring? list options that contain substring in their name\n" +
" -Xprefix... list options that are prefixed wtih prefix\n" ;
}

public static String getPropertyHelp() {
@@ -113,8 +118,8 @@ public static String getPropertyHelp() {
}

public static String getVersionString() {
String fullVersion = String.format(
"jruby%s %s (%s) %s %s %s %s on %s%s%s [%s-%s]",
return String.format(
"jruby%s %s (%s) %s %s %s %s on %s%s%s [%s-%s]",
Options.COMPILE_MODE.load().isTruffle() ? "+truffle" : "",
Constants.VERSION,
Constants.RUBY_VERSION,
@@ -127,12 +132,10 @@ public static String getVersionString() {
Options.COMPILE_MODE.load().shouldJIT() ? " +jit" : "",
RbConfigLibrary.getOSName(),
RbConfigLibrary.getArchitecture()
);

return fullVersion;
);
}

public static String getCopyrightString() {
return "JRuby - Copyright (C) 2001-2015 The JRuby Community (and contribs)";
return "JRuby - Copyright (C) 2001-2016 The JRuby Community (and contribs)";
}
}
28 changes: 12 additions & 16 deletions core/src/main/java/org/jruby/util/io/EncodingUtils.java
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@
import org.jruby.util.TypeConverter;

import java.util.Arrays;
import java.util.List;

public class EncodingUtils {
public static final int ECONV_DEFAULT_NEWLINE_DECORATOR = Platform.IS_WINDOWS ? EConvFlags.UNIVERSAL_NEWLINE_DECORATOR : 0;
@@ -384,20 +385,14 @@ public static void ioExtIntToEncs(ThreadContext context, IOEncodable encodable,

// mri: parse_mode_enc
public static void parseModeEncoding(ThreadContext context, IOEncodable ioEncodable, String option, int[] fmode_p) {
Ruby runtime = context.runtime;
final Ruby runtime = context.runtime;
EncodingService service = runtime.getEncodingService();
Encoding idx2;
Encoding intEnc, extEnc;
if (fmode_p == null) fmode_p = new int[]{0};
String estr;

String[] encs = option.split(":", 2);
List<String> encs = StringSupport.split(option, ':', 2);

if (encs.length == 2) {
estr = encs[0];
} else {
estr = option;
}
String estr = encs.size() == 2 ? encs.get(0) : option;

if (estr.toLowerCase().startsWith("bom|")) {
estr = estr.substring(4);
@@ -410,7 +405,7 @@ public static void parseModeEncoding(ThreadContext context, IOEncodable ioEncoda
}
}

Encoding idx = service.findEncodingNoError(new ByteList(estr.getBytes()));
Encoding idx = service.findEncodingNoError(new ByteList(estr.getBytes(), false));

if (idx == null) {
runtime.getWarnings().warn("Unsupported encoding " + estr + " ignored");
@@ -420,16 +415,17 @@ public static void parseModeEncoding(ThreadContext context, IOEncodable ioEncoda
}

intEnc = null;
if (encs.length == 2) {
if (encs[1].equals("-")) {
if (encs.size() == 2) {
String istr = encs.get(1);
if (istr.equals("-")) {
intEnc = null;
} else {
idx2 = service.getEncodingFromString(encs[1]);
if (idx2 == null) {
context.runtime.getWarnings().warn("ignoring internal encoding " + idx2 + ": it is identical to external encoding " + idx);
idx = service.getEncodingFromString(istr);
if (idx == null) {
runtime.getWarnings().warn("ignoring internal encoding " + idx + ": it is identical to external encoding " + idx);
intEnc = null;
} else {
intEnc = idx2;
intEnc = idx;
}
}
}
8 changes: 8 additions & 0 deletions lib/ruby/truffle/cext/ruby.h
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ extern "C" {

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

@@ -63,6 +64,8 @@ VALUE get_rb_eRuntimeError(void);

// Conversions

VALUE CHR2FIX(char ch);

int NUM2INT(VALUE value);
unsigned int NUM2UINT(VALUE value);
long NUM2LONG(VALUE value);
@@ -85,6 +88,7 @@ VALUE ID2SYM(ID value);

int NIL_P(VALUE value);
int FIXNUM_P(VALUE value);
int RTEST(VALUE value);

// Float

@@ -99,6 +103,8 @@ VALUE rb_str_new_cstr(const char *string);
#define rb_str_new2 rb_str_new_cstr
void rb_str_cat(VALUE string, const char *to_concat, long length);

VALUE rb_str_buf_new(long capacity);

// Symbol

ID rb_intern(const char *string);
@@ -145,6 +151,8 @@ int rb_scan_args(int argc, VALUE *argv, const char *format, ...);

#define rb_funcall(object, name, argc, ...) truffle_invoke(object, "__send__", name, ##__VA_ARGS__)

VALUE rb_yield(VALUE value);

// Instance variables

VALUE rb_iv_get(VALUE object, const char *name);
Binary file modified lib/ruby/truffle/cext/ruby.su
Binary file not shown.
6 changes: 3 additions & 3 deletions lib/ruby/truffle/jruby+truffle/gem_ci/actionpack.rb
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@
has_to_succeed setup

set_result run([%w[--require-pattern test/**/*_test.rb],
(option(:exclude) ? %w[-r excluded-tests] : []),
%w[-- -I test -e nil]].flatten(1),
raise: false)
(option(:exclude) ? %w[-r excluded-tests] : []),
%w[-- -I test -e nil]].flatten(1),
raise: false)

2 changes: 1 addition & 1 deletion lib/ruby/truffle/jruby+truffle/gem_ci/travis.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
actionpack
# actionpack
activesupport
activemodel
algebrick
300 changes: 0 additions & 300 deletions lib/ruby/truffle/jruby+truffle/gem_configurations/actionpack.yaml

This file was deleted.

87 changes: 0 additions & 87 deletions lib/ruby/truffle/jruby+truffle/gem_configurations/activemodel.yaml

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

4 changes: 0 additions & 4 deletions lib/ruby/truffle/jruby+truffle/gem_configurations/psd.yaml

This file was deleted.

239 changes: 239 additions & 0 deletions lib/ruby/truffle/jruby+truffle/lib/truffle/actionpack_exclusions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
---
ActionDispatch::Journey::GTG::TestGeneralizedTable:
- test_to_svg # blocking
ActionPackAssertionsControllerTest:
- test_render_file_absolute_path
- test_render_file_relative_path
AssertTemplateTest:
- test_assert_template_reset_between_requests
- test_fails_with_repeated_name_in_path
- test_file_with_absolute_path_success
- test_file_with_relative_path_success
- test_with_file_failure
FunctionalFragmentCachingTest:
- test_fragment_caching
- test_fragment_caching_with_variant
IntegrationProcessTest:
- test_cookie_persist_to_next_request
- test_https_and_port_via_host_and_https!
- test_https_and_port_via_process
- test_port_via_host!
- test_port_via_process
- test_cookie_persist_to_next_request_on_another_domain
- test_get
- test_get_with_parameters
- test_get_with_query_string
- test_get_xml_rss_atom
- test_head
- test_post
- test_redirect
- test_request_with_bad_format
- test_respect_removal_of_default_headers_by_a_controller_action
- test_response_cookies_are_added_to_the_cookie_jar_for_the_next_request
- test_xml_http_request_get
ApplicationIntegrationTest:
- test_missing_route_helper_after_controller_access
- test_missing_route_helper_before_controller_access
ActionController::SSETest:
- test_basic_sse
- test_sse_with_event_name
- test_sse_with_id
- test_sse_with_multiple_line_message
- test_sse_with_retry
ActionController::LiveStreamTest:
- test_abort_with_full_buffer
- test_async_stream
- test_bad_request_in_controller_before_streaming
- test_exception_callback_when_committed
- test_exception_handling_html
- test_exception_handling_plain_text
- test_exception_in_controller_before_streaming
- test_exceptions_raised_handling_exceptions_and_committed
- test_ignore_client_disconnect
- test_live_stream_default_header
- test_render_text
- test_set_cookie
- test_stale_with_etag
- test_stale_without_etag
- test_thread_locals_get_copied
- test_write_to_stream
ExpiresInRenderTest:
- test_dynamic_render_with_file
- test_permitted_dynamic_render_file_hash
RequestForgeryProtectionControllerUsingResetSessionTest:
- test_should_emit_a_csrf-param_meta_tag_and_a_csrf-token_meta_tag
- test_should_render_button_to_with_token_tag
- test_should_render_form_with_token_tag
- test_should_render_form_with_token_tag_if_remote_and_authenticity_token_requested
- test_should_render_form_with_token_tag_if_remote_and_external_authenticity_token_requested
- test_should_render_form_with_token_tag_if_remote_and_external_authenticity_token_requested_and_embedding_is_on
- test_should_render_form_with_token_tag_with_authenticity_token_requested
RequestForgeryProtectionControllerUsingExceptionTest:
- test_should_render_button_to_with_token_tag
- test_should_render_form_with_token_tag
- test_should_render_form_with_token_tag_if_remote_and_authenticity_token_requested
- test_should_render_form_with_token_tag_if_remote_and_external_authenticity_token_requested
- test_should_render_form_with_token_tag_if_remote_and_external_authenticity_token_requested_and_embedding_is_on
- test_should_render_form_with_token_tag_with_authenticity_token_requested
FreeCookieControllerTest:
- test_should_not_render_button_to_with_token_tag
- test_should_not_render_form_with_token_tag
RescueControllerTest:
- test_block_rescue_handler_with_argument
- test_block_rescue_handler_with_argument_as_string
- test_proc_rescue_handle_with_argument
- test_proc_rescue_handle_with_argument_as_string
SendFileTest:
- test_send_file_with_action_controller_live
TestCaseTest:
- test_assert_select_with_body
- test_assert_select_without_body
- test_should_not_impose_childless_html_tags_in_xml
MimeControllerLayoutsTest:
- test_format_with_inherited_layouts
- test_missing_layout_renders_properly
RespondToControllerTest:
- test_html_type_with_layout
ContentNegotiation::TestContentNegotiation:
- test_A_*/*_Accept_header_will_return_HTML
RenderAction::RenderActionTest:
- test_rendering_an_action_using_'<action>'
- test_rendering_an_action_using_'<action>'_and_options
- test_rendering_an_action_using_:action
- test_rendering_an_action_using_:action_=>_:hello_world
- test_rendering_an_action_using_:action_=>_<String>
RenderAction::RenderLayoutTest:
- test_rendering_with_layout_=>_'greetings'
- test_rendering_with_layout_=>_:nil
- test_rendering_with_layout_=>_false
- test_rendering_with_layout_=>_true
RenderActionWithApplicationLayout::LayoutTest:
- test_rendering_implicit_application.html.erb_as_layout
- test_rendering_with_layout_=>_'greetings'
- test_rendering_with_layout_=>_:nil
- test_rendering_with_layout_=>_false
- test_rendering_with_layout_=>_true
RenderActionWithApplicationLayout::TestLayout:
- test_builder_works_with_layouts
RenderActionWithControllerLayout::ControllerLayoutTest:
- test_render_hello_world_and_implicitly_use_<controller_path>.html.erb_as_a_layout.
- test_rendering_with_layout_=>_:nil
- test_rendering_with_layout_=>_false
- test_rendering_with_layout_=>_true
RenderActionWithBothLayouts::ControllerLayoutTest:
- test_rendering_implicitly_use_<controller_path>.html.erb_over_application.html.erb_as_a_layout
- test_rendering_with_layout_=>_:nil
- test_rendering_with_layout_=>_false
- test_rendering_with_layout_=>_true
RenderBody::RenderBodyTest:
- test_rendering_body_with_layout:_'greetings'
- test_rendering_body_with_layout:_true
RenderContext::RenderContextTest:
- test_rendering_using_the_controller_as_context
- test_rendering_using_the_controller_as_context_with_layout
RenderHtml::RenderHtmlTest:
- test_rendering_text_with_layout:_'greetings'
- test_rendering_text_with_layout:_true
RenderImplicitAction::RenderImplicitActionTest:
- test_available_action?_returns_true_for_implicit_actions
- test_render_a_simple_action_with_new_explicit_call_to_render
- test_render_an_action_called_not_implemented
- test_render_an_action_with_a_missing_method_and_has_special_characters
ControllerLayouts::RenderLayoutTest:
- test_overriding_an_implicit_layout_with_render_:layout_option
- test_rendering_a_normal_template,_but_using_an_implicit_NAMED_layout
- test_rendering_a_normal_template,_but_using_the_implicit_layout
ControllerLayouts::LayoutOptionsTest:
- test_rendering_with_:layout_=>_false_leaves_out_the_implicit_layout
ControllerLayouts::MismatchFormatTest:
- test_a_layout_for_JS_is_ignored_even_if_explicitly_provided_for_HTML
- test_if_XML_is_implicitly_selected,_an_HTML_template_is_not_also_selected
- test_if_XML_is_selected,_an_HTML_template_is_not_also_selected
ControllerLayouts::FalseLayoutMethodTest:
- test_access_false_layout_returned_by_a_method/proc
RenderPartial::TestPartial:
- test_rendering_a_partial_in_ActionView_doesn't_pull_the_ivars_again_from_the_controller
- test_rendering_a_template_with_renders_another_partial_with_other_format_that_renders_other_partial_in_the_same_format
RenderPartial::TestInheritedPartial:
- test_partial_from_child_controller_gets_picked
- test_partial_from_parent_controller_gets_picked_if_missing_in_child_one
RenderPlain::RenderPlainTest:
- test_rendering_text_with_layout:_'greetings'
- test_rendering_text_with_layout:_true
RenderStreaming::StreamingTest:
- test_do_not_stream_on_HTTP/1.0
- test_rendering_with_layout_exception
- test_rendering_with_streaming_do_not_override_explicit_cache_control_given_to_render
- test_rendering_with_streaming_enabled_at_the_class_level
- test_rendering_with_streaming_given_to_render
- test_rendering_with_streaming_no_layout
- test_rendering_with_template_exception
- test_rendering_with_template_exception_logs_the_exception
- test_skip_rendering_with_streaming_at_render_level
RenderTemplate::TestWithoutLayout:
- test_rendering_a_builder_template
- test_rendering_a_normal_template_with_full_path_without_layout
- test_rendering_a_normal_template_with_full_path_without_layout_without_key
- test_rendering_a_template_not_in_a_subdirectory
- test_rendering_a_template_not_in_a_subdirectory_with_a_leading_slash
- test_rendering_a_template_not_in_a_subdirectory_with_a_leading_slash_without_key
- test_rendering_a_template_with_<%=raw_stuff_%>
- test_rendering_a_template_with_error_properly_excerts_the_code
- test_rendering_a_template_with_local_variables
- test_rendering_a_template_with_local_variables_without_key
- test_rendering_a_template_with_renders_another_template_with_other_format_that_renders_other_template_in_the_same_format
RenderTemplate::TestWithLayout:
- test_rendering_layout_=>_'greetings'
- test_rendering_with_implicit_layout
- test_rendering_with_layout_=>_:false
- test_rendering_with_layout_=>_:nil
- test_rendering_with_layout_=>_:true
RenderTemplate::Compatibility::TestTemplateRenderWithForwardSlash:
- test_rendering_a_normal_template_with_full_path_starting_with_a_leading_slash
Render::RenderTest:
- test_render_with_blank
Render::TestOnlyRenderPublicActions:
- test_raises_an_exception_when_a_method_of_Object_is_called
- test_raises_an_exception_when_a_private_method_is_called
Render::TestVariousObjectsAvailableInView:
- test_The_action_name_is_accessible_in_the_view
- test_The_controller_name_is_accessible_in_the_view
- test_The_request_object_is_accessible_in_the_view
Render::TestViewInheritance:
- test_Template_from_child_controller_gets_picked_over_parent_one
- test_Template_from_child_controller_with_custom_view_paths_appended_gets_picked_over_parent_one
- test_Template_from_child_controller_with_custom_view_paths_prepended_gets_picked_over_parent_one
- test_Template_from_parent_controller_gets_picked_if_missing_in_child_controller
RenderText::RenderTextTest:
- test_rendering_text_with_layout:_'greetings'
- test_rendering_text_with_layout:_true
CookiesTest:
- test_encrypted_cookie_using_custom_digest
- test_legacy_json_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_cookie_jar_if_both_secret_token_and_secret_key_base_are_set
- test_legacy_json_signed_cookie_is_read_and_transparently_upgraded_by_signed_json_hybrid_jar_if_both_secret_token_and_secret_key_base_are_set
- test_legacy_signed_cookie_is_read_and_transparently_upgraded_by_signed_cookie_jar_if_both_secret_token_and_secret_key_base_are_set
DebugExceptionsTest:
- test_debug_exceptions_app_shows_user_code_that_caused_the_error_in_source_view
- test_display_backtrace_on_template_missing_errors
- test_display_backtrace_when_error_type_is_SyntaxError
- test_display_backtrace_when_error_type_is_SyntaxError_wrapped_by_ActionView::Template::Error
- test_displays_request_and_response_info_when_a_RoutingError_occurs
- test_rescue_with_diagnostics_message
- test_rescue_with_text_error_for_xhr_request
- test_show_registered_original_exception_for_wrapped_exceptions
TestGenerationPrefix::WithMountedEngine:
- test_[ENGINE]_absolute_path_redirect_doesn't_use_SCRIPT_NAME_from_request
- test_[ENGINE]_absolute_path_root_doesn't_use_SCRIPT_NAME_from_request
- test_[ENGINE]_relative_path_redirect_uses_SCRIPT_NAME_from_request
TestRoutingMapper:
- test_redirect_modulo
TestRedirectInterpolation:
- test_path_redirect_escapes_interpolated_parameters_correctly
- test_redirect_escapes_interpolated_parameters_with_redirect_proc
TestGlobRoutingMapper:
- test_glob_constraint
QueryStringParsingTest:
- test_query_string_with_many_ampersands
CookieStoreTest:
- test_deserializes_unloaded_classes_on_get_id
- test_deserializes_unloaded_classes_on_get_value
210 changes: 210 additions & 0 deletions lib/ruby/truffle/jruby+truffle/lib/truffle/config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
require 'pp'
require 'yaml'

stubs = {
# TODO (pitr-ch 23-Jun-2016): remove? it's not used any more
minitest: dedent(<<-RUBY),
require 'minitest'
# mock load_plugins as it loads rubygems
def Minitest.load_plugins
end
RUBY

activesupport_isolation: dedent(<<-RUBY),
require 'active_support/testing/isolation'
module ActiveSupport
module Testing
module Isolation
def run
with_info_handler do
time_it do
capture_exceptions do
before_setup; setup; after_setup
skip 'isolation not supported'
end
%w{ before_teardown teardown after_teardown }.each do |hook|
capture_exceptions do
self.send hook
end
end
end
end
return self # per contract
end
end
end
end
RUBY

bcrypt: dedent(<<-RUBY),
require 'bcrypt'
module BCrypt
class Engine
def self.hash_secret(secret, salt, _ = nil)
if valid_secret?(secret)
if valid_salt?(salt)
Truffle::Gem::BCrypt.hashpw(secret.to_s, salt.to_s)
else
raise Errors::InvalidSalt.new("invalid salt")
end
else
raise Errors::InvalidSecret.new("invalid secret")
end
end
def self.generate_salt(cost = self.cost)
cost = cost.to_i
if cost > 0
if cost < MIN_COST
cost = MIN_COST
end
Truffle::Gem::BCrypt.gensalt(cost)
else
raise Errors::InvalidCost.new("cost must be numeric and > 0")
end
end
end
end
RUBY

html_sanitizer: dedent(<<-RUBY),
require 'action_view'
require 'action_view/helpers'
require 'action_view/helpers/sanitize_helper'
module ActionView
module Helpers
module SanitizeHelper
def sanitize(html, options = {})
html
end
def sanitize_css(style)
style
end
def strip_tags(html)
html
end
def strip_links(html)
html
end
module ClassMethods #:nodoc:
attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
def sanitized_allowed_tags
[]
end
def sanitized_allowed_attributes
[]
end
end
end
end
end
RUBY

}.reduce({}) do |h, (k, v)|
file_name = "stub-#{k}"
h.update k => { setup: { file: { "#{file_name}.rb" => v } },
run: { require: [file_name] } }
end

replacements = {
bundler: dedent(<<-RUBY),
module Bundler
BundlerError = Class.new(Exception)
def self.setup
end
end
RUBY
:'bundler/gem_tasks' => nil,
java: nil,
bcrypt_ext: nil,
method_source: nil,
:'rails-html-sanitizer' => nil,
nokogiri: nil
}.reduce({}) do |h, (k, v)|
h.update k => { setup: { file: { "#{k}.rb" => v || %[puts "loaded '#{k}.rb' an empty replacement"] } } }
end

# add required replacements to stubs
deep_merge!(stubs.fetch(:bcrypt),
replacements.fetch(:java),
replacements.fetch(:bcrypt_ext))
deep_merge!(stubs.fetch(:html_sanitizer),
replacements.fetch(:'rails-html-sanitizer'),
replacements.fetch(:nokogiri))

def exclusion_file(gem_name)
data = YAML.load_file(__dir__ + "/#{gem_name}_exclusions.yaml")
data.pretty_inspect
end

rails_common =
deep_merge replacements.fetch(:bundler),
setup: { without: %w(db job) },
run: { environment: { 'N' => 1 },
require: %w(rubygems date bigdecimal pathname openssl-stubs) }

config :activesupport,
deep_merge(
rails_common,
stubs.fetch(:activesupport_isolation),
replacements.fetch(:method_source))

config :activemodel,
deep_merge(
rails_common,
stubs.fetch(:activesupport_isolation),
stubs.fetch(:bcrypt))

# TODO (pitr-ch 23-Jun-2016): investigate, fails intermittently
config :actionpack,
deep_merge(
rails_common,
stubs.fetch(:html_sanitizer),
setup: { file: { 'excluded-tests.rb' => format(dedent(<<-RUBY), exclusion_file(:actionpack)),
failures = %s
require 'truffle/exclude_rspec_examples'
Truffle.exclude_rspec_examples failures
RUBY
} })

config :'concurrent-ruby',
setup: { file: { "stub-processor_number.rb" => dedent(<<-RUBY) } },
# stub methods calling #system
require 'concurrent'
module Concurrent
module Utility
class ProcessorCounter
def compute_processor_count
2
end
def compute_physical_processor_count
2
end
end
end
end
RUBY
run: { require: %w(stub-processor_number) }

config :monkey_patch,
replacements.fetch(:bundler)

config :openweather,
replacements.fetch(:'bundler/gem_tasks')

config :psd,
replacements.fetch(:nokogiri)
222 changes: 167 additions & 55 deletions lib/ruby/truffle/jruby+truffle/lib/truffle/runner.rb
Original file line number Diff line number Diff line change
@@ -16,6 +16,21 @@
require 'rubygems'
require 'bundler'

class String
def pretty_print(q)
lines = self.lines
if lines.size > 1
q.group(0, '', '') do
q.seplist(lines, lambda { q.text ' +'; q.breakable }) do |v|
q.pp v
end
end
else
q.text inspect
end
end
end

module Truffle
class Runner
module Utils
@@ -61,10 +76,20 @@ def print_cmd(cmd, dir, print)
command.map { |v| Shellwords.escape v }
end

puts '$ ' + [*formatted_env, *formatted_cmd].compact.join(' ') + (dir ? " (in #{dir})" : '')
log '$ ' + [*formatted_env, *formatted_cmd].compact.join(' ') + (dir ? " (in #{dir})" : '')
return cmd
end

def dig(data, *keys)
return data if keys.empty?

if data.respond_to?(:[])
dig data[keys[0]], keys[1..-1]
else
raise ArgumentError, "#{data.inspect} does not have :[] method to apply key: #{keys[0]}"
end
end

def dig_deep(data, selection)
return data if selection.nil?

@@ -87,6 +112,59 @@ def dig_deep(data, selection)
end
end

def deep_merge!(a, b, *rest)
[a, b, *rest].reduce { |a, b| deep_merge2! a, b }
end

def deep_merge(a, b, *rest)
[a, b, *rest].reduce { |a, b| deep_merge2 a, b }
end

def deep_merge2!(a, b)
if Hash === a
if Hash === b
return a.merge!(b) { |k, ov, nv| deep_merge2! ov, nv }
else
return a
end
end

if Array === a
if Array === b
return a.concat b
else
return a
end
end

b
end

def deep_merge2(a, b)
if Hash === a
if Hash === b
return a.merge(b) { |k, ov, nv| deep_merge2 ov, nv }
else
return a
end
end

if Array === a
if Array === b
return a + b
else
return a
end
end

b
end

def log(message)
puts 'jtr: ' + message
end

extend self
end

include Utils
@@ -113,7 +191,7 @@ module OptionBlocks
apply_pattern = -> (pattern, old, options) do
Dir.glob(pattern) do |file|
if options[:exclude_pattern].any? { |p| /#{p}/ =~ file }
puts "skipped: #{file}" if verbose?
log "skipped: #{file}" if verbose?
next
end
options[:require] << File.expand_path(file)
@@ -149,6 +227,8 @@ module OptionBlocks
STORE_NEW_VALUE, true],
bundle_options: ['--bundle-options OPTIONS', 'bundle options separated by space', STORE_NEW_VALUE, ''],
configuration: ['--config GEM_NAME', 'Load configuration for specified gem', STORE_NEW_VALUE, nil],
configuration_file: ['--config-file PATH', 'Path to a file defining configuration of gems',
STORE_NEW_VALUE, ROOT.join('lib', 'truffle', 'config.rb')],
dir: ['--dir DIRECTORY', 'Set working directory', STORE_NEW_VALUE, nil],
},
setup: { help: ['-h', '--help', 'Show this message', STORE_NEW_VALUE, false],
@@ -271,9 +351,24 @@ def initialize(argv, options = {})
@subcommand, *argv_after_global = @option_parsers[:global].order argv
@called_from_dir = Dir.pwd
@options[:global][:dir] = File.expand_path(@options[:global][:dir] || @called_from_dir)
@config = Config.new

Dir.chdir @options[:global][:dir] do
puts "pwd: #{Dir.pwd}" if verbose?
log "pwd is #{Dir.pwd}" if verbose?

@config.load @options[:global][:configuration_file].to_s

@gem_name = if @options[:global][:configuration]
@options[:global][:configuration].to_sym
else
candidates = Dir['*.gemspec']
if candidates.size == 1
gem_name, _ = candidates.first.split('.')
gem_name.to_sym
end
end

log "detected gem/app: #{@gem_name}" if @gem_name

load_gem_configuration
load_local_configuration
@@ -296,14 +391,14 @@ def initialize(argv, options = {})

def run
Dir.chdir @options[:global][:dir] do
log "executing #{@subcommand}"
send "subcommand_#{@subcommand}", @argv_after_subcommand
end
end

def print_options
if verbose?
puts 'Options:'
pp @options
log "Options are\n" + @options.pretty_inspect
end
end

@@ -339,14 +434,13 @@ def build_option_parser(parser_options, options_hash)
end

def load_gem_configuration
candidates = @options[:global][:configuration] ? [@options[:global][:configuration]] : Dir['*.gemspec']

if candidates.size == 1
gem_name, _ = candidates.first.split('.')
yaml_path = ROOT.join 'gem_configurations', "#{gem_name}.yaml"
if @gem_name
if (c = @config.for(@gem_name))
apply_hash_to_configuration c
log "loading configuration for #{@gem_name}"
log "configuration is:\n#{c.pretty_inspect}" if verbose?
end
end

apply_yaml_to_configuration(yaml_path)
end

def load_local_configuration
@@ -357,11 +451,16 @@ def load_local_configuration
def apply_yaml_to_configuration(yaml_path)
if yaml_path && File.exist?(yaml_path)
yaml_data = YAML.load_file(yaml_path)
@options = deep_merge @options, yaml_data
puts "loading #{yaml_path}"
apply_hash_to_configuration(yaml_data)
log "loading YAML configuration #{yaml_path}"
end
end

def apply_hash_to_configuration(yaml_data)
# pp '--------', @options, yaml_data, deep_merge(@options, yaml_data)
@options = deep_merge! @options, yaml_data
end

def construct_default_options
OPTION_DEFINITIONS.each_with_object({}) do |(group, group_options), options|
options[group] = default_option_values(group_options)
@@ -392,34 +491,14 @@ def help(key = nil)
exit
end

def deep_merge(a, b)
if Hash === a
if Hash === b
return a.merge!(b) { |k, ov, nv| deep_merge ov, nv }
else
return a
end
end

if Array === a
if Array === b
return a.concat b
else
return a
end
end

b
end

BUNDLER_EVAL_ENV = Object.new

def BUNDLER_EVAL_ENV.gem(name, options)
[name, options]
end

def BUNDLER_EVAL_ENV.process(line)
eval line
def parse_gemfile_line(line)
BUNDLER_EVAL_ENV.send :eval, line
end

def gemfile_use_path!(gems_path)
@@ -428,7 +507,7 @@ def gemfile_use_path!(gems_path)
new_lines = File.read(gemfile).lines.map do |line|
if line =~ /^( +)gem.*git:/
space = $1
gem_name, options = BUNDLER_EVAL_ENV.process(line)
gem_name, options = parse_gemfile_line(line)
repo_name = options[:git].split('/').last
repo_match = "#{gems_path}/bundler/gems/#{repo_name}-*"
repo_path = Dir[repo_match].first
@@ -452,36 +531,36 @@ def subcommand_setup(rest)
bundle_options = @options[:global][:bundle_options].split(' ')
bundle_path = File.expand_path(@options[:global][:truffle_bundle_path])
execute_all = lambda do |cmd|
execute_cmd([{ 'JRUBY_BIN' => JRUBY_BIN.to_s }, cmd])
case cmd
when Proc
cmd.call
when Array, String
execute_cmd([{ 'JRUBY_BIN' => JRUBY_BIN.to_s }, cmd])
else
raise
end
end

real_path = File.join(bundle_path, RUBY_ENGINE)
target_gem_path = File.join(bundle_path, RUBY_ENGINE, '2.3.0')
target_gem_path_22 = File.join(bundle_path, RUBY_ENGINE, '2.2.0')
jruby_truffle_path = File.join(bundle_path, 'jruby+truffle')
mock_path = File.join(bundle_path, @options[:global][:mock_load_path])

FileUtils.mkpath real_path
FileUtils.mkpath mock_path

FileUtils.ln_s RUBY_ENGINE,
jruby_truffle_path,
verbose: verbose?,
force: true
File.symlink RUBY_ENGINE, jruby_truffle_path unless File.exist? jruby_truffle_path

if @options[:setup][:offline]
FileUtils.rmtree target_gem_path
FileUtils.ln_s @options[:setup][:offline_gem_path],
target_gem_path,
verbose: verbose?
File.symlink @options[:setup][:offline_gem_path], target_gem_path
else
File.delete target_gem_path if File.symlink?(target_gem_path)
FileUtils.mkpath target_gem_path
end

FileUtils.ln_s '2.3.0',
File.join(bundle_path, RUBY_ENGINE, '2.2.0'),
verbose: verbose?,
force: true
File.symlink '2.3.0', target_gem_path_22 unless File.exist? target_gem_path_22

@options[:setup][:before].each(&execute_all)

@@ -498,7 +577,7 @@ def subcommand_setup(rest)
print_always: true)

@options[:setup][:file].each do |name, content|
puts "creating file: #{mock_path}/#{name}" if verbose?
log "creating file: #{mock_path}/#{name}" if verbose?
FileUtils.mkpath File.dirname("#{mock_path}/#{name}")
File.write "#{mock_path}/#{name}", content
end
@@ -511,7 +590,7 @@ def subcommand_setup(rest)

true
rescue => e
puts format('%s: %s\n%s', e.class, e.message, e.backtrace.join("\n"))
log format('%s: %s\n%s', e.class, e.message, e.backtrace.join("\n"))
false
end

@@ -544,7 +623,7 @@ def subcommand_run(rest)
core_load_path = jruby_path.join 'truffle/src/main/ruby'

missing_core_load_path = !File.exists?(core_load_path)
puts "Core load path: #{core_load_path} does not exist, fallbacking to --no-use-fs-core" if missing_core_load_path
log "Core load path: #{core_load_path} does not exist, fallbacking to --no-use-fs-core" if missing_core_load_path

truffle_options = [
'-X+T',
@@ -603,7 +682,8 @@ def subcommand_ci(rest)
end

results = batch.each_line.map do |line|
next if line =~ /^#/ || line.strip.empty?
# commented lines result in true
next true if line =~ /^#/ || line.strip.empty?

options = {}
option_parser = build_option_parser OPTION_DEFINITIONS[:ci], options
@@ -635,6 +715,38 @@ def execute_cmd(cmd, dir: nil, raise: true, print_always: false)
super cmd, dir: dir, raise: raise, print: verbose? || print_always
end

class Config
def initialize
@configurations = {}
end

def load(path)
content = File.read path
eval content, binding, path, 1
end

def dedent(string)
indent = string.lines[0] =~ /[^ ]/
string.lines.map { |l| l[indent..-1] }.join
end

def config(gem_name, configuration)
@configurations[gem_name] = configuration
end

def deep_merge(a, b, *rest)
Utils.deep_merge a, b, *rest
end

def deep_merge!(a, b, *rest)
Utils.deep_merge! a, b, *rest
end

def for(gem_name)
@configurations[gem_name]
end
end

class CIEnvironment
include Utils
include OptionBlocks
@@ -687,12 +799,12 @@ def do_definition(name, raise: true)
ci_file = Dir.glob(ROOT.join('gem_ci', "{#{name}}.rb")).first
return false if ci_file.nil?

puts "Using CI definition: #{ci_file}"
log "Using CI definition: #{ci_file}"
catch :cancel_ci! do
begin
instance_eval File.read(ci_file), ci_file, 1
rescue => e
puts format('%s: %s\n%s', e.class, e.message, e.backtrace.join("\n"))
log format('%s: %s\n%s', e.class, e.message, e.backtrace.join("\n"))
end
end

67 changes: 67 additions & 0 deletions spec/ruby/core/encoding/compatible_spec.rb
Original file line number Diff line number Diff line change
@@ -126,6 +126,42 @@
Encoding.compatible?(@str, @str).should == Encoding::UTF_8
end
end

describe "when the first String is empty and the second is not" do
describe "and the first's Encoding is ASCII compatible" do
before :each do
@str = "".force_encoding("utf-8")
end

it "returns the first's encoding when the second String is ASCII only" do
Encoding.compatible?(@str, "def".encode("us-ascii")).should == Encoding::UTF_8
end

it "returns the second's encoding when the second String is not ASCII only" do
Encoding.compatible?(@str, "def".encode("utf-32le")).should == Encoding::UTF_32LE
end
end

describe "when the first's Encoding is not ASCII compatible" do
before :each do
@str = "".force_encoding Encoding::UTF_7
end

it "returns the second string's encoding" do
Encoding.compatible?(@str, "def".encode("us-ascii")).should == Encoding::US_ASCII
end
end
end

describe "when the second String is empty" do
before :each do
@str = "abc".force_encoding("utf-7")
end

it "returns the first Encoding" do
Encoding.compatible?(@str, "").should == Encoding::UTF_7
end
end
end

describe "Encoding.compatible? String, Regexp" do
@@ -174,6 +210,37 @@
end
end

describe "Encoding.compatible? String, Encoding" do
it "returns nil if the String's encoding is not ASCII compatible" do
Encoding.compatible?("abc".encode("utf-32le"), Encoding::US_ASCII).should be_nil
end

it "returns nil if the Encoding is not ASCII compatible" do
Encoding.compatible?("abc".encode("us-ascii"), Encoding::UTF_32LE).should be_nil
end

it "returns the String's encoding if the Encoding is US-ASCII" do
[ [Encoding, "\xff", Encoding::ASCII_8BIT],
[Encoding, "\u3042".encode("utf-8"), Encoding::UTF_8],
[Encoding, "\xa4\xa2".force_encoding("euc-jp"), Encoding::EUC_JP],
[Encoding, "\x82\xa0".force_encoding("shift_jis"), Encoding::Shift_JIS],
].should be_computed_by(:compatible?, Encoding::US_ASCII)
end

it "returns the Encoding if the String's encoding is ASCII compatible and the String is ASCII only" do
str = "abc".encode("utf-8")

Encoding.compatible?(str, Encoding::ASCII_8BIT).should == Encoding::ASCII_8BIT
Encoding.compatible?(str, Encoding::UTF_8).should == Encoding::UTF_8
Encoding.compatible?(str, Encoding::EUC_JP).should == Encoding::EUC_JP
Encoding.compatible?(str, Encoding::Shift_JIS).should == Encoding::Shift_JIS
end

it "returns nil if the String's encoding is ASCII compatible but the string is not ASCII only" do
Encoding.compatible?("\u3042".encode("utf-8"), Encoding::ASCII_8BIT).should be_nil
end
end

describe "Encoding.compatible? Regexp, String" do
it "returns US-ASCII if both are US-ASCII" do
str = "abc".force_encoding("us-ascii")
3 changes: 0 additions & 3 deletions spec/ruby/optional/capi/ext/jruby_truffle.h
Original file line number Diff line number Diff line change
@@ -443,7 +443,6 @@
#undef HAVE_RB_STR2INUM
#undef HAVE_RB_STR_APPEND
#undef HAVE_RB_STR_BUF_CAT
#undef HAVE_RB_STR_BUF_NEW
#undef HAVE_RB_STR_BUF_NEW2
#undef HAVE_RB_STR_CAT
#undef HAVE_RB_STR_CAT2
@@ -466,8 +465,6 @@
#undef HAVE_RB_STR_SPLIT
#undef HAVE_RB_STR_SUBSTR
#undef HAVE_RB_STR_TO_STR
#undef HAVE_RSTRING_LEN
#undef HAVE_RSTRING_PTR
#undef HAVE_STRINGVALUE

#undef HAVE_RB_STR_FREE
6 changes: 0 additions & 6 deletions spec/truffle/tags/core/encoding/compatible_tags.txt

This file was deleted.

26 changes: 13 additions & 13 deletions spec/truffle/tags/core/kernel/chomp_tags.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
fails:Kernel.chomp removes the final newline of $_
fails:Kernel.chomp removes the final carriage return of $_
fails:Kernel.chomp removes the final carriage return, newline of $_
fails:Kernel.chomp removes only the final newline of $_
fails:Kernel.chomp removes the value of $/ from the end of $_
fails:Kernel#chomp removes the final newline of $_
fails:Kernel#chomp removes the final carriage return of $_
fails:Kernel#chomp removes the final carriage return, newline of $_
fails:Kernel#chomp removes only the final newline of $_
fails:Kernel#chomp removes the value of $/ from the end of $_
fails:Kernel#chomp is a private method
fails:Kernel.chomp removes the final carriage return, newline from a multi-byte $_
fails:Kernel#chomp removes the final carriage return, newline from a multi-byte $_
slow:Kernel.chomp removes the final newline of $_
slow:Kernel.chomp removes the final carriage return of $_
slow:Kernel.chomp removes the final carriage return, newline of $_
slow:Kernel.chomp removes only the final newline of $_
slow:Kernel.chomp removes the value of $/ from the end of $_
slow:Kernel#chomp removes the final newline of $_
slow:Kernel#chomp removes the final carriage return of $_
slow:Kernel#chomp removes the final carriage return, newline of $_
slow:Kernel#chomp removes only the final newline of $_
slow:Kernel#chomp removes the value of $/ from the end of $_
slow:Kernel#chomp is a private method
slow:Kernel.chomp removes the final carriage return, newline from a multi-byte $_
slow:Kernel#chomp removes the final carriage return, newline from a multi-byte $_
14 changes: 7 additions & 7 deletions spec/truffle/tags/core/kernel/chop_tags.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
fails:Kernel.chop removes the final character of $_
fails:Kernel.chop removes the final carriage return, newline of $_
fails:#chop is a private method
fails:#chop removes the final character of $_
fails:#chop removes the final carriage return, newline of $_
fails:Kernel.chop removes the final multi-byte character from $_
fails:Kernel#chop removes the final multi-byte character from $_
slow:Kernel.chop removes the final character of $_
slow:Kernel.chop removes the final carriage return, newline of $_
slow:#chop is a private method
slow:#chop removes the final character of $_
slow:#chop removes the final carriage return, newline of $_
slow:Kernel.chop removes the final multi-byte character from $_
slow:Kernel#chop removes the final multi-byte character from $_
6 changes: 0 additions & 6 deletions spec/truffle/tags/core/string/modulo_tags.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
fails:String#% formats single % characters before a newline or NULL as literal %s
fails:String#% raises an error if single % appears anywhere else
fails:String#% raises an error if NULL or \n appear anywhere else in the format string
fails:String#% raises an ArgumentError for unused arguments when $DEBUG is true
fails:String#% always allows unused arguments when positional argument style is used
fails:String#% replaces trailing absolute argument specifier without type with percent sign
fails:String#% raises an ArgumentError when given invalid argument specifiers
fails:String#% raises an ArgumentError when multiple positional argument tokens are given for one format specifier
fails:String#% raises an ArgumentError when multiple width star tokens are given for one format specifier
fails:String#% raises an ArgumentError when a width star token is seen after a width token
fails:String#% raises an ArgumentError when multiple precision tokens are given
fails:String#% raises an ArgumentError when absolute and relative argument numbers are mixed
fails:String#% allows reuse of the one argument multiple via absolute argument numbers
fails:String#% allows positional arguments for width star and precision star arguments
4 changes: 0 additions & 4 deletions spec/truffle/tags/optional/capi/string_tags.txt
Original file line number Diff line number Diff line change
@@ -53,10 +53,6 @@ fails:C-API String function rb_str_subseq returns a byte-indexed substring
fails:C-API String function rb_str_substr returns a substring
fails:C-API String function rb_str_to_str calls #to_str to coerce the value to a String
fails:C-API String function rb_str_to_str raises a TypeError if coercion fails
fails:C-API String function RSTRING_PTR returns a pointer to the string's contents
fails:C-API String function RSTRING_PTR allows changing the characters in the string
fails:C-API String function RSTRING_PTR reflects changes after a rb_funcall
fails:C-API String function RSTRING_PTR returns a pointer to the contents of encoded pointer-sized string
fails:C-API String function RSTRING_LEN returns the size of the string
fails:C-API String function RSTRING_LENINT returns the size of a string
fails:C-API String function StringValue does not call #to_str on a String
8 changes: 8 additions & 0 deletions spec/truffle/truffle.mspec
Original file line number Diff line number Diff line change
@@ -92,6 +92,14 @@ class MSpecScript
"^spec/ruby/library/openssl/x509/name/parse_spec.rb"
]

set :capi, [
"spec/ruby/optional/capi/array_spec.rb",
"spec/ruby/optional/capi/class_spec.rb",
"spec/ruby/optional/capi/module_spec.rb",
"spec/ruby/optional/capi/proc_spec.rb",
"spec/ruby/optional/capi/string_spec.rb",
]

set :truffle, [
"spec/truffle/specs"
]
1 change: 1 addition & 0 deletions test/truffle/compiler/pe/core/encoding_pe.rb
Original file line number Diff line number Diff line change
@@ -15,3 +15,4 @@
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
example "Encoding.compatible?('abc', Encoding::US_ASCII)", Encoding::UTF_8
22 changes: 22 additions & 0 deletions test/truffle/compiler/pe/language/global_pe.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright (c) 2014, 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

$stable_global = 42

example "$stable_global", 42

$almost_stable_global = 1
$almost_stable_global = 2

example "$almost_stable_global", 2

100.times { |i|
$unstable_global = i
}

counter example "$unstable_global"
19 changes: 10 additions & 9 deletions tool/jt.rb
Original file line number Diff line number Diff line change
@@ -467,7 +467,7 @@ def run(*args)
end

if args.delete('--js')
jruby_args << '-J-classpath'
jruby_args << '-J-cp'
jruby_args << Utilities.find_graal_js
end

@@ -580,7 +580,7 @@ def test_compiler(*args)
jruby_opts << '-Xtruffle.graal.warn_unless=false'

if ENV['GRAAL_JS_JAR']
jruby_opts << '-J-classpath'
jruby_opts << '-J-cp'
jruby_opts << Utilities.find_graal_js
end

@@ -632,12 +632,12 @@ def test_integration(env={}, *args)
jruby_opts << '-Xtruffle.graal.warn_unless=false'

if ENV['GRAAL_JS_JAR']
jruby_opts << '-J-classpath'
jruby_opts << '-J-cp'
jruby_opts << Utilities.find_graal_js
end

if ENV['SL_JAR']
jruby_opts << '-J-classpath'
jruby_opts << '-J-cp'
jruby_opts << Utilities.find_sl
end

@@ -661,7 +661,7 @@ def test_gems(env={}, *args)
jruby_opts << '-Xtruffle.graal.warn_unless=false'

if ENV['GRAAL_JS_JAR']
jruby_opts << '-J-classpath'
jruby_opts << '-J-cp'
jruby_opts << Utilities.find_graal_js
end

@@ -1005,12 +1005,13 @@ def collect_sulong_args(env_vars, args, arg_prefix='')
env_vars["JAVACMD"] = Utilities.find_sulong_graal(dir)

if ENV["SULONG_CLASSPATH"]
args << "#{arg_prefix}-J-classpath" << "#{arg_prefix}#{ENV["SULONG_CLASSPATH"]}"
args << "#{arg_prefix}-J-cp" << "#{arg_prefix}#{ENV["SULONG_CLASSPATH"]}"
else
args << "#{arg_prefix}-J-classpath" << "#{arg_prefix}#{dir}/lib/*"
args << "#{arg_prefix}-J-classpath" << "#{arg_prefix}#{dir}/build/sulong.jar"
truffle_jar = File.expand_path("../truffle/mxbuild/dists/truffle-api.jar", dir)
args << "#{arg_prefix}-J-Xbootclasspath/p:#{truffle_jar}"
nfi_classes = File.expand_path('../graal-core/mxbuild/graal/com.oracle.nfi/bin', dir)
args << "#{arg_prefix}-J-classpath" << "#{arg_prefix}#{nfi_classes}"
args << "#{arg_prefix}-J-cp"
args << "#{arg_prefix}#{dir}/lib/*:#{dir}/build/sulong.jar:#{nfi_classes}"
end

args << "#{arg_prefix}-J-XX:-UseJVMCIClassLoader"
Original file line number Diff line number Diff line change
@@ -26,4 +26,5 @@ DOT : '.' ;
HASH : '#' ;
CURLY_KEY : '{' .*? '}' -> mode(DEFAULT_MODE) ;
TYPE : [bBdiouxXeEfgGaAcps] -> mode(DEFAULT_MODE) ;
NOT_TYPE : ~([bBdiouxXeEfgGaAcps%]);
ESCAPED : '%' -> mode(DEFAULT_MODE) ;
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ directive : CURLY_KEY # string
flag*
width=NUMBER?
(DOT precision=(ZERO|NUMBER))?
TYPE # format ;
(type=TYPE | invalidType=NOT_TYPE) # format ;

flag : SPACE
| ZERO
30 changes: 26 additions & 4 deletions truffle/src/main/c/cext/ruby.c
Original file line number Diff line number Diff line change
@@ -55,6 +55,10 @@ VALUE get_rb_eRuntimeError() {

// Conversions

VALUE CHR2FIX(char ch) {
return INT2FIX((unsigned char) ch);
}

int NUM2INT(VALUE value) {
return truffle_invoke_i(RUBY_CEXT, "NUM2INT", value);
}
@@ -117,6 +121,10 @@ int FIXNUM_P(VALUE value) {
return truffle_invoke_b(RUBY_CEXT, "FIXNUM_P", value);
}

int RTEST(VALUE value) {
return truffle_invoke_b(RUBY_CEXT, "RTEST", value);
}

// Float

VALUE rb_float_new(double value) {
@@ -126,16 +134,19 @@ VALUE rb_float_new(double value) {
// String

char *RSTRING_PTR(VALUE string) {
// Needs to return a fake char* which actually calls back into Ruby when read or written
return (char*) truffle_invoke(RUBY_CEXT, "RSTRING_PTR", string);
return truffle_invoke(RUBY_CEXT, "CExtString", string);
}

int RSTRING_LEN(VALUE string) {
return truffle_get_size(string);
return truffle_invoke_i(string, "bytesize");
}

VALUE rb_str_new_cstr(const char *string) {
return (VALUE) truffle_invoke(RUBY_CEXT, "rb_str_new_cstr", truffle_read_string(string));
if (truffle_is_truffle_object((VALUE) string)) {
return truffle_invoke(RUBY_CEXT, "to_ruby_string", string);
} else {
return (VALUE) truffle_invoke(RUBY_CEXT, "rb_str_new_cstr", truffle_read_string(string));
}
}

VALUE rb_intern_str(VALUE string) {
@@ -146,6 +157,10 @@ void rb_str_cat(VALUE string, const char *to_concat, long length) {
truffle_invoke(RUBY_CEXT, "rb_str_cat", string, rb_str_new_cstr(to_concat), length);
}

VALUE rb_str_buf_new(long capacity) {
return rb_str_new_cstr("");
}

// Symbol

ID rb_intern(const char *string) {
@@ -245,6 +260,12 @@ int rb_scan_args(int argc, VALUE *argv, const char *format, ...) {
return truffle_invoke_i(RUBY_CEXT, "rb_scan_args", argc, argv, format /*, where to get args? */);
}

// Calls

VALUE rb_yield(VALUE value) {
return truffle_invoke(RUBY_CEXT, "rb_yield", value);
}

// Instance variables

VALUE rb_iv_get(VALUE object, const char *name) {
@@ -294,6 +315,7 @@ void rb_define_global_const(const char *name, VALUE value) {

void rb_raise(VALUE exception, const char *format, ...) {
truffle_invoke(RUBY_CEXT, "rb_raise", format /*, where to get args? */);
exit(1); // To make the compiler happy
}

// Defining classes, modules and methods
6 changes: 3 additions & 3 deletions truffle/src/main/java/org/jruby/truffle/core/CoreLibrary.java
Original file line number Diff line number Diff line change
@@ -95,7 +95,7 @@
import org.jruby.truffle.extra.TrufflePosixNodesFactory;
import org.jruby.truffle.extra.ffi.PointerPrimitiveNodesFactory;
import org.jruby.truffle.gem.bcrypt.BCryptNodesFactory;
import org.jruby.truffle.interop.CExtNodesFactory;
import org.jruby.truffle.interop.cext.CExtNodesFactory;
import org.jruby.truffle.interop.InteropNodesFactory;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.RubyNode;
@@ -1315,11 +1315,11 @@ public GlobalVariables getGlobalVariables() {
}

public DynamicObject getLoadPath() {
return (DynamicObject) loadPathStorage.value;
return (DynamicObject) loadPathStorage.getValue();
}

public DynamicObject getLoadedFeatures() {
return (DynamicObject) loadedFeaturesStorage.value;
return (DynamicObject) loadedFeaturesStorage.getValue();
}

public DynamicObject getMainObject() {
Original file line number Diff line number Diff line change
@@ -9,11 +9,13 @@
*/
package org.jruby.truffle.core.cast;

import com.oracle.truffle.api.dsl.Fallback;
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.encoding.EncodingOperations;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.RubyNode;

@@ -46,7 +48,14 @@ public Encoding regexpToEncoding(DynamicObject value) {

@Specialization(guards = "isRubyEncoding(value)")
public Encoding rubyEncodingToEncoding(DynamicObject value) {
return Layouts.ENCODING.getEncoding(value);
// While we have a Ruby encoding object, the jcodings encoding it represents might not have been loaded yet.
// So, we can't simply use one of the layout helpers. We need to potentially load the encoding from the
// jcodings database.
return EncodingOperations.getEncoding(value);
}

@Fallback
public Encoding failure(Object value) {
return null;
}
}
206 changes: 170 additions & 36 deletions truffle/src/main/java/org/jruby/truffle/core/encoding/EncodingNodes.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
import org.jruby.truffle.core.format.convert.ToDoubleWithCoercionNodeGen;
import org.jruby.truffle.core.format.convert.ToIntegerNodeGen;
import org.jruby.truffle.core.format.convert.ToStringNodeGen;
import org.jruby.truffle.core.format.exceptions.InvalidFormatException;
import org.jruby.truffle.core.format.format.FormatIntegerBinaryNodeGen;
import org.jruby.truffle.core.format.format.FormatFloatHumanReadableNodeGen;
import org.jruby.truffle.core.format.format.FormatFloatNodeGen;
@@ -77,6 +78,14 @@ public void exitString(PrintfParser.StringContext ctx) {
public void exitFormat(PrintfParser.FormatContext ctx) {
final int width;

if(ctx.invalidType != null){
throw new InvalidFormatException("malformed format string - %" + ctx.invalidType.getText());
} else if (ctx.type == null && ctx.width != null){
throw new InvalidFormatException("malformed format string - %*[0-9]");
} else if (ctx.type == null) {
throw new InvalidFormatException("invalid format character - %");
}

if (ctx.width != null) {
width = Integer.parseInt(ctx.width.getText());
} else {
@@ -101,6 +110,9 @@ public void exitFormat(PrintfParser.FormatContext ctx) {
} else if (flag.ZERO() != null) {
hasZeroFlag = true;
} else if (flag.STAR() != null) {
if (hasStarFlag || ctx.width != null) {
throw new InvalidFormatException("width given twice");
}
hasStarFlag = true;
} else if (flag.PLUS() != null) {
hasPlusFlag = true;
29 changes: 16 additions & 13 deletions truffle/src/main/java/org/jruby/truffle/core/rope/LazyIntRope.java
Original file line number Diff line number Diff line change
@@ -10,7 +10,6 @@
package org.jruby.truffle.core.rope;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import org.jcodings.Encoding;
import org.jcodings.specific.USASCIIEncoding;

@@ -30,8 +29,9 @@ protected LazyIntRope(int value, Encoding encoding, int length) {
assert Integer.toString(value).length() == length;
}

@ExplodeLoop
private static int length(int value) {
final int sign;

if (value < 0) {
/*
* We can't represent -Integer.MIN_VALUE, and we're about to multiple by 10 to add the space needed for the
@@ -43,25 +43,28 @@ private static int length(int value) {
}

value = -value;
value *= 10;
}

// If values were evenly distributed it would make more sense to do this in the reverse order, but they aren't

for (int n = 1, limit = 10; n < 10; n++, limit *= 10) {
if (value < limit) {
return n;
}
sign = 1;
} else {
sign = 0;
}

return 10;
return sign + (value < 1E5 ?
value < 1E2 ?
value < 1E1 ? 1 : 2 :
value < 1E3 ? 3 :
value < 1E4 ? 4 : 5 :
value < 1E7 ?
value < 1E6 ? 6 : 7 :
value < 1E8 ? 8 :
value < 1E9 ? 9 : 10);
}

@Override
public Rope withEncoding(Encoding newEncoding, CodeRange newCodeRange) {
if (newCodeRange != getCodeRange()) {
CompilerDirectives.transferToInterpreterAndInvalidate();
throw new UnsupportedOperationException("Cannot fast-path updating encoding with different code range.");
throw new UnsupportedOperationException(
"Cannot fast-path updating encoding with different code range.");
}

return new LazyIntRope(value, newEncoding, length(value));
2 changes: 2 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/core/rope/Rope.java
Original file line number Diff line number Diff line change
@@ -25,6 +25,8 @@ public abstract class Rope {
protected byte[] bytes;

protected Rope(Encoding encoding, CodeRange codeRange, boolean singleByteOptimizable, int byteLength, int characterLength, int ropeDepth, byte[] bytes) {
assert encoding != null;

this.encoding = encoding;
this.codeRange = codeRange;
this.singleByteOptimizable = singleByteOptimizable;
Original file line number Diff line number Diff line change
@@ -22,10 +22,12 @@
public class StringCodeRangeableWrapper implements CodeRangeable {

private final DynamicObject string;
private final EncodingNodes.CheckEncodingNode checkEncodingNode;

public StringCodeRangeableWrapper(DynamicObject string) {
public StringCodeRangeableWrapper(DynamicObject string, EncodingNodes.CheckEncodingNode checkEncodingNode) {
assert RubyGuards.isRubyString(string);
this.string = string;
this.checkEncodingNode = checkEncodingNode;
}

@Override
@@ -74,20 +76,8 @@ public final void modifyAndKeepCodeRange() {
}

@Override
@TruffleBoundary(throwsControlFlowException = true)
public Encoding checkEncoding(CodeRangeable other) {
final Encoding encoding = EncodingNodes.CompatibleQueryNode.compatibleEncodingForStrings(string, ((StringCodeRangeableWrapper) other).getString());

// TODO (nirvdrum 23-Mar-15) We need to raise a proper Truffle+JRuby exception here, rather than a non-Truffle JRuby exception.
if (encoding == null) {
final RubyContext context = Layouts.MODULE.getFields(Layouts.BASIC_OBJECT.getLogicalClass(string)).getContext();
throw context.getJRubyRuntime().newEncodingCompatibilityError(
String.format("incompatible character encodings: %s and %s",
Layouts.STRING.getRope(string).getEncoding().toString(),
other.getByteList().getEncoding().toString()));
}

return encoding;
return checkEncodingNode.executeCheckEncoding(string, ((StringCodeRangeableWrapper) other).string);
}

@Override

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -35,7 +35,6 @@

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import org.jcodings.Encoding;
import org.jruby.RubyEncoding;
@@ -47,7 +46,6 @@
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeOperations;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.control.RaiseException;
import org.jruby.util.ByteList;

import java.nio.charset.Charset;
@@ -74,8 +72,9 @@ public static String getString(DynamicObject string) {
return RopeOperations.decodeRope(StringOperations.rope(string));
}

public static StringCodeRangeableWrapper getCodeRangeableReadWrite(final DynamicObject string) {
return new StringCodeRangeableWrapper(string) {
public static StringCodeRangeableWrapper getCodeRangeableReadWrite(final DynamicObject string,
final EncodingNodes.CheckEncodingNode checkEncodingNode) {
return new StringCodeRangeableWrapper(string, checkEncodingNode) {
private final ByteList byteList = RopeOperations.toByteListCopy(StringOperations.rope(string));
int codeRange = StringOperations.getCodeRange(string).toInt();

@@ -96,8 +95,9 @@ public ByteList getByteList() {
};
}

public static StringCodeRangeableWrapper getCodeRangeableReadOnly(final DynamicObject string) {
return new StringCodeRangeableWrapper(string) {
public static StringCodeRangeableWrapper getCodeRangeableReadOnly(final DynamicObject string,
final EncodingNodes.CheckEncodingNode checkEncodingNode) {
return new StringCodeRangeableWrapper(string, checkEncodingNode) {
@Override
public ByteList getByteList() {
return StringOperations.getByteListReadOnly(string);
@@ -144,18 +144,6 @@ public static int clampExclusiveIndex(DynamicObject string, int index) {
return ArrayOperations.clampExclusiveIndex(StringOperations.rope(string).byteLength(), index);
}

@TruffleBoundary
public static Encoding checkEncoding(RubyContext context, DynamicObject string, DynamicObject other, Node node) {
final Encoding encoding = EncodingNodes.CompatibleQueryNode.compatibleEncodingForStrings(string, other);

if (encoding == null) {
throw new RaiseException(context.getCoreExceptions().encodingCompatibilityErrorIncompatible(
rope(string).getEncoding(), rope(other).getEncoding(), node));
}

return encoding;
}

@TruffleBoundary
public static Rope encodeRope(CharSequence value, Encoding encoding, CodeRange codeRange) {
// Taken from org.jruby.RubyString#encodeByteList.
Original file line number Diff line number Diff line change
@@ -36,57 +36,47 @@ public class SymbolTable {

private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final WeakHashMap<Rope, WeakReference<DynamicObject>> symbolsTable = new WeakHashMap<>();
private final WeakHashMap<String, WeakReference<DynamicObject>> symbolsTableByString = new WeakHashMap<>();
private final WeakHashMap<String, WeakReference<Rope>> ropesTableByString = new WeakHashMap<>();

public SymbolTable(RubyContext context) {
this.context = context;
}

@CompilerDirectives.TruffleBoundary
@TruffleBoundary
public DynamicObject getSymbol(String string) {
lock.readLock().lock();

Rope rope = null;
try {
final WeakReference<DynamicObject> symbolReference = symbolsTableByString.get(string);

if (symbolReference != null) {
final DynamicObject symbol = symbolReference.get();

if (symbol != null) {
return symbol;
}
}
rope = readRope(string);
} finally {
lock.readLock().unlock();
}

final Rope rope = StringOperations.createRope(string, USASCIIEncoding.INSTANCE);
final DynamicObject symbol = getSymbol(rope);

lock.writeLock().lock();
if (rope == null) {
rope = StringOperations.createRope(string, USASCIIEncoding.INSTANCE);

try {
symbolsTableByString.put(string, new WeakReference<>(symbol));
// we do not need to ensure that the map does not have the string key,
// it may write different objects but logically it writes equal key, value

return symbol;
} finally {
lock.writeLock().unlock();
lock.writeLock().lock();
try {
ropesTableByString.put(string, new WeakReference<>(rope));
} finally {
lock.writeLock().unlock();
}
}

return getSymbol(rope);
}

@CompilerDirectives.TruffleBoundary
@TruffleBoundary
public DynamicObject getSymbol(Rope rope) {
lock.readLock().lock();

try {
final WeakReference<DynamicObject> symbolReference = symbolsTable.get(rope);

if (symbolReference != null) {
final DynamicObject symbol = symbolReference.get();

if (symbol != null) {
return symbol;
}
final DynamicObject symbol = readSymbol(rope);
if (symbol != null) {
return symbol;
}
} finally {
lock.readLock().unlock();
@@ -97,12 +87,9 @@ public DynamicObject getSymbol(Rope rope) {
try {
final WeakReference<DynamicObject> symbolReference = symbolsTable.get(rope);

if (symbolReference != null) {
final DynamicObject symbol = symbolReference.get();

if (symbol != null) {
return symbol;
}
final DynamicObject symbol = readSymbol(rope);
if (symbol != null) {
return symbol;
}

final DynamicObject symbolClass = context.getCoreLibrary().getSymbolClass();
@@ -122,7 +109,17 @@ public DynamicObject getSymbol(Rope rope) {
}
}

@CompilerDirectives.TruffleBoundary
private Rope readRope(String string) {
final WeakReference<Rope> ropeReference = ropesTableByString.get(string);
return ropeReference != null ? ropeReference.get() : null;
}

private DynamicObject readSymbol(Rope rope) {
final WeakReference<DynamicObject> ropeReference = symbolsTable.get(rope);
return ropeReference != null ? ropeReference.get() : null;
}

@TruffleBoundary
public Collection<DynamicObject> allSymbols() {
final Collection<WeakReference<DynamicObject>> symbolReferences;

Original file line number Diff line number Diff line change
@@ -7,13 +7,19 @@
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.interop;
package org.jruby.truffle.interop.cext;

import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.CreateCast;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.frame.FrameInstanceVisitor;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.builtins.CoreClass;
import org.jruby.truffle.builtins.CoreMethod;
@@ -22,6 +28,7 @@
import org.jruby.truffle.core.cast.NameToJavaStringNodeGen;
import org.jruby.truffle.language.RubyConstant;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.arguments.RubyArguments;
import org.jruby.truffle.language.constants.GetConstantNode;
import org.jruby.truffle.language.constants.LookupConstantNode;

@@ -141,6 +148,48 @@ public int long2fix(int num) {

}

@CoreMethod(names = "CExtString", isModuleFunction = true, required = 1)
public abstract static class CExtStringNode extends CoreMethodArrayArgumentsNode {

@Specialization(guards = "isRubyString(string)")
public CExtString cExtString(DynamicObject string) {
return new CExtString(string);
}

}

@CoreMethod(names = "to_ruby_string", isModuleFunction = true, required = 1)
public abstract static class ToRubyStringNode extends CoreMethodArrayArgumentsNode {

@Specialization
public DynamicObject toRubyString(CExtString cExtString) {
return cExtString.getString();
}

@Specialization(guards = "isRubyString(string)")
public DynamicObject toRubyString(DynamicObject string) {
return string;
}

}

@CoreMethod(names = "get_block", isModuleFunction = true)
public abstract static class GetBlockNode extends CoreMethodArrayArgumentsNode {

@TruffleBoundary
@Specialization
public DynamicObject getBlock() {
return Truffle.getRuntime().iterateFrames(new FrameInstanceVisitor<DynamicObject>() {
@Override
public DynamicObject visitFrame(FrameInstance frameInstance) {
Frame frame = frameInstance.getFrame(FrameAccess.READ_ONLY, true);
return RubyArguments.tryGetBlock(frame);
}
});
}

}

@NodeChildren({
@NodeChild(type = RubyNode.class, value = "module"),
@NodeChild(type = RubyNode.class, value = "name")
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.interop.cext;

import com.oracle.truffle.api.interop.ForeignAccess;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.object.DynamicObject;

public final class CExtString implements TruffleObject {

private final DynamicObject string;

public CExtString(DynamicObject string) {
this.string = string;
}

public DynamicObject getString() {
return string;
}

@Override
public ForeignAccess getForeignAccess() {
return CExtStringMessageResolutionForeign.ACCESS;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* 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.interop.cext;

import static org.jruby.truffle.core.string.StringOperations.rope;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.interop.CanResolve;
import com.oracle.truffle.api.interop.MessageResolution;
import com.oracle.truffle.api.interop.Resolve;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.nodes.Node;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.RubyLanguage;
import org.jruby.truffle.core.rope.RopeNodes.GetByteNode;
import org.jruby.truffle.core.rope.RopeNodesFactory.GetByteNodeGen;
import org.jruby.truffle.core.string.StringNodes.SetByteNode;
import org.jruby.truffle.core.string.StringNodesFactory.SetByteNodeFactory;

@MessageResolution(
receiverType = CExtString.class,
language = RubyLanguage.class
)
public class CExtStringMessageResolution {

@CanResolve
public abstract static class Check extends Node {

protected static boolean test(TruffleObject receiver) {
return receiver instanceof CExtString;
}

}

@Resolve(message = "HAS_SIZE")
public static abstract class ForeignHasSizeNode extends Node {

protected Object access(CExtString object) {
return true;
}

}

@Resolve(message = "GET_SIZE")
public static abstract class ForeignGetSizeNode extends Node {

protected Object access(CExtString cExtString) {
return rope(cExtString.getString()).byteLength();
}

}

@Resolve(message = "READ")
public static abstract class ForeignReadNode extends Node {

@Child private GetByteNode getByteNode;

protected Object access(CExtString cExtString, int index) {
return getHelperNode().executeGetByte(rope(cExtString.getString()), index);
}

private GetByteNode getHelperNode() {
if (getByteNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getByteNode = insert(GetByteNodeGen.create(null, null));
}
return getByteNode;
}

}

@Resolve(message = "WRITE")
public static abstract class ForeignWriteNode extends Node {

@Child private Node findContextNode;
@Child private SetByteNode setByteNode;

protected Object access(CExtString cExtString, int index, Object value) {
return getHelperNode().executeSetByte(cExtString.getString(), index, value);
}

private SetByteNode getHelperNode() {
if (setByteNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
findContextNode = insert(RubyLanguage.INSTANCE.unprotectedCreateFindContextNode());
RubyContext context = RubyLanguage.INSTANCE.unprotectedFindContext(findContextNode);
setByteNode = insert(SetByteNodeFactory.create(context, null, null, null, null));
}
return setByteNode;
}

}

}
Original file line number Diff line number Diff line change
@@ -10,10 +10,60 @@

package org.jruby.truffle.language.globals;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.utilities.CyclicAssumption;
import org.jruby.util.cli.Options;

public class GlobalVariableStorage {
public volatile Object value;

private static final int GLOBAL_VARIABLE_MAX_INVALIDATIONS = Options.TRUFFLE_GLOBAL_VARIABLE_MAX_INVALIDATIONS.load();

private final CyclicAssumption unchangedAssumption = new CyclicAssumption("global variable unchanged");
private int changes = 0;

// This really means @CompilationFinal for compilation and volatile in interpreter
@CompilationFinal private volatile boolean assumeConstant = true;

private volatile Object value;

GlobalVariableStorage(Object value) {
this.value = value;
}

public Object getValue() {
return value;
}

public Assumption getUnchangedAssumption() {
return unchangedAssumption.getAssumption();
}

public void setValue(Object value) {
if (assumeConstant) {
if (value != this.value) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.value = value;

synchronized (this) {
if (!assumeConstant) {
// Compiled code didn't see that we do not assumeConstant anymore
return;
}

if (changes <= GLOBAL_VARIABLE_MAX_INVALIDATIONS) {
changes++;
unchangedAssumption.invalidate();
} else {
unchangedAssumption.getAssumption().invalidate();
assumeConstant = false;
}
}
}
} else {
this.value = value;
}
}

}
Loading

0 comments on commit 61cc5b7

Please sign in to comment.