Skip to content

Commit

Permalink
Merge branch 'jruby-1_7'
Browse files Browse the repository at this point in the history
* jruby-1_7:
  avoid (expensive) replaceAll when char replace is possible
  little BacktraceData#transformBacktrace cleanup
  avoid Pattern compiling on every BacktraceData construction - use startsWith
  use static helpers in OSEnvironment for slightly less empty map garbage
  Hack --2.0 net/http gzip problem to avoid multiple version refactoring hell (Fixes #1595, #1216)
  Revert "[build] do not use maven-rc"
  use EMPTY_MAP instead of new (temporary) HashMap instance
  avoid getRuntime() in RubyHash#to_h
  minor cleanup at RubyGlobal
  for better ENV compatibility we should not respond to `to_h` on <= 1.9.3
  ensure that ENV.to_h duplicates the env per MRI ruby
  add failing spec for to_h not duplicating
  [build] do not use maven-rc
  improve test on whether jrubyHome is inside a jar
  Fix NPE when getting parameter.

Conflicts:
	core/src/main/java/org/jruby/RubyGlobal.java
	core/src/main/java/org/jruby/runtime/backtrace/BacktraceData.java
	core/src/main/java/org/jruby/util/OSEnvironment.java
	ext/readline/src/main/java/org/jruby/JRubyApplet.java
	lib/ruby/2.0/net/http/response.rb
kares committed Aug 6, 2015
2 parents 2187665 + 5df5491 commit a9557ff
Showing 7 changed files with 124 additions and 118 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyFile.java
Original file line number Diff line number Diff line change
@@ -634,7 +634,7 @@ public static IRubyObject dirname(ThreadContext context, IRubyObject recv, IRuby
return runtime.newString(dirname(context, jfilename)).infectBy(filename);
}

private static Pattern PROTOCOL_PATTERN = Pattern.compile("^(uri|jar|file|classpath):([^:]*:)?//?.*");
static Pattern PROTOCOL_PATTERN = Pattern.compile("^(uri|jar|file|classpath):([^:]*:)?//?.*");
public static String dirname(ThreadContext context, String jfilename) {
String name = jfilename.replace('\\', '/');
int minPathLength = 1;
101 changes: 53 additions & 48 deletions core/src/main/java/org/jruby/RubyGlobal.java
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@
* Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
* Copyright (C) 2006 Michael Studman <codehaus@michaelstudman.com>
* Copyright (C) 2008 Joseph LaFata <joe@quibb.org>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -70,7 +70,7 @@
import java.util.Map;

/** This class initializes global variables and constants.
*
*
* @author jpetersen
*/
public class RubyGlobal {
@@ -95,7 +95,7 @@ public static void createGlobals(ThreadContext context, Ruby runtime) {
GlobalVariables globals = runtime.getGlobalVariables();

runtime.defineGlobalConstant("TOPLEVEL_BINDING", runtime.newBinding());

runtime.defineGlobalConstant("TRUE", runtime.getTrue());
runtime.defineGlobalConstant("FALSE", runtime.getFalse());
runtime.defineGlobalConstant("NIL", runtime.getNil());
@@ -129,7 +129,7 @@ public static void createGlobals(ThreadContext context, Ruby runtime) {

runtime.defineGlobalConstant("RELEASE_DATE", release);
runtime.defineGlobalConstant("PLATFORM", platform);

IRubyObject jrubyVersion = runtime.newString(Constants.VERSION).freeze(context);
IRubyObject jrubyRevision = runtime.newString(Constants.REVISION).freeze(context);
runtime.defineGlobalConstant("JRUBY_VERSION", jrubyVersion);
@@ -139,11 +139,11 @@ public static void createGlobals(ThreadContext context, Ruby runtime) {
runtime.defineGlobalConstant("RUBY_REVISION", runtime.newFixnum(Constants.RUBY_REVISION));
runtime.defineGlobalConstant("RUBY_ENGINE", engine);
runtime.defineGlobalConstant("RUBY_ENGINE_VERSION", jrubyVersion);

RubyInstanceConfig.Verbosity verbosity = runtime.getInstanceConfig().getVerbosity();
runtime.defineVariable(new WarningGlobalVariable(runtime, "$-W", verbosity), GLOBAL);

final GlobalVariable kcodeGV;
final GlobalVariable kcodeGV;
kcodeGV = new NonEffectiveGlobalVariable(runtime, "$KCODE", runtime.getNil());

runtime.defineVariable(kcodeGV, GLOBAL);
@@ -168,7 +168,7 @@ public static void createGlobals(ThreadContext context, Ruby runtime) {
} else {
runtime.defineVariable(new GlobalVariable(runtime, "$;", RubyRegexp.newRegexp(runtime, runtime.getInstanceConfig().getInputFieldSeparator(), new RegexpOptions())), GLOBAL);
}

RubyInstanceConfig.Verbosity verbose = runtime.getInstanceConfig().getVerbosity();
IRubyObject verboseValue = null;
if (verbose == RubyInstanceConfig.Verbosity.NIL) {
@@ -181,7 +181,7 @@ public static void createGlobals(ThreadContext context, Ruby runtime) {
runtime.defineVariable(new VerboseGlobalVariable(runtime, "$VERBOSE", verboseValue), GLOBAL);
runtime.defineVariable(new VerboseGlobalVariable(runtime, "$-v", verboseValue), GLOBAL);
runtime.defineVariable(new VerboseGlobalVariable(runtime, "$-w", verboseValue), GLOBAL);

IRubyObject debug = runtime.newBoolean(runtime.getInstanceConfig().isDebug());
runtime.defineVariable(new DebugGlobalVariable(runtime, "$DEBUG", debug), GLOBAL);
runtime.defineVariable(new DebugGlobalVariable(runtime, "$-d", debug), GLOBAL);
@@ -212,26 +212,26 @@ public static void createGlobals(ThreadContext context, Ruby runtime) {
runtime.defineVariable(new LoadPath(runtime, "$:"), GLOBAL);
runtime.defineVariable(new LoadPath(runtime, "$-I"), GLOBAL);
runtime.defineVariable(new LoadPath(runtime, "$LOAD_PATH"), GLOBAL);

runtime.defineVariable(new MatchMatchGlobalVariable(runtime, "$&"), FRAME);
runtime.defineVariable(new PreMatchGlobalVariable(runtime, "$`"), FRAME);
runtime.defineVariable(new PostMatchGlobalVariable(runtime, "$'"), FRAME);
runtime.defineVariable(new LastMatchGlobalVariable(runtime, "$+"), FRAME);
runtime.defineVariable(new BackRefGlobalVariable(runtime, "$~"), FRAME);

// On platforms without a c-library accessable through JNA, getpid will return hashCode
// On platforms without a c-library accessable through JNA, getpid will return hashCode
// as $$ used to. Using $$ to kill processes could take down many runtimes, but by basing
// $$ on getpid() where available, we have the same semantics as MRI.
globals.defineReadonly("$$", new PidAccessor(runtime), GLOBAL);

// after defn of $stderr as the call may produce warnings
defineGlobalEnvConstants(runtime);
// Fixme: Do we need the check or does Main.java not call this...they should consolidate

// Fixme: Do we need the check or does Main.java not call this...they should consolidate
if (globals.get("$*").isNil()) {
globals.defineReadonly("$*", new ValueAccessor(runtime.newArray()), GLOBAL);
}

globals.defineReadonly("$-p",
new ValueAccessor(runtime.newBoolean(runtime.getInstanceConfig().isAssumePrinting())),
GLOBAL);
@@ -291,24 +291,25 @@ private static Channel prepareStdioChannel(Ruby runtime, STDIO stdio, Object str
}
}


@SuppressWarnings("unchecked")
private static void defineGlobalEnvConstants(Ruby runtime) {
OSEnvironment environment = new OSEnvironment();
Map<RubyString, RubyString> environmentVariableMap = environment.getEnvironmentVariableMap(runtime);

CaseInsensitiveStringOnlyRubyHash env = new CaseInsensitiveStringOnlyRubyHash(runtime,
environmentVariableMap,
runtime.getNil(),
runtime.getInstanceConfig().isNativeEnabled() &&
runtime.getInstanceConfig().isUpdateNativeENVEnabled() );
Map<RubyString, RubyString> environmentVariableMap = OSEnvironment.environmentVariableMap(runtime);
RubyHash env = new CaseInsensitiveStringOnlyRubyHash(
runtime, environmentVariableMap, runtime.getNil(),
runtime.getInstanceConfig().isNativeEnabled() && runtime.getInstanceConfig().isUpdateNativeENVEnabled()
);
env.getSingletonClass().defineAnnotatedMethods(CaseInsensitiveStringOnlyRubyHash.class);
runtime.defineGlobalConstant("ENV", env);
runtime.setENV(env);

// Define System.getProperties() in ENV_JAVA
Map<RubyString, RubyString> systemProps = environment.getSystemPropertiesMap(runtime);
RubyHash systemPropsHash = new ReadOnlySystemPropertiesHash(runtime, systemProps, runtime.getNil());
systemPropsHash.setFrozen(true);
runtime.defineGlobalConstant("ENV_JAVA", systemPropsHash);
Map<RubyString, RubyString> systemPropertiesMap = OSEnvironment.systemPropertiesMap(runtime);
RubyHash envJava = new ReadOnlySystemPropertiesHash(
runtime, systemPropertiesMap, runtime.getNil()
);
envJava.setFrozen(true);
runtime.defineGlobalConstant("ENV_JAVA", envJava);
}

/**
@@ -332,9 +333,13 @@ public IRubyObject op_aset(ThreadContext context, IRubyObject key, IRubyObject v
return case_aware_op_aset(context, key, value, false);
}

@JRubyMethod
@JRubyMethod(name = "to_s")
public IRubyObject to_s(ThreadContext context) {
return context.runtime.newString("ENV");
}

@Override
public IRubyObject to_s(){
public IRubyObject to_s() {
return getRuntime().newString("ENV");
}

@@ -356,7 +361,7 @@ public static class StringOnlyRubyHash extends RubyHash {
// class and not in the caseinsensitive hash. In order to not refactor
// both of these maps we will pass in a flag to specify whether we want
// the op_aset to also update the real ENV map via setenv/unsetenv.
private boolean updateRealENV;
private final boolean updateRealENV;

public StringOnlyRubyHash(Ruby runtime, Map<RubyString, RubyString> valueMap, IRubyObject defaultValue, boolean updateRealENV) {
super(runtime, valueMap, defaultValue);
@@ -398,15 +403,16 @@ protected IRubyObject case_aware_op_aref(ThreadContext context, IRubyObject key,
return super.op_aref(context, key);
}

protected IRubyObject case_aware_op_aset(ThreadContext context, IRubyObject key, IRubyObject value, boolean caseSensitive) {
protected IRubyObject case_aware_op_aset(ThreadContext context,
IRubyObject key, final IRubyObject value, boolean caseSensitive) {
if (!key.respondsTo("to_str")) {
throw getRuntime().newTypeError("can't convert " + key.getMetaClass() + " into String");
}
if (!value.respondsTo("to_str") && !value.isNil()) {
throw getRuntime().newTypeError("can't convert " + value.getMetaClass() + " into String");
}

if (! caseSensitive) {
if (!caseSensitive) {
key = getCorrectKey(key, context);
}

@@ -415,14 +421,14 @@ protected IRubyObject case_aware_op_aset(ThreadContext context, IRubyObject key,
}

IRubyObject keyAsStr = normalizeEnvString(Helpers.invoke(context, key, "to_str"));
IRubyObject valueAsStr = value.isNil() ? getRuntime().getNil() :
IRubyObject valueAsStr = value.isNil() ? context.nil :
normalizeEnvString(Helpers.invoke(context, value, "to_str"));

if (updateRealENV) {
POSIX posix = getRuntime().getPosix();
POSIX posix = context.runtime.getPosix();
String keyAsJava = keyAsStr.asJavaString();
// libc (un)setenv is not reentrant, so we need to synchronize across the entire JVM (JRUBY-5933)
if (valueAsStr == getRuntime().getNil()) {
if (valueAsStr == context.nil) {
synchronized (Object.class) { posix.unsetenv(keyAsJava); }
} else {
synchronized (Object.class) { posix.setenv(keyAsJava, valueAsStr.asJavaString(), 1); }
@@ -458,9 +464,8 @@ private IRubyObject normalizeEnvString(IRubyObject str) {
RubyString newStr = getRuntime().newString(new ByteList(str.toString().getBytes(), enc));
newStr.setFrozen(true);
return newStr;
} else {
return str;
}
return str;
}
}

@@ -502,13 +507,13 @@ private static class LastExitStatusVariable extends GlobalVariable {
public LastExitStatusVariable(Ruby runtime, String name) {
super(runtime, name, runtime.getNil());
}

@Override
public IRubyObject get() {
IRubyObject lastExitStatus = runtime.getCurrentContext().getLastExitStatus();
return lastExitStatus == null ? runtime.getNil() : lastExitStatus;
}

@Override
public IRubyObject set(IRubyObject lastExitStatus) {
throw runtime.newNameError("$? is a read-only variable", "$?");
@@ -519,7 +524,7 @@ private static class MatchMatchGlobalVariable extends GlobalVariable {
public MatchMatchGlobalVariable(Ruby runtime, String name) {
super(runtime, name, runtime.getNil());
}

@Override
public IRubyObject get() {
return RubyRegexp.last_match(runtime.getCurrentContext().getBackRef());
@@ -530,7 +535,7 @@ private static class PreMatchGlobalVariable extends GlobalVariable {
public PreMatchGlobalVariable(Ruby runtime, String name) {
super(runtime, name, runtime.getNil());
}

@Override
public IRubyObject get() {
return RubyRegexp.match_pre(runtime.getCurrentContext().getBackRef());
@@ -541,7 +546,7 @@ private static class PostMatchGlobalVariable extends GlobalVariable {
public PostMatchGlobalVariable(Ruby runtime, String name) {
super(runtime, name, runtime.getNil());
}

@Override
public IRubyObject get() {
return RubyRegexp.match_post(runtime.getCurrentContext().getBackRef());
@@ -552,7 +557,7 @@ private static class LastMatchGlobalVariable extends GlobalVariable {
public LastMatchGlobalVariable(Ruby runtime, String name) {
super(runtime, name, runtime.getNil());
}

@Override
public IRubyObject get() {
return RubyRegexp.match_last(runtime.getCurrentContext().getBackRef());
@@ -563,7 +568,7 @@ private static class BackRefGlobalVariable extends GlobalVariable {
public BackRefGlobalVariable(Ruby runtime, String name) {
super(runtime, name, runtime.getNil());
}

@Override
public IRubyObject get() {
return Helpers.getBackref(runtime, runtime.getCurrentContext());
@@ -610,7 +615,7 @@ public IRubyObject set(IRubyObject value) {
!(JavaUtil.isJavaObject(value) && JavaUtil.unwrapJavaObject(value) instanceof Throwable)) {
throw runtime.newTypeError("assigning non-exception to $!");
}

return runtime.getCurrentContext().setErrorInfo(value);
}

@@ -674,7 +679,7 @@ public VerboseGlobalVariable(Ruby runtime, String name, IRubyObject initialValue
super(runtime, name, initialValue);
set(initialValue);
}

@Override
public IRubyObject get() {
return runtime.getVerbose();
@@ -691,7 +696,7 @@ public IRubyObject set(IRubyObject newValue) {
return newValue;
}
}

private static class WarningGlobalVariable extends ReadonlyGlobalVariable {
public WarningGlobalVariable(Ruby runtime, String name, RubyInstanceConfig.Verbosity verbosity) {
super(runtime, name,
@@ -779,7 +784,7 @@ public IRubyObject set(IRubyObject value) {
if (value == get()) {
return value;
}

return super.set(value);
}
}
@@ -808,7 +813,7 @@ private static class LoadPath extends ReadonlyGlobalVariable {
public LoadPath(Ruby runtime, String name) {
super(runtime, name, null);
}

/**
* @see org.jruby.runtime.GlobalVariable#get()
*/
@@ -822,7 +827,7 @@ private static class LoadedFeatures extends ReadonlyGlobalVariable {
public LoadedFeatures(Ruby runtime, String name) {
super(runtime, name, null);
}

/**
* @see org.jruby.runtime.GlobalVariable#get()
*/
3 changes: 2 additions & 1 deletion core/src/main/java/org/jruby/RubyHash.java
Original file line number Diff line number Diff line change
@@ -948,7 +948,8 @@ public RubyHash to_hash() {

@JRubyMethod
public RubyHash to_h(ThreadContext context) {
return getType() == getRuntime().getHash() ? this : newHash(getRuntime()).replace(context, this);
final Ruby runtime = context.runtime;
return getType() == runtime.getHash() ? this : newHash(runtime).replace(context, this);
}

@Override
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/RubyInstanceConfig.java
Original file line number Diff line number Diff line change
@@ -320,7 +320,7 @@ private String calculateJRubyHome() {
// RegularFileResource absolutePath will canonicalize resources so that will change c: paths to C:.
// We will cannonicalize on windows so that jruby.home is also C:.
// assume all those uri-like pathnames are already in absolute form
if (Platform.IS_WINDOWS && !newJRubyHome.startsWith("jar:") && !newJRubyHome.startsWith("file:") && !newJRubyHome.startsWith("classpath:") && !newJRubyHome.startsWith("uri:")) {
if (Platform.IS_WINDOWS && !RubyFile.PROTOCOL_PATTERN.matcher(newJRubyHome).matches()) {
File file = new File(newJRubyHome);

try {
@@ -667,7 +667,7 @@ public void setEnvironment(Map<String, String> newEnvironment) {
}

private void setupEnvironment(String jrubyHome) {
if (!new File(jrubyHome).exists() && !environment.containsKey("RUBY")) {
if (RubyFile.PROTOCOL_PATTERN.matcher(jrubyHome).matches() && !environment.containsKey("RUBY")) {
// the assumption that if JRubyHome is not a regular file that jruby
// got launched in an embedded fashion
environment.put("RUBY", ClasspathLauncher.jrubyCommand(defaultClassLoader()) );
26 changes: 9 additions & 17 deletions core/src/main/java/org/jruby/runtime/backtrace/BacktraceData.java
Original file line number Diff line number Diff line change
@@ -5,9 +5,7 @@

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

public class BacktraceData implements Serializable {
private RubyStackTraceElement[] backtraceElements;
@@ -17,8 +15,6 @@ public class BacktraceData implements Serializable {
private final boolean maskNative;
private final boolean includeNonFiltered;

private final Pattern FILTER_CLASSES = Pattern.compile("^(org\\.jruby)|(sun\\.reflect)");

public BacktraceData(StackTraceElement[] javaTrace, BacktraceElement[] rubyTrace, boolean fullTrace, boolean maskNative, boolean includeNonFiltered) {
this.javaTrace = javaTrace;
this.rubyTrace = rubyTrace;
@@ -42,11 +38,10 @@ public RubyStackTraceElement[] getBacktrace(Ruby runtime) {
}

private RubyStackTraceElement[] constructBacktrace(Map<String, Map<String, String>> boundMethods) {
List<RubyStackTraceElement> trace = new ArrayList<RubyStackTraceElement>(javaTrace.length);
ArrayList<RubyStackTraceElement> trace = new ArrayList<RubyStackTraceElement>(javaTrace.length);

// used for duplicating the previous Ruby frame when masking native calls
boolean dupFrame = false;
String dupFrameName = null;
boolean dupFrame = false; String dupFrameName = null;

// a running index into the Ruby backtrace stack, incremented for each
// interpreter frame we encounter in the Java backtrace.
@@ -158,7 +153,6 @@ private RubyStackTraceElement[] constructBacktrace(Map<String, Map<String, Strin
line,
false
));
continue;
}
}

@@ -177,18 +171,16 @@ public static String getBoundMethodName(Map<String,Map<String,String>> boundMeth
private static String packagedFilenameFromElement(String filename, String className) {
// stick package on the beginning
if (filename == null) {
filename = className.replaceAll("\\.", "/");
} else {
int lastDot = className.lastIndexOf('.');
if (lastDot != -1) {
filename = className.substring(0, lastDot + 1).replaceAll("\\.", "/") + filename;
}
return className.replace('.', '/');
}

return filename;
int lastDot = className.lastIndexOf('.');
if (lastDot == -1) return filename;
return className.substring(0, lastDot + 1).replace('.', '/') + filename;
}

private boolean isFilteredClass(String className) {
return FILTER_CLASSES.matcher(className).find();
// ^(org\\.jruby)|(sun\\.reflect)
private static boolean isFilteredClass(final String className) {
return className.startsWith("org.jruby") || className.startsWith("sun.reflect") ;
}
}
101 changes: 52 additions & 49 deletions core/src/main/java/org/jruby/util/OSEnvironment.java
Original file line number Diff line number Diff line change
@@ -28,53 +28,37 @@

package org.jruby.util;

import org.jcodings.Encoding;
import org.jruby.Ruby;

import jnr.posix.util.Platform;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import jnr.posix.util.Platform;
import org.jcodings.Encoding;
import org.jruby.Ruby;
import org.jruby.RubyString;

public class OSEnvironment {

/**
* Returns the environment as a hash of Ruby strings.
*
* @param runtime
*/
public Map<RubyString, RubyString> getEnvironmentVariableMap(Ruby runtime) {
if (runtime.getInstanceConfig().getEnvironment() != null) {
return getAsMapOfRubyStrings(runtime, runtime.getInstanceConfig().getEnvironment());
}

final Map<RubyString, RubyString> envs;
// fall back on empty env when security disallows environment var access (like in an applet)
if (Ruby.isSecurityRestricted()) {
envs = new HashMap<RubyString, RubyString>();
} else {
Map<String, String> variables = System.getenv();
envs = getAsMapOfRubyStrings(runtime, variables);
}
public static Map<RubyString, RubyString> environmentVariableMap(Ruby runtime) {
@SuppressWarnings("unchecked")
Map<String, String> env = runtime.getInstanceConfig().getEnvironment();
if ( env != null ) return asMapOfRubyStrings(runtime, env);

return envs;
if ( Ruby.isSecurityRestricted() ) return Collections.emptyMap();

return asMapOfRubyStrings(runtime, System.getenv());
}

/**
* Returns java system properties as a Map<RubyString,RubyString>.
* @param runtime
* @return the java system properties as a Map<RubyString,RubyString>.
*/
public Map<RubyString, RubyString> getSystemPropertiesMap(Ruby runtime) {
if (Ruby.isSecurityRestricted()) {
return new HashMap<RubyString, RubyString>();
} else {
return getAsMapOfRubyStrings(runtime, propertiesToStringMap(System.getProperties()));
}
public Map<RubyString, RubyString> getEnvironmentVariableMap(Ruby runtime) {
Map envMap = OSEnvironment.environmentVariableMap(runtime);
return envMap == Collections.EMPTY_MAP ? new HashMap(4) : envMap;
}

public static Map<String, String> propertiesToStringMap(Properties properties) {
@@ -87,46 +71,65 @@ public static Map<String, String> propertiesToStringMap(Properties properties) {
return map;
}

private static Map<RubyString, RubyString> getAsMapOfRubyStrings(Ruby runtime, Map<String, String> map) {
Map<RubyString, RubyString> envs = new HashMap<RubyString, RubyString>();
/**
* Returns java system properties as a Map<RubyString,RubyString>.
* @param runtime
* @return the java system properties as a Map<RubyString,RubyString>.
*/
public static Map<RubyString, RubyString> systemPropertiesMap(Ruby runtime) {
if ( Ruby.isSecurityRestricted() ) return Collections.emptyMap();
return asMapOfRubyStrings(runtime, (Properties) System.getProperties().clone());
}

public Map<RubyString, RubyString> getSystemPropertiesMap(Ruby runtime) {
Map sysMap = OSEnvironment.systemPropertiesMap(runtime);
return sysMap == Collections.EMPTY_MAP ? new HashMap(4) : sysMap;
}

private static Map<RubyString, RubyString> asMapOfRubyStrings(final Ruby runtime, final Map<?, ?> map) {
@SuppressWarnings("unchecked")
final Map<RubyString, RubyString> rubyMap = new HashMap(map.size() + 2);
Encoding encoding = runtime.getEncodingService().getLocaleEncoding();

// On Windows, map doesn't have corresponding keys for these
if (Platform.IS_WINDOWS) {
// these may be null when in a restricted environment (JRUBY-6514)
String home = SafePropertyAccessor.getProperty("user.home");
String user = SafePropertyAccessor.getProperty("user.name");
addRubyKeyValuePair(runtime, envs, "HOME", home == null ? "/" : home, encoding);
addRubyKeyValuePair(runtime, envs, "USER", user == null ? "" : user, encoding);
putRubyKeyValuePair(runtime, rubyMap, "HOME", home == null ? "/" : home, encoding);
putRubyKeyValuePair(runtime, rubyMap, "USER", user == null ? "" : user, encoding);
}

for (Entry<String, String> entry : map.entrySet()) {
Object tmp = entry.getKey();
if (!(tmp instanceof String)) continue; // Java devs can stuff non-string objects into env
String key = (String) tmp;
for (Map.Entry<?, ?> entry : map.entrySet()) {
Object val = entry.getKey();

if ( ! (val instanceof String) ) continue; // Java devs can stuff non-string objects into env
final String key = (String) val;

if (Platform.IS_WINDOWS && key.startsWith("=")) continue;

tmp = entry.getValue();
if (!(tmp instanceof String)) continue; // Java devs can stuff non-string objects into env

addRubyKeyValuePair(runtime, envs, key, (String) tmp, encoding);
val = entry.getValue();
if ( ! (val instanceof String) ) continue; // Java devs can stuff non-string objects into env

putRubyKeyValuePair(runtime, rubyMap, key, (String) val, encoding);
}

return envs;
return rubyMap;
}

private static void addRubyKeyValuePair(Ruby runtime, Map<RubyString, RubyString> map, String key, String value, Encoding encoding) {

private static void putRubyKeyValuePair(Ruby runtime,
final Map<RubyString, RubyString> map,
String key, String value, Encoding encoding) {
ByteList keyBytes = new ByteList(key.getBytes(), encoding);
ByteList valueBytes = new ByteList(value.getBytes(), encoding);

RubyString keyString = runtime.newString(keyBytes);
RubyString valueString = runtime.newString(valueBytes);

keyString.setFrozen(true);
valueString.setFrozen(true);

map.put(keyString, valueString);
}

}
5 changes: 5 additions & 0 deletions spec/ruby/core/env/shared/to_hash.rb
Original file line number Diff line number Diff line change
@@ -19,4 +19,9 @@
h = ENV.send(@method)
h.object_id.should_not == ENV.object_id
end

it "duplicates the ENV when converting to a Hash" do
h = ENV.send(@method)
h.object_id.should_not == ENV.object_id
end
end

0 comments on commit a9557ff

Please sign in to comment.