Skip to content

Commit

Permalink
Showing 58 changed files with 12,017 additions and 9,360 deletions.
10 changes: 5 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -9,8 +9,8 @@ cache:
before_install:
- rm ~/.m2/settings.xml
- export MAVEN_SKIP_RC=true
- mvn -Xmx32M -v | grep 1.7.0; if [ $? = 0 ]; then export MAVEN_OPTS="-XX:MaxPermSize=200M"; else export MAVEN_OPTS="-XX:MaxMetaspaceSize=200M -XX:CompressedClassSpaceSize=200M"; fi
- export MAVEN_OPTS="-Xmx512M $MAVEN_OPTS"
- mvn -Xmx32M -v | grep 1.7.0; if [ $? = 0 ]; then export MAVEN_OPTS="-XX:MaxPermSize=180M"; else export MAVEN_OPTS="-XX:MaxMetaspaceSize=180M -XX:CompressedClassSpaceSize=180M"; fi
- export MAVEN_OPTS="-Xmx500M $MAVEN_OPTS"

before_script:
- unset GEM_PATH GEM_HOME IRBRC JRUBY_OPTS
@@ -28,7 +28,7 @@ os:

env:
global:
- JAVA_OPTS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xmn48M -Xmx512M"
- JAVA_OPTS="-XX:+TieredCompilation -XX:TieredStopAtLevel=1 -Xmn36M -Xmx512M"
- MALLOC_ARENA_MAX=2
matrix:
- PHASE='-Ptest'
@@ -76,8 +76,8 @@ matrix:
- env: COMMAND=test/check_versions.sh
jdk: oraclejdk8
allow_failures:
- env: PHASE='-Pj2ee'
jdk: oraclejdk7
#- env: PHASE='-Pj2ee'
# jdk: oraclejdk7
# NOTE: build seems to never start (waited for any to finish for more than a day) - probably a travis-ci bug
#- env: PHASE='-Pmain'
# sudo: required
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ For [`rbenv`](https://github.com/sstephenson/rbenv) you will need the
package manager can provide these. Then you can run:

```
$ rbenv install jruby-9.1.9.0
$ rbenv install jruby-9.1.10.0
```

For [`rvm`](https://rvm.io) you can simply do:
5 changes: 4 additions & 1 deletion core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -376,6 +376,10 @@ public final IRubyObject callMethod(String name, IRubyObject... args) {
return Helpers.invoke(getRuntime().getCurrentContext(), this, name, args);
}

public final IRubyObject callMethod(String name, IRubyObject arg) {
return Helpers.invoke(getRuntime().getCurrentContext(), this, name, arg);
}

public final IRubyObject callMethod(String name) {
return Helpers.invoke(getRuntime().getCurrentContext(), this, name);
}
@@ -2198,7 +2202,6 @@ public IRubyObject untaint(ThreadContext context) {
* from prog.rb:3
*/
public IRubyObject freeze(ThreadContext context) {
Ruby runtime = context.runtime;
if ((flags & FROZEN_F) == 0) {
flags |= FROZEN_F;
}
5 changes: 3 additions & 2 deletions core/src/main/java/org/jruby/RubyClass.java
Original file line number Diff line number Diff line change
@@ -197,8 +197,9 @@ public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
* constructor (Ruby, RubyClass) on the given class via a static
* __allocate__ method intermediate.
*
* @param clazz The class from which to grab a standard Ruby __allocate__
* method.
* @param clazz The class from which to grab a standard Ruby __allocate__ method.
*
* @note Used with `jrubyc --java` generated (interoperability) class files.
*/
public void setRubyStaticAllocator(final Class<?> clazz) {
try {
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyDir.java
Original file line number Diff line number Diff line change
@@ -79,7 +79,7 @@ public class RubyDir extends RubyObject {

private final static Encoding UTF8 = UTF8Encoding.INSTANCE;

private static Pattern PROTOCOL_PATTERN = Pattern.compile("^(uri|jar|file|classpath):([^:]*:)?//?.*");
private static final Pattern PROTOCOL_PATTERN = Pattern.compile("^(uri|jar|file|classpath):([^:]*:)?//?.*");

public RubyDir(Ruby runtime, RubyClass type) {
super(runtime, type);
67 changes: 31 additions & 36 deletions core/src/main/java/org/jruby/RubyFile.java
Original file line number Diff line number Diff line change
@@ -35,14 +35,11 @@
***** END LICENSE BLOCK *****/
package org.jruby;

import jnr.constants.platform.OpenFlags;
import jnr.posix.POSIX;
import org.jcodings.Encoding;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.channels.Channels;
@@ -61,13 +58,14 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import jnr.constants.platform.OpenFlags;
import jnr.posix.POSIX;
import jnr.posix.util.Platform;
import org.jcodings.Encoding;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import jnr.posix.FileStat;
import jnr.posix.util.Platform;
import org.jruby.runtime.Block;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.JavaSites;
import org.jruby.runtime.JavaSites.FileSites;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
@@ -264,7 +262,7 @@ public RubyFile(Ruby runtime, RubyClass type) {

// XXX This constructor is a hack to implement the __END__ syntax.
// Converting a reader back into an InputStream doesn't generally work.
public RubyFile(Ruby runtime, String path, final Reader reader) {
RubyFile(Ruby runtime, String path, final Reader reader) {
this(runtime, path, new InputStream() {
@Override
public int read() throws IOException {
@@ -657,42 +655,38 @@ public static IRubyObject dirname(ThreadContext context, IRubyObject recv, IRuby
return runtime.newString(dirname(context, jfilename)).infectBy(filename);
}

public static Pattern PROTOCOL_PATTERN = Pattern.compile(URI_PREFIX_STRING + ".*");
static final Pattern PROTOCOL_PATTERN = Pattern.compile(URI_PREFIX_STRING + ".*");

public static String dirname(ThreadContext context, String jfilename) {
final Ruby runtime = context.runtime;
final String separator = runtime.getClass("File").getConstant("SEPARATOR").toString();
final RubyClass File = context.runtime.getFile();
final String separator = File.getConstant("SEPARATOR").toString();
final char separatorChar = separator.charAt(0);
String altSeparator = null;
char altSeparatorChar = '\0';
final IRubyObject rbAltSeparator = runtime.getClass("File").getConstant("ALT_SEPARATOR");
final IRubyObject rbAltSeparator = File.getConstant("ALT_SEPARATOR");
if (rbAltSeparator != context.nil) {
altSeparator = rbAltSeparator.toString();
altSeparatorChar = altSeparator.charAt(0);
altSeparator = rbAltSeparator.toString();
altSeparatorChar = altSeparator.charAt(0);
}
String name = jfilename;
if (altSeparator != null) {
name = jfilename.replace(altSeparator, separator);
name = jfilename.replace(altSeparator, separator);
}
int minPathLength = 1;
boolean trimmedSlashes = false;

boolean startsWithSeparator = false;

if (!name.isEmpty()) {
startsWithSeparator = name.charAt(0) == separatorChar;
startsWithSeparator = name.charAt(0) == separatorChar;
}

boolean startsWithUNCOnWindows = Platform.IS_WINDOWS && startsWith(name, separatorChar, separatorChar);

if (startsWithUNCOnWindows) {
minPathLength = 2;
}
if (startsWithUNCOnWindows) minPathLength = 2;

boolean startsWithDriveLetterOnWindows = startsWithDriveLetterOnWindows(name);

if (startsWithDriveLetterOnWindows) {
minPathLength = 3;
}
if (startsWithDriveLetterOnWindows) minPathLength = 3;

// jar like paths
if (name.contains(".jar!" + separator)) {
@@ -819,7 +813,7 @@ public static IRubyObject extname(ThreadContext context, IRubyObject recv, IRuby
*/
@JRubyMethod(name = "expand_path", required = 1, optional = 1, meta = true)
public static IRubyObject expand_path(ThreadContext context, IRubyObject recv, IRubyObject... args) {
return expandPathInternal(context, recv, args, true, false);
return expandPathInternal(context, args, true, false);
}

@Deprecated
@@ -849,17 +843,17 @@ public static IRubyObject expand_path19(ThreadContext context, IRubyObject recv,
*/
@JRubyMethod(required = 1, optional = 1, meta = true)
public static IRubyObject absolute_path(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
return expandPathInternal(context, recv, args, false, false);
return expandPathInternal(context, args, false, false);
}

@JRubyMethod(required = 1, optional = 1, meta = true)
public static IRubyObject realdirpath(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
return expandPathInternal(context, recv, args, false, true);
return expandPathInternal(context, args, false, true);
}

@JRubyMethod(required = 1, optional = 1, meta = true)
public static IRubyObject realpath(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
IRubyObject file = expandPathInternal(context, recv, args, false, true);
IRubyObject file = expandPathInternal(context, args, false, true);
if (!RubyFileTest.exist_p(recv, file).isTrue()) {
throw context.runtime.newErrnoENOENTError(file.toString());
}
@@ -1140,14 +1134,14 @@ public static IRubyObject truncate19(ThreadContext context, IRubyObject recv, IR
@JRubyMethod(meta = true, optional = 1)
public static IRubyObject umask(ThreadContext context, IRubyObject recv, IRubyObject[] args) {
Ruby runtime = context.runtime;
int oldMask = 0;
int oldMask;
if (args.length == 0) {
oldMask = PosixShim.umask(runtime.getPosix());
} else if (args.length == 1) {
int newMask = (int) args[0].convertToInteger().getLongValue();
oldMask = PosixShim.umask(runtime.getPosix(), newMask);
} else {
runtime.newArgumentError("wrong number of arguments");
throw runtime.newArgumentError("wrong number of arguments");
}

return runtime.newFixnum(oldMask);
@@ -1246,9 +1240,7 @@ public static IRubyObject unlink(ThreadContext context, IRubyObject... args) {
// rb_file_size but not using stat
@JRubyMethod
public IRubyObject size(ThreadContext context) {
Ruby runtime = context.runtime;
OpenFile fptr;
FileStat st;
long size;

fptr = getOpenFileChecked();
@@ -1258,7 +1250,7 @@ public IRubyObject size(ThreadContext context) {

size = fptr.posix.size(fptr.fd());

return RubyFixnum.newFixnum(runtime, size);
return RubyFixnum.newFixnum(context.runtime, size);
}

@JRubyMethod(meta = true)
@@ -1458,6 +1450,7 @@ public String toString() {
return "RubyFile(" + openFile.getPath() + ", " + openFile.getMode();
}

@Deprecated // private
public static ZipEntry getFileEntry(ZipFile zf, String path) throws IOException {
ZipEntry entry = zf.getEntry(path);
if (entry == null) {
@@ -1468,10 +1461,12 @@ public static ZipEntry getFileEntry(ZipFile zf, String path) throws IOException
return entry;
}

@Deprecated // not-used
public static ZipEntry getDirOrFileEntry(String jar, String path) throws IOException {
return getDirOrFileEntry(new JarFile(jar), path);
}

@Deprecated // private
public static ZipEntry getDirOrFileEntry(ZipFile zf, String path) throws IOException {
String dirPath = path + '/';
ZipEntry entry = zf.getEntry(dirPath); // first try as directory
@@ -1592,7 +1587,7 @@ private void checkClosed(ThreadContext context) {

private static final Pattern PROTOCOL_PREFIX_PATTERN = Pattern.compile(URI_PREFIX_STRING);

private static IRubyObject expandPathInternal(ThreadContext context, IRubyObject recv, IRubyObject[] args, boolean expandUser, boolean canonicalize) {
private static IRubyObject expandPathInternal(ThreadContext context, IRubyObject[] args, boolean expandUser, boolean canonicalize) {
Ruby runtime = context.runtime;

RubyString origPath = StringSupport.checkEmbeddedNulls(runtime, get_path(context, args[0]));
@@ -1981,7 +1976,7 @@ private static String canonicalize(String canonicalPath, String remaining) {
// no canonical path yet or length is zero, and we have a / followed by a dot...
if (slash == -1) {
// we don't have another slash after this, so replace /. with /
if (canonicalPath != null && canonicalPath.length() == 0 && slash == -1) canonicalPath += '/';
if (canonicalPath != null && canonicalPath.length() == 0) canonicalPath += '/';
} else {
// we do have another slash; omit both / and . (JRUBY-1606)
}
@@ -2153,7 +2148,7 @@ private static IRubyObject truncateCommon(ThreadContext context, IRubyObject rec
RubyInteger newLength = arg2.convertToInteger();

File testFile ;
File childFile = new File(filename.getUnicodeValue() );
File childFile = new File(filename.getUnicodeValue());
String filenameString = Helpers.decodeByteList(runtime, filename.getByteList());

if ( childFile.isAbsolute() ) {
@@ -2202,7 +2197,7 @@ public IRubyObject initialize19(IRubyObject[] args, Block block) {
private static final int FNM_SYSCASE = Platform.IS_WINDOWS ? FNM_CASEFOLD : 0;

private static final String[] SLASHES = {"", "/", "//"};
private static Pattern URI_PREFIX = Pattern.compile("^(jar:)?[a-z]{2,}:(.*)");
private static final Pattern URI_PREFIX = Pattern.compile("^(jar:)?[a-z]{2,}:(.*)");

@Deprecated
protected String path;
4 changes: 3 additions & 1 deletion core/src/main/java/org/jruby/RubyKernel.java
Original file line number Diff line number Diff line change
@@ -1781,7 +1781,9 @@ public static IRubyObject __method__(ThreadContext context, IRubyObject recv) {

@JRubyMethod(name = "__dir__", module = true, visibility = PRIVATE, reads = FILENAME)
public static IRubyObject __dir__(ThreadContext context, IRubyObject recv) {
String dir = RubyFile.dirname(context, new File(context.gatherCallerBacktrace()[1].getFileName()).getAbsolutePath());
// NOTE: not using __FILE__ = context.getFile() since it won't work with JIT
final String __FILE__ = context.gatherCallerBacktrace()[1].getFileName();
String dir = RubyFile.dirname(context, __FILE__);
return RubyString.newString(context.runtime, dir);
}

48 changes: 21 additions & 27 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -2641,7 +2641,7 @@ public IRubyObject mix(ThreadContext context, IRubyObject mod) {
@JRubyMethod(name = "mix", visibility = PRIVATE)
public IRubyObject mix(ThreadContext context, IRubyObject mod, IRubyObject hash0) {
Ruby runtime = context.runtime;
RubyHash methodNames = null;
RubyHash methodNames;

if (!mod.isModule()) {
throw runtime.newTypeError(mod, runtime.getModule());
@@ -2934,14 +2934,17 @@ private void doIncludeModule(RubyModule baseModule) {
// nextClass.isIncluded() && nextClass.getNonIncludedClass() == nextModule.getNonIncludedClass();
// scan class hierarchy for module
for (RubyClass nextClass = methodLocation.getSuperClass(); nextClass != null; nextClass = nextClass.getSuperClass()) {
if (doesTheClassWrapTheModule(nextClass, nextModule)) {
// next in hierarchy is an included version of the module we're attempting,
// so we skip including it
if (nextClass.isIncluded()) {
// does the class equal the module
if (nextClass.getDelegate() == nextModule.getDelegate()) {
// next in hierarchy is an included version of the module we're attempting,
// so we skip including it

// if we haven't encountered a real superclass, use the found module as the new inclusion point
if (!superclassSeen) currentInclusionPoint = nextClass;
// if we haven't encountered a real superclass, use the found module as the new inclusion point
if (!superclassSeen) currentInclusionPoint = nextClass;

continue ModuleLoop;
continue ModuleLoop;
}
} else {
superclassSeen = true;
}
@@ -2990,36 +2993,27 @@ private void doPrependModule(RubyModule baseModule) {
boolean superclassSeen = false;

// scan class hierarchy for module
for (RubyClass nextClass = this.getSuperClass(); nextClass != null; nextClass = nextClass.getSuperClass()) {
if (doesTheClassWrapTheModule(nextClass, nextModule)) {
// next in hierarchy is an included version of the module we're attempting,
// so we skip including it
for (RubyClass nextClass = methodLocation.getSuperClass(); nextClass != null; nextClass = nextClass.getSuperClass()) {
if (nextClass.isIncluded()) {
// does the class equal the module
if (nextClass.getDelegate() == nextModule.getDelegate()) {
// next in hierarchy is an included version of the module we're attempting,
// so we skip including it

// if we haven't encountered a real superclass, use the found module as the new inclusion point
if (!superclassSeen) currentInclusionPoint = nextClass;
// if we haven't encountered a real superclass, use the found module as the new inclusion point
if (!superclassSeen) currentInclusionPoint = nextClass;

continue ModuleLoop;
continue ModuleLoop;
}
} else {
superclassSeen = true;
}
}

currentInclusionPoint = proceedWithPrepend(currentInclusionPoint, nextModule);
currentInclusionPoint = proceedWithPrepend(currentInclusionPoint, nextModule.getDelegate());
}
}

/**
* Is the given class a wrapper for the specified module?
*
* @param theClass The class to inspect
* @param theModule The module we're looking for
* @return true if the class is a wrapper for the module, false otherwise
*/
private boolean doesTheClassWrapTheModule(RubyClass theClass, RubyModule theModule) {
return theClass.isIncluded() &&
theClass.getDelegate() == theModule.getDelegate();
}

/**
* Gather all modules that would be included by including the given module.
* The resulting list contains the given module and its (zero or more)
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyNameError.java
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ public IRubyObject to_str(ThreadContext context) {
} else {
String description = null;
String separator;
String className = null;
String className;
boolean singleton = false;

if (object.isNil()) {
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubySymbol.java
Original file line number Diff line number Diff line change
@@ -868,7 +868,7 @@ private RubySymbol fastCreateSymbol(final String internedName, boolean hard) {

public RubySymbol lookup(long id) {
SymbolEntry[] table = symbolTable;
RubySymbol symbol = null;
RubySymbol symbol;

for (int i = table.length; --i >= 0; ) {
for (SymbolEntry e = table[i]; e != null; e = e.next) {
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyThread.java
Original file line number Diff line number Diff line change
@@ -1753,7 +1753,7 @@ public boolean select(Channel channel, OpenFile fptr, int ops, long timeout) {
synchronized (selectable.blockingLock()) {
boolean oldBlocking = selectable.isBlocking();

SelectionKey key = null;
SelectionKey key;
try {
selectable.configureBlocking(false);

9 changes: 7 additions & 2 deletions core/src/main/java/org/jruby/RubyTime.java
Original file line number Diff line number Diff line change
@@ -509,6 +509,9 @@ public RubyTime succ() {

@JRubyMethod(name = {"gmtime", "utc"})
public RubyTime gmtime() {
if (isFrozen()) {
throw getRuntime().newFrozenError("Time", true);
}
dt = dt.withZone(DateTimeZone.UTC);
return this;
}
@@ -519,6 +522,9 @@ public RubyTime localtime() {

@JRubyMethod(name = "localtime", optional = 1)
public RubyTime localtime19(ThreadContext context, IRubyObject[] args) {
if (isFrozen()) {
throw getRuntime().newFrozenError("Time", true);
}
DateTimeZone newDtz;
if (args.length == 0) {
newDtz = getLocalTimeZone(context.runtime);
@@ -799,8 +805,7 @@ private IRubyObject inspectCommon(DateTimeFormatter formatter, DateTimeFormatter
@JRubyMethod
@Override
public RubyArray to_a() {
return RubyArray.newArrayMayCopy(getRuntime(), new IRubyObject[]{sec(), min(), hour(), mday(), month(),
year(), wday(), yday(), isdst(), zone()});
return RubyArray.newArrayMayCopy(getRuntime(), sec(), min(), hour(), mday(), month(), year(), wday(), yday(), isdst(), zone());
}

@JRubyMethod
4 changes: 4 additions & 0 deletions core/src/main/java/org/jruby/ext/ffi/CallbackInfo.java
Original file line number Diff line number Diff line change
@@ -110,6 +110,10 @@ public static final IRubyObject newCallbackInfo(ThreadContext context, IRubyObje
+ returnType.getMetaClass().getName() + " (expected FFI::Type)");
}

if (returnType instanceof MappedType) {
returnType = ((MappedType) returnType).getRealType();
}

if (!(paramTypes instanceof RubyArray)) {
throw context.runtime.newTypeError("wrong argument type "
+ paramTypes.getMetaClass().getName() + " (expected Array)");
Original file line number Diff line number Diff line change
@@ -90,7 +90,7 @@ private final NativeFunctionInfo newFunctionInfo(Ruby runtime, CallbackInfo cbIn
}

if (!isReturnTypeValid(cbInfo.getReturnType()) || FFIUtil.getFFIType(cbInfo.getReturnType()) == null) {
runtime.newTypeError("invalid callback return type: " + cbInfo.getReturnType());
throw runtime.newTypeError("invalid callback return type: " + cbInfo.getReturnType());
}

return new NativeFunctionInfo(runtime, cbInfo.getReturnType(), cbInfo.getParameterTypes(),
Original file line number Diff line number Diff line change
@@ -82,7 +82,6 @@ public IRubyObject getDefaultProc(ThreadContext context) {
return context.runtime.getNil();
}
}
private static final IRubyObject[] EMPTY_ARGS = new IRubyObject[]{};

@JRubyMethod(name = "value", required = 0)
public IRubyObject getValue(ThreadContext context) {
@@ -96,7 +95,7 @@ public IRubyObject getValue(ThreadContext context) {
// pre-set for the sake of terminating recursive calls
contextVariables.put(this, context.runtime.getNil());
final IRubyObject new_value;
new_value = default_proc.call(context, EMPTY_ARGS, null, Block.NULL_BLOCK);
new_value = default_proc.call(context, IRubyObject.NULL_ARRAY, null, Block.NULL_BLOCK);
contextVariables.put(this, new_value);
return new_value;
} else {
1,103 changes: 556 additions & 547 deletions core/src/main/java/org/jruby/ext/ripper/RipperParser.java

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion core/src/main/java/org/jruby/ext/ripper/RipperParser.y
Original file line number Diff line number Diff line change
@@ -1393,10 +1393,14 @@ lambda : /* none */ {
p.pushBlockScope();
$$ = p.getLeftParenBegin();
p.setLeftParenBegin(p.incrementParenNest());
} {
$$ = p.getCmdArgumentState().getStack();
p.getCmdArgumentState().reset();
} f_larglist lambda_body {
$$ = p.dispatch("on_lambda", $2, $3);
$$ = p.dispatch("on_lambda", $3, $4);
p.popCurrentScope();
p.setLeftParenBegin($<Integer>1);
p.getCmdArgumentState().reset($<Long>2.longValue());
}

f_larglist : tLPAREN2 f_args opt_bv_decl tRPAREN {
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/ext/ripper/RubyRipper.java
Original file line number Diff line number Diff line change
@@ -312,9 +312,9 @@ public IRubyObject filename(ThreadContext context) {

@JRubyMethod
public IRubyObject lineno(ThreadContext context) {
if (!parser.hasStarted()) context.runtime.newArgumentError("method called for uninitialized object");
if (!parser.hasStarted()) throw context.runtime.newArgumentError("method called for uninitialized object");

if (!parseStarted) return context.runtime.getNil();
if (!parseStarted) return context.nil;

return context.runtime.newFixnum(parser.getLineno());
}
7,638 changes: 3,747 additions & 3,891 deletions core/src/main/java/org/jruby/ext/ripper/YyTables.java

Large diffs are not rendered by default.

181 changes: 181 additions & 0 deletions core/src/main/java/org/jruby/ext/securerandom/RubySecureRandom.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2017 JRuby Community
*
* 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"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.ext.securerandom;

import org.jruby.RubyBignum;
import org.jruby.RubyFixnum;
import org.jruby.RubyFloat;
import org.jruby.RubyInteger;
import org.jruby.RubyRange;
import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.anno.JRubyModule;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ConvertBytes;

import java.math.BigInteger;
import java.security.SecureRandom;

/**
* securerandom.rb native parts.
*/
@JRubyModule(name = "SecureRandom")
public class RubySecureRandom {

@JRubyMethod(meta = true, name = "random_bytes")
public static IRubyObject random_bytes(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, nextBytes(context, 16));
}

@JRubyMethod(meta = true, name = "random_bytes", alias = "gen_random") // gen_random for 'better' compat (not-used)
public static IRubyObject random_bytes(ThreadContext context, IRubyObject self, IRubyObject n) {
return RubyString.newStringNoCopy(context.runtime, nextBytes(context, n));
}

@JRubyMethod(meta = true)
public static IRubyObject hex(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.twosComplementToHexBytes(nextBytes(context, 16), false));
}

@JRubyMethod(meta = true)
public static IRubyObject hex(ThreadContext context, IRubyObject self, IRubyObject n) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.twosComplementToHexBytes(nextBytes(context, n), false));
}

@JRubyMethod(meta = true)
public static IRubyObject uuid(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.bytesToUUIDBytes(nextBytes(context, 16), false));
}

private static byte[] nextBytes(ThreadContext context, IRubyObject n) {
return nextBytes(context, n.isNil() ? 16 : n.convertToInteger().getIntValue());
}

private static byte[] nextBytes(ThreadContext context, int size) {
if (size < 0) throw context.runtime.newArgumentError("negative argument: " + size);

byte[] bytes = new byte[size];
getSecureRandom(context).nextBytes(bytes);

return bytes;
}

@JRubyMethod(meta = true)
public static IRubyObject random_number(ThreadContext context, IRubyObject self) {
return randomDouble(context);
}

@JRubyMethod(meta = true)
public static IRubyObject random_number(ThreadContext context, IRubyObject self, IRubyObject n) {
if (n instanceof RubyFixnum) {
final long bound = ((RubyFixnum) n).getLongValue();
return ( bound < 0 ) ? randomDouble(context) : randomFixnum(context, 0, bound - 1);
}
if (n instanceof RubyFloat) {
final double bound = ((RubyFloat) n).getDoubleValue();
return ( bound < 0 ) ? randomDouble(context) : randomDouble(context, 0, bound - Double.MIN_VALUE);
}
if (n instanceof RubyBignum) {
final BigInteger bound = ((RubyBignum) n).getBigIntegerValue();
return ( bound.signum() < 0 ) ? randomDouble(context) : randomBignum(context, 0, bound);
}

if (n instanceof RubyRange) {
final IRubyObject beg = ((RubyRange) n).begin(context);
final IRubyObject end = ((RubyRange) n).end(context);
final boolean exclude = ((RubyRange) n).isExcludeEnd();

if (beg instanceof RubyFixnum && end instanceof RubyFixnum) {
long lower = ((RubyFixnum) beg).getLongValue();
long upper = ((RubyFixnum) end).getLongValue();
if ( lower > upper ) return randomDouble(context);
if ( exclude ) upper--; // rand(2) never returns 2 but rand(0..2) does
return randomFixnum(context, lower, upper);
}
if (beg instanceof RubyInteger && end instanceof RubyInteger) {
BigInteger lower = ((RubyInteger) beg).getBigIntegerValue();
BigInteger upper = ((RubyInteger) end).getBigIntegerValue();
if ( lower.compareTo(upper) > 0 ) return randomDouble(context);
if ( ! exclude ) upper = upper.add(BigInteger.ONE);
return randomBignum(context, lower, upper);
}
if (beg instanceof RubyFloat && end instanceof RubyFloat) {
double lower = ((RubyFloat) beg).getDoubleValue();
double upper = ((RubyFloat) end).getDoubleValue();
if ( lower > upper ) return randomDouble(context);
if ( exclude ) upper = upper - Double.MIN_VALUE;
return randomDouble(context, lower, upper);
}
}

throw context.runtime.newArgumentError("invalid argument - " + n.anyToString());
}

private static RubyFixnum randomFixnum(final ThreadContext context, final long lower, final long upper) {
double rnd = getSecureRandom(context).nextDouble();
rnd = rnd * upper + (1.0 - rnd) * lower + rnd;
return context.runtime.newFixnum((long) Math.floor(rnd));
}

// NOTE: upper is exclusive here compared to others
private static RubyBignum randomBignum(final ThreadContext context, final Number lower, final BigInteger upperExc) {
BigInteger lowerBig = lower instanceof BigInteger ? (BigInteger) lower : BigInteger.valueOf(lower.longValue());
BigInteger bound = upperExc.subtract(lowerBig);
BigInteger rnd = nextBigInteger(getSecureRandom(context), bound, bound.bitLength());
return RubyBignum.newBignum(context.runtime, rnd.add(lowerBig));
}

private static final int BI_ADD_BITS = 96; // 8+4 random bytes 'wasted'

// <0, bound)
private static BigInteger nextBigInteger(final SecureRandom random, final BigInteger bound, final int bits) {
BigInteger val = new BigInteger(bits + BI_ADD_BITS, random);
BigInteger rnd = val.mod(bound);

if (val.add(bound).subtract(rnd).subtract(BigInteger.ONE).bitLength() >= bits + BI_ADD_BITS) {
return nextBigInteger(random, bound, bits); // highly unlikely to recurse at all
}
return rnd;
}

private static RubyFloat randomDouble(final ThreadContext context, final double lower, final double upper) {
double rnd = getSecureRandom(context).nextDouble();
return context.runtime.newFloat( rnd * upper + (1.0 - rnd) * lower );
}

private static RubyFloat randomDouble(final ThreadContext context) {
return context.runtime.newFloat( getSecureRandom(context).nextDouble() );
}

private static SecureRandom getSecureRandom(ThreadContext context) {
return context.getSecureRandom();
}

}
Original file line number Diff line number Diff line change
@@ -1,52 +1,48 @@
package org.jruby.ext.securerandom;

import org.jruby.RubyString;
import org.jruby.anno.JRubyMethod;
import org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.ConvertBytes;
import org.jruby.runtime.load.Library;

/**
* Created by headius on 1/14/14.
*/
public class SecureRandomLibrary {
@JRubyMethod(meta = true)
public class SecureRandomLibrary implements Library {

public static void load(Ruby runtime) {
RubyModule SecureRandom = runtime.defineModule("SecureRandom");
SecureRandom.defineAnnotatedMethods(RubySecureRandom.class);
}

public void load(Ruby runtime, boolean wrap) {
SecureRandomLibrary.load(runtime);
}

@Deprecated
public static IRubyObject random_bytes(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, nextBytes(context, 16));
return RubySecureRandom.random_bytes(context, self);
}

@JRubyMethod(meta = true)
@Deprecated
public static IRubyObject random_bytes(ThreadContext context, IRubyObject self, IRubyObject n) {
return RubyString.newStringNoCopy(context.runtime, nextBytes(context, n));
return RubySecureRandom.random_bytes(context, self, n);
}

@JRubyMethod(meta = true)
@Deprecated
public static IRubyObject hex(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.twosComplementToHexBytes(nextBytes(context, 16), false));
return RubySecureRandom.hex(context, self);
}

@JRubyMethod(meta = true)
@Deprecated
public static IRubyObject hex(ThreadContext context, IRubyObject self, IRubyObject n) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.twosComplementToHexBytes(nextBytes(context, n), false));
return RubySecureRandom.hex(context, self, n);
}

@JRubyMethod(meta = true)
@Deprecated
public static IRubyObject uuid(ThreadContext context, IRubyObject self) {
return RubyString.newStringNoCopy(context.runtime, ConvertBytes.bytesToUUIDBytes(nextBytes(context, 16), false));
return RubySecureRandom.hex(context, self);
}

private static byte[] nextBytes(ThreadContext context, IRubyObject n) {
int size = n.isNil() ? 16 : (int) n.convertToInteger().getLongValue();

return nextBytes(context, size);
}

private static byte[] nextBytes(ThreadContext context, int size) {
if (size < 0) throw context.runtime.newArgumentError("negative argument: " + size);

byte[] bytes = new byte[size];
context.getSecureRandom().nextBytes(bytes);

return bytes;
}
}
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ext/socket/Addrinfo.java
Original file line number Diff line number Diff line change
@@ -214,7 +214,7 @@ private void initializeCommon(ThreadContext context, IRubyObject sockaddr, IRuby

RubyString sockaddrString = sockaddr.convertToString();

InetAddress inetAddress = null;
InetAddress inetAddress;

inetAddress = getRubyInetAddress(sockaddr);
if (inetAddress == null) {
11 changes: 5 additions & 6 deletions core/src/main/java/org/jruby/ext/socket/RubyIPSocket.java
Original file line number Diff line number Diff line change
@@ -114,12 +114,11 @@ public IRubyObject recvfrom(ThreadContext context, IRubyObject _length) {
}

IRubyObject addressArray = context.runtime.newArray(
new IRubyObject[]{
runtime.newString("AF_INET"),
runtime.newFixnum(port),
runtime.newString(hostName),
runtime.newString(hostAddress)
});
runtime.newString("AF_INET"),
runtime.newFixnum(port),
runtime.newString(hostName),
runtime.newString(hostAddress)
);

return runtime.newArray(result, addressArray);
}
34 changes: 17 additions & 17 deletions core/src/main/java/org/jruby/ext/zlib/JZlibRubyGzipReader.java
Original file line number Diff line number Diff line change
@@ -191,11 +191,9 @@ public IRubyObject lineno() {

@JRubyMethod(name = "readline", writes = FrameField.LASTLINE)
public IRubyObject readline(ThreadContext context) {
IRubyObject dst = gets_18(context, new IRubyObject[0]);
IRubyObject dst = gets(context, IRubyObject.NULL_ARRAY);

if (dst.isNil()) {
throw getRuntime().newEOFError();
}
if (dst.isNil()) throw context.runtime.newEOFError();

return dst;
}
@@ -299,6 +297,7 @@ private void skipNewlines() throws IOException {
}
}

@Deprecated
public IRubyObject gets_18(ThreadContext context, IRubyObject[] args) {
return gets(context, args);
}
@@ -632,7 +631,7 @@ public IRubyObject comment() {
public IRubyObject each(ThreadContext context, IRubyObject[] args, Block block) {
if (!block.isGiven()) return RubyEnumerator.enumeratorize(context.runtime, this, "each", args);

ByteList sep = ((RubyString) getRuntime().getGlobalVariables().get("$/")).getByteList();
ByteList sep = ((RubyString) context.runtime.getGlobalVariables().get("$/")).getByteList();

if (args.length > 0 && !args[0].isNil()) {
sep = args[0].convertToString().getByteList();
@@ -643,10 +642,10 @@ public IRubyObject each(ThreadContext context, IRubyObject[] args, Block block)
block.yield(context, result);
}
} catch (IOException ioe) {
throw getRuntime().newIOErrorFromException(ioe);
throw context.runtime.newIOErrorFromException(ioe);
}

return getRuntime().getNil();
return context.nil;
}

@JRubyMethod(optional = 1)
@@ -670,10 +669,10 @@ public IRubyObject ungetc(ThreadContext context, IRubyObject c) {
bufferedStream.unread(bytes);
position -= bytes.length;
} catch (IOException ioe) {
throw getRuntime().newIOErrorFromException(ioe);
throw context.runtime.newIOErrorFromException(ioe);
}

return getRuntime().getNil();
return context.nil;
}

@JRubyMethod
@@ -692,13 +691,13 @@ public IRubyObject ungetbyte(IRubyObject b) {

@JRubyMethod(optional = 1)
public IRubyObject readlines(ThreadContext context, IRubyObject[] args) {
List<IRubyObject> array = new ArrayList<IRubyObject>();
List<IRubyObject> array = new ArrayList<>();

if (args.length != 0 && args[0].isNil()) {
array.add(read(context, new IRubyObject[0]));
array.add(read(context, IRubyObject.NULL_ARRAY));

} else {
ByteList sep = ((RubyString) getRuntime().getGlobalVariables().get("$/")).getByteList();
ByteList sep = ((RubyString) context.runtime.getGlobalVariables().get("$/")).getByteList();

if (args.length > 0) sep = args[0].convertToString().getByteList();

@@ -707,7 +706,7 @@ public IRubyObject readlines(ThreadContext context, IRubyObject[] args) {
array.add(result);
}
} catch (IOException ioe) {
throw getRuntime().newIOErrorFromException(ioe);
throw context.runtime.newIOErrorFromException(ioe);
}
}

@@ -716,21 +715,22 @@ public IRubyObject readlines(ThreadContext context, IRubyObject[] args) {

@JRubyMethod
public IRubyObject each_byte(ThreadContext context, Block block) {
if (!block.isGiven()) return RubyEnumerator.enumeratorize(context.runtime, this, "each_byte");
final Ruby runtime = context.runtime;
if (!block.isGiven()) return RubyEnumerator.enumeratorize(runtime, this, "each_byte");

try {
int value = bufferedStream.read();

while (value != -1) {
position++;
block.yield(context, getRuntime().newFixnum(value));
block.yield(context, runtime.newFixnum(value));
value = bufferedStream.read();
}
} catch (IOException ioe) {
throw getRuntime().newIOErrorFromException(ioe);
throw runtime.newIOErrorFromException(ioe);
}

return getRuntime().getNil();
return context.nil;
}

private void fixBrokenTrailingCharacter(ByteList result) throws IOException {
Original file line number Diff line number Diff line change
@@ -275,12 +275,13 @@ public IRubyObject putc(IRubyObject p1) {

@JRubyMethod(name = "puts", rest = true)
public IRubyObject puts(ThreadContext context, IRubyObject[] args) {
StringIO sio = (StringIO) getRuntime().getClass("StringIO").newInstance(context, new IRubyObject[0], Block.NULL_BLOCK);
final RubyClass StringIO = context.runtime.getClass("StringIO");
StringIO sio = (StringIO) StringIO.newInstance(context, IRubyObject.NULL_ARRAY, Block.NULL_BLOCK);

sio.puts(context, args);
write(sio.string(context));

return getRuntime().getNil();
return context.nil;
}

@Override
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/ext/zlib/RubyGzipFile.java
Original file line number Diff line number Diff line change
@@ -110,7 +110,7 @@ public static RubyGzipFile newInstance(IRubyObject recv, Block block) {

RubyGzipFile result = (RubyGzipFile) klass.allocate();

result.callInit(new IRubyObject[0], block);
result.callInit(IRubyObject.NULL_ARRAY, block);

return result;
}
7 changes: 6 additions & 1 deletion core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -386,14 +386,18 @@ private void emitEnsureBlocks(IRLoop loop) {
}
}

private Operand buildOperand(Variable result, Node node) throws NotCompilableException {
private void determineIfWeNeedLineNumber(Node node) {
if (node.isNewline()) {
int currLineNum = node.getLine();
if (currLineNum != _lastProcessedLineNum) { // Do not emit multiple line number instrs for the same line
needsLineNumInfo = true;
_lastProcessedLineNum = currLineNum;
}
}
}

private Operand buildOperand(Variable result, Node node) throws NotCompilableException {
determineIfWeNeedLineNumber(node);

switch (node.getNodeType()) {
case ALIASNODE: return buildAlias((AliasNode) node);
@@ -2700,6 +2704,7 @@ public Operand buildFCall(Variable result, FCallNode fcallNode) {
}
}

determineIfWeNeedLineNumber(fcallNode); // buildOperand for fcall was papered over by args operand building so we check once more.
CallInstr callInstr = CallInstr.create(scope, CallType.FUNCTIONAL, result, fcallNode.getName(), buildSelf(), args, block);
receiveBreakException(block, callInstr);
return result;
Original file line number Diff line number Diff line change
@@ -150,8 +150,9 @@ public void encode(String value) {
if (value == null) {
encode(NULL_STRING);
} else {
encode(value.length());
buf.put(value.getBytes());
byte[] bytes = value.getBytes();
encode(bytes.length);
buf.put(bytes);
}
}

62 changes: 62 additions & 0 deletions core/src/main/java/org/jruby/lexer/StrptimeLexer.flex
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* IMPORTANT: must compile with JFlex 1.4, JFlex 1.4.3 seems buggy with look-ahead */

package org.jruby.lexer;

import org.jruby.util.StrptimeToken;

%%
%public
%class StrptimeLexer
//%debug
%unicode
%type org.jruby.util.StrptimeToken
%{
StringBuilder stringBuf = new StringBuilder();

public StrptimeToken rawString() {
String str = stringBuf.toString();
stringBuf.setLength(0);
return StrptimeToken.str(str);
}

public StrptimeToken directive(char c) {
StrptimeToken token;
if (c == 'z') {
int colons = yylength()-1; // can only be colons except the 'z'
return StrptimeToken.zoneOffsetColons(colons);
} else if ((token = StrptimeToken.format(c)) != null) {
return token;
} else {
return StrptimeToken.special(c);
}
}
%}

Flags = [-_0#\^]+
Width = [1-9][0-9]*

// See RubyDateFormatter.main to generate this
// Chars are sorted by | ruby -e 'p STDIN.each_char.sort{|a,b|a.casecmp(b).tap{|c|break a<=>b if c==0}}.join'
Conversion = [\+AaBbCcDdeFGgHhIjkLlMmNnPpQRrSsTtUuVvWwXxYyZz] | {IgnoredModifier} | {Zone}
// From MRI strftime.c
IgnoredModifier = E[CcXxYy] | O[deHIMmSUuVWwy]
Zone = :{1,3} z

SimpleDirective = "%"
LiteralPercent = "%%"
Unknown = .|\n

%xstate CONVERSION

%%

<YYINITIAL> {
{LiteralPercent} { return StrptimeToken.str("%"); }
{SimpleDirective} / {Conversion} { yybegin(CONVERSION); }
}

<CONVERSION> {Conversion} { yybegin(YYINITIAL); return directive(yycharat(yylength()-1)); }

/* fallback */
{Unknown} / [^%] { stringBuf.append(yycharat(0)); }
{Unknown} { stringBuf.append(yycharat(0)); return rawString(); }
577 changes: 577 additions & 0 deletions core/src/main/java/org/jruby/lexer/StrptimeLexer.java

Large diffs are not rendered by default.

1,158 changes: 584 additions & 574 deletions core/src/main/java/org/jruby/parser/RubyParser.java

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion core/src/main/java/org/jruby/parser/RubyParser.y
Original file line number Diff line number Diff line change
@@ -1723,10 +1723,15 @@ lambda : /* none */ {
support.pushBlockScope();
$$ = lexer.getLeftParenBegin();
lexer.setLeftParenBegin(lexer.incrementParenNest());
} {
$$ = lexer.getCmdArgumentState().getStack();
lexer.getCmdArgumentState().reset();
} f_larglist lambda_body {
$$ = new LambdaNode($2.getPosition(), $2, $3, support.getCurrentScope());
$$ = new LambdaNode($3.getPosition(), $3, $4, support.getCurrentScope());
support.popCurrentScope();
lexer.setLeftParenBegin($<Integer>1);
lexer.getCmdArgumentState().reset($<Long>2.longValue());

}

f_larglist : tLPAREN2 f_args opt_bv_decl tRPAREN {
7,864 changes: 3,986 additions & 3,878 deletions core/src/main/java/org/jruby/parser/YyTables.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -79,7 +79,9 @@ public class UnmarshalStream extends InputStream {
public UnmarshalStream(Ruby runtime, InputStream in, IRubyObject proc, boolean taint) throws IOException {
assert runtime != null;
assert in != null;
assert proc != null;

// Older native java ext expects proc can be null (spymemcached.jruby at least).
if (proc == null) proc = runtime.getNil();

this.runtime = runtime;
this.cache = new UnmarshalCache(runtime);
4 changes: 4 additions & 0 deletions core/src/main/java/org/jruby/util/JarCache.java
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.AccessControlException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
@@ -147,6 +148,9 @@ public JarIndex getIndex(String jarPath) {
indexCache.put(cacheKey, index);
} catch (IOException ioe) {
return null;
} catch (AccessControlException ace) {
// No permissions to index the given path, bail out
return null;
}
}

151 changes: 151 additions & 0 deletions core/src/main/java/org/jruby/util/RubyDateParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* 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"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.util;

import org.jruby.RubyBignum;
import org.jruby.RubyFixnum;
import org.jruby.RubyRational;
import org.jruby.RubyString;
import org.jruby.runtime.ThreadContext;

import java.util.HashMap;
import java.util.List;

import static org.jruby.util.StrptimeParser.FormatBag.has;

/**
* This class has {@code StrptimeParser} and provides methods that are calls from JRuby.
*/
public class RubyDateParser {
private final StrptimeParser strptimeParser;

public RubyDateParser() {
this.strptimeParser = new StrptimeParser();
}

/**
* Date._strptime method in JRuby 9.1.5.0's lib/ruby/stdlib/date/format.rb is replaced
* with this method. This is Java implementation of date__strptime method in MRI 2.3.1's
* ext/date/date_strptime.c.
* @see https://github.com/jruby/jruby/blob/036ce39f0476d4bd718e23e64caff36bb50b8dbc/lib/ruby/stdlib/date/format.rb
* @see https://github.com/ruby/ruby/blob/394fa89c67722d35bdda89f10c7de5c304a5efb1/ext/date/date_strptime.c
*/

public HashMap<String, Object> parse(ThreadContext context, final RubyString format, final RubyString text) {
final boolean tainted = text.isTaint();
final List<StrptimeToken> compiledPattern = strptimeParser.compilePattern(format.asJavaString());
final StrptimeParser.FormatBag bag = strptimeParser.parse(compiledPattern, text.asJavaString());
if (bag != null) {
return convertFormatBagToHash(context, bag, tainted);
} else {
return null;
}
}

private HashMap<String, Object> convertFormatBagToHash(ThreadContext context, StrptimeParser.FormatBag bag, boolean tainted) {
final HashMap<String, Object> map = new HashMap<>();

if (has(bag.getMDay())) {
map.put("mday", bag.getMDay());
}
if (has(bag.getWDay())) {
map.put("wday", bag.getWDay());
}
if (has(bag.getCWDay())) {
map.put("cwday", bag.getCWDay());
}
if (has(bag.getYDay())) {
map.put("yday", bag.getYDay());
}
if (has(bag.getCWeek())) {
map.put("cweek", bag.getCWeek());
}
if (has(bag.getCWYear())) {
map.put("cwyear", bag.getCWYear());
}
if (has(bag.getMin())) {
map.put("min", bag.getMin());
}
if (has(bag.getMon())) {
map.put("mon", bag.getMon());
}
if (has(bag.getHour())) {
map.put("hour", bag.getHour());
}
if (has(bag.getYear())) {
map.put("year", bag.getYear());
}
if (has(bag.getSec())) {
map.put("sec", bag.getSec());
}
if (has(bag.getWNum0())) {
map.put("wnum0", bag.getWNum0());
}
if (has(bag.getWNum1())) {
map.put("wnum1", bag.getWNum1());
}
if (bag.getZone() != null) {
final RubyString zone = RubyString.newString(context.getRuntime(), bag.getZone());
if (tainted) {
zone.taint(context);
}
map.put("zone", zone);
int offset = TimeZoneConverter.dateZoneToDiff(bag.getZone());
if (offset != Integer.MIN_VALUE) {
map.put("offset", offset);
}
}
if (has(bag.getSecFraction())) {
final RubyBignum secFraction = RubyBignum.newBignum(context.getRuntime(), bag.getSecFraction());
final RubyFixnum secFractionSize = RubyFixnum.newFixnum(context.getRuntime(), (long)Math.pow(10, bag.getSecFractionSize()));
map.put("sec_fraction", RubyRational.newRationalCanonicalize(context, secFraction, secFractionSize));
}
if (bag.has(bag.getSeconds())) {
if (has(bag.getSecondsSize())) {
final RubyBignum seconds = RubyBignum.newBignum(context.getRuntime(), bag.getSeconds());
final RubyFixnum secondsSize = RubyFixnum.newFixnum(context.getRuntime(), (long)Math.pow(10, bag.getSecondsSize()));
map.put("seconds", RubyRational.newRationalCanonicalize(context, seconds, secondsSize));
} else {
map.put("seconds", bag.getSeconds());
}
}
if (has(bag.getMerid())) {
map.put("_merid", bag.getMerid());
}
if (has(bag.getCent())) {
map.put("_cent", bag.getCent());
}
if (bag.getLeftover() != null) {
final RubyString leftover = RubyString.newString(context.getRuntime(), bag.getLeftover());
if (tainted) {
leftover.taint(context);
}
map.put("leftover", leftover);
}

return map;
}
}
70 changes: 70 additions & 0 deletions core/src/main/java/org/jruby/util/StrptimeFormat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* 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"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.util;

/**
* This class is ported from RubyDateFormatter.Format in JRuby 9.1.5.0.
* @see https://github.com/jruby/jruby/blob/036ce39f0476d4bd718e23e64caff36bb50b8dbc/core/src/main/java/org/jruby/util/RubyDateFormatter.java
*/
enum StrptimeFormat {
FORMAT_STRING, // raw string, no formatting
FORMAT_SPECIAL, // composition of other formats

FORMAT_WEEK_LONG, // %A
FORMAT_WEEK_SHORT, // %a
FORMAT_MONTH_LONG, // %B
FORMAT_MONTH_SHORT, // %b, %h
FORMAT_CENTURY, // %C
FORMAT_DAY, // %d
FORMAT_DAY_S, // %e
FORMAT_WEEKYEAR, // %G
FORMAT_WEEKYEAR_SHORT, // %g
FORMAT_HOUR, // %H
FORMAT_HOUR_M, // %I
FORMAT_DAY_YEAR, // %j
FORMAT_HOUR_BLANK, // %k
FORMAT_MILLISEC, // %L
FORMAT_HOUR_S, // %l
FORMAT_MINUTES, // %M
FORMAT_MONTH, // %m
FORMAT_NANOSEC, // %N
FORMAT_MERIDIAN_LOWER_CASE, // %P
FORMAT_MERIDIAN, // %p
FORMAT_MICROSEC_EPOCH, // %Q Only for Date/DateTime from here
FORMAT_SECONDS, // %S
FORMAT_EPOCH, // %s
FORMAT_WEEK_YEAR_S, // %U
FORMAT_DAY_WEEK2, // %u
FORMAT_WEEK_WEEKYEAR, // %V
FORMAT_WEEK_YEAR_M, // %W
FORMAT_DAY_WEEK, // %w
FORMAT_YEAR_LONG, // %Y
FORMAT_YEAR_SHORT, // %y

FORMAT_COLON_ZONE_OFF, // %z, %:z, %::z, %:::z must be given number of colons as data

FORMAT_ZONE_ID; // %Z Change between Time and Date
}
823 changes: 823 additions & 0 deletions core/src/main/java/org/jruby/util/StrptimeParser.java

Large diffs are not rendered by default.

119 changes: 119 additions & 0 deletions core/src/main/java/org/jruby/util/StrptimeToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* 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"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.util;

import static org.jruby.util.StrptimeFormat.*;

/**
* This class is ported from RubyDateFormatter.Token in JRuby 9.1.5.0.
* @see https://github.com/jruby/jruby/blob/036ce39f0476d4bd718e23e64caff36bb50b8dbc/core/src/main/java/org/jruby/util/RubyDateFormatter.java
*/
public class StrptimeToken {
static final StrptimeToken[] CONVERSION2TOKEN = new StrptimeToken[256];

static {
CONVERSION2TOKEN['A'] = new StrptimeToken(FORMAT_WEEK_LONG);
CONVERSION2TOKEN['a'] = new StrptimeToken(FORMAT_WEEK_SHORT);
CONVERSION2TOKEN['B'] = new StrptimeToken(FORMAT_MONTH_LONG);
CONVERSION2TOKEN['b'] = new StrptimeToken(FORMAT_MONTH_SHORT);
CONVERSION2TOKEN['h'] = CONVERSION2TOKEN['b'];
CONVERSION2TOKEN['C'] = new StrptimeToken(FORMAT_CENTURY);
CONVERSION2TOKEN['d'] = new StrptimeToken(FORMAT_DAY);
CONVERSION2TOKEN['e'] = new StrptimeToken(FORMAT_DAY_S);
CONVERSION2TOKEN['G'] = new StrptimeToken(FORMAT_WEEKYEAR);
CONVERSION2TOKEN['g'] = new StrptimeToken(FORMAT_WEEKYEAR_SHORT);
CONVERSION2TOKEN['H'] = new StrptimeToken(FORMAT_HOUR);
CONVERSION2TOKEN['I'] = new StrptimeToken(FORMAT_HOUR_M);
CONVERSION2TOKEN['j'] = new StrptimeToken(FORMAT_DAY_YEAR);
CONVERSION2TOKEN['k'] = new StrptimeToken(FORMAT_HOUR_BLANK);
CONVERSION2TOKEN['L'] = new StrptimeToken(FORMAT_MILLISEC);
CONVERSION2TOKEN['l'] = new StrptimeToken(FORMAT_HOUR_S);
CONVERSION2TOKEN['M'] = new StrptimeToken(FORMAT_MINUTES);
CONVERSION2TOKEN['m'] = new StrptimeToken(FORMAT_MONTH);
CONVERSION2TOKEN['N'] = new StrptimeToken(FORMAT_NANOSEC);
CONVERSION2TOKEN['P'] = new StrptimeToken(FORMAT_MERIDIAN_LOWER_CASE);
CONVERSION2TOKEN['p'] = new StrptimeToken(FORMAT_MERIDIAN);
CONVERSION2TOKEN['Q'] = new StrptimeToken(FORMAT_MICROSEC_EPOCH);
CONVERSION2TOKEN['S'] = new StrptimeToken(FORMAT_SECONDS);
CONVERSION2TOKEN['s'] = new StrptimeToken(FORMAT_EPOCH);
CONVERSION2TOKEN['U'] = new StrptimeToken(FORMAT_WEEK_YEAR_S);
CONVERSION2TOKEN['u'] = new StrptimeToken(FORMAT_DAY_WEEK2);
CONVERSION2TOKEN['V'] = new StrptimeToken(FORMAT_WEEK_WEEKYEAR);
CONVERSION2TOKEN['W'] = new StrptimeToken(FORMAT_WEEK_YEAR_M);
CONVERSION2TOKEN['w'] = new StrptimeToken(FORMAT_DAY_WEEK);
CONVERSION2TOKEN['Y'] = new StrptimeToken(FORMAT_YEAR_LONG);
CONVERSION2TOKEN['y'] = new StrptimeToken(FORMAT_YEAR_SHORT);
}

private final StrptimeFormat format;
private final Object data;

StrptimeToken(StrptimeFormat format) {
this(format, null);
}

StrptimeToken(StrptimeFormat formatString, Object data) {
this.format = formatString;
this.data = data;
}

public static StrptimeToken str(String str) {
return new StrptimeToken(StrptimeFormat.FORMAT_STRING, str);
}

public static StrptimeToken format(char c) {
return CONVERSION2TOKEN[c];
}

public static StrptimeToken zoneOffsetColons(int colons) {
return new StrptimeToken(StrptimeFormat.FORMAT_COLON_ZONE_OFF, colons);
}

public static StrptimeToken special(char c) {
return new StrptimeToken(StrptimeFormat.FORMAT_SPECIAL, c);
}

/**
* Gets the data.
* @return Returns a Object
*/
Object getData() {
return data;
}

/**
* Gets the format.
* @return Returns a int
*/
StrptimeFormat getFormat() {
return format;
}

@Override
public String toString() {
return "<Token "+format+ " "+data+">";
}
}
474 changes: 474 additions & 0 deletions core/src/main/java/org/jruby/util/TimeZoneConverter.java

Large diffs are not rendered by default.

28 changes: 3 additions & 25 deletions lib/ruby/stdlib/date/format.rb
Original file line number Diff line number Diff line change
@@ -376,31 +376,9 @@ def self._strptime_i(str, fmt, e) # :nodoc:
private_class_method :_strptime_i

def self._strptime(str, fmt='%F')
str = str.dup
e = Format::Bag.new
return unless _strptime_i(str, fmt, e)

if e._cent
if e.cwyear
e.cwyear += e._cent * 100
end
if e.year
e. year += e._cent * 100
end
end

if e._merid
if e.hour
e.hour %= 12
e.hour += e._merid
end
end

unless str.empty?
e.leftover = str
end

e.to_hash
parser = org.jruby.util.RubyDateParser.new
map = parser.parse(JRuby.runtime.current_context, fmt, str)
return map.nil? ? nil : map.to_hash.inject({}){|hash,(k,v)| hash[k.to_sym] = v; hash}
end

def self.s3e(e, y, m, d, bc=false)
18 changes: 9 additions & 9 deletions lib/ruby/stdlib/ffi/platform/x86_64-windows/platform.conf
Original file line number Diff line number Diff line change
@@ -430,8 +430,8 @@ rbx.platform.typedef._fpos_t = long
rbx.platform.typedef._fpos64_t = long_long
rbx.platform.typedef._ssize_t = int
rbx.platform.typedef.wint_t = uint
rbx.platform.typedef.ptrdiff_t = int
rbx.platform.typedef.size_t = uint
rbx.platform.typedef.ptrdiff_t = long_long
rbx.platform.typedef.size_t = ulong_long
rbx.platform.typedef.__off_t = long
rbx.platform.typedef.__pid_t = int
rbx.platform.typedef.__loff_t = long_long
@@ -443,16 +443,16 @@ rbx.platform.typedef.ushort = ushort
rbx.platform.typedef.uint = uint
rbx.platform.typedef.ulong = ulong
rbx.platform.typedef.clock_t = ulong
rbx.platform.typedef.time_t = long
rbx.platform.typedef.time_t = long_long
rbx.platform.typedef.daddr_t = long
rbx.platform.typedef.caddr_t = string
rbx.platform.typedef.pid_t = int
rbx.platform.typedef.ssize_t = int
rbx.platform.typedef.pid_t = long_long
rbx.platform.typedef.ssize_t = long_long
rbx.platform.typedef.nlink_t = ushort
rbx.platform.typedef.fd_mask = long
rbx.platform.typedef.clockid_t = ulong
rbx.platform.typedef.timer_t = ulong
rbx.platform.typedef.useconds_t = ulong
rbx.platform.typedef.useconds_t = uint
rbx.platform.typedef.suseconds_t = long
rbx.platform.typedef.int8_t = char
rbx.platform.typedef.int16_t = short
@@ -478,15 +478,15 @@ rbx.platform.typedef.uint_fast8_t = uchar
rbx.platform.typedef.uint_fast16_t = uint
rbx.platform.typedef.uint_fast32_t = uint
rbx.platform.typedef.uint_fast64_t = ulong_long
rbx.platform.typedef.intptr_t = int
rbx.platform.typedef.intptr_t = long_long
rbx.platform.typedef.uintptr_t = ulong_long
rbx.platform.typedef.intmax_t = long_long
rbx.platform.typedef.uintmax_t = ulong_long
rbx.platform.typedef.off_t = long_long
rbx.platform.typedef.loff_t = long_long
rbx.platform.typedef.__dev16_t = short
rbx.platform.typedef.__dev32_t = ulong
rbx.platform.typedef.dev_t = ulong
rbx.platform.typedef.dev_t = uint
rbx.platform.typedef.blksize_t = long
rbx.platform.typedef.__blkcnt32_t = long
rbx.platform.typedef.__blkcnt64_t = long_long
@@ -501,7 +501,7 @@ rbx.platform.typedef.__gid32_t = ulong
rbx.platform.typedef.gid_t = ulong
rbx.platform.typedef.__ino32_t = ulong
rbx.platform.typedef.__ino64_t = ulong_long
rbx.platform.typedef.ino_t = ulong_long
rbx.platform.typedef.ino_t = ushort
rbx.platform.typedef.id_t = ulong
rbx.platform.typedef.key_t = long_long
rbx.platform.typedef.vm_offset_t = ulong
6 changes: 1 addition & 5 deletions lib/ruby/stdlib/jruby/compiler.rb
Original file line number Diff line number Diff line change
@@ -56,10 +56,6 @@ def compile_argv(argv)
options[:javac_options] << o
end

#opts.on("-5"," --jdk5", "Generate JDK 5 classes (version 49)") do |x|
# options[:jdk5] = true
#end

opts.on("--java", "Generate Java classes (.java) for a script containing Ruby class definitions") do
options[:java] = true
end
@@ -146,7 +142,7 @@ def compile_files_with_options(filenames, options = default_options)

FileUtils.mkdir_p java_dir

java_src = File.join(java_dir, cls.name + ".java")
java_src = File.join(java_dir, "#{cls.name}.java")
puts "Generating Java class #{cls.name} to #{java_src}" if options[:verbose]

files << java_src
238 changes: 143 additions & 95 deletions lib/ruby/stdlib/jruby/compiler/java_class.rb

Large diffs are not rendered by default.

229 changes: 108 additions & 121 deletions lib/ruby/stdlib/securerandom.rb
Original file line number Diff line number Diff line change
@@ -1,105 +1,119 @@
# = Secure random number generator interface.
#
# This library is an interface for secure random number generator which is
# suitable for generating session key in HTTP cookies, etc.
#
# It supports following secure random number generators.
#
# * Java's java.security.SecureRandom.
#
# == Example
#
# # random hexadecimal string.
# p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
# p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
# p SecureRandom.hex(11) #=> "6aca1b5c58e4863e6b81b8"
# p SecureRandom.hex(12) #=> "94b2fff3e7fd9b9c391a2306"
# p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
# ...
#
# # random base64 string.
# p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
# p SecureRandom.base64(10) #=> "9b0nsevdwNuM/w=="
# p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
# p SecureRandom.base64(11) #=> "l7XEiFja+8EKEtY="
# p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
# p SecureRandom.base64(13) #=> "vKLJ0tXBHqQOuIcSIg=="
# ...
#
# # random binary string.
# p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
# p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
# ...
# -*- coding: us-ascii -*-
# frozen_string_literal: true

require 'jruby'
org.jruby.ext.securerandom.SecureRandomLibrary.load JRuby.runtime

# == Secure random number generator interface.
#
# This library is an interface to secure random number generators which are
# suitable for generating session keys in HTTP cookies, etc.
#
# You can use this library in your application by requiring it:
#
# require 'securerandom'
#
# It supports the following secure random number generators:
#
# * openssl
# * /dev/urandom
# * Win32
#
# === Examples
#
# Generate random hexadecimal strings:
#
# require 'securerandom'
#
# p SecureRandom.hex(10) #=> "52750b30ffbc7de3b362"
# p SecureRandom.hex(10) #=> "92b15d6c8dc4beb5f559"
# p SecureRandom.hex(13) #=> "39b290146bea6ce975c37cfc23"
#
# Generate random base64 strings:
#
# p SecureRandom.base64(10) #=> "EcmTPZwWRAozdA=="
# p SecureRandom.base64(10) #=> "KO1nIU+p9DKxGg=="
# p SecureRandom.base64(12) #=> "7kJSM/MzBJI+75j8"
#
# Generate random binary strings:
#
# p SecureRandom.random_bytes(10) #=> "\016\t{\370g\310pbr\301"
# p SecureRandom.random_bytes(10) #=> "\323U\030TO\234\357\020\a\337"
#
# Generate UUIDs:
#
# p SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
# p SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
#

module Random::Formatter

module SecureRandom
# SecureRandom.random_bytes generates a random binary string.
#
# The argument n specifies the length of the result string.
# The argument _n_ specifies the length of the result string.
#
# If n is not specified, 16 is assumed.
# If _n_ is not specified or is nil, 16 is assumed.
# It may be larger in future.
#
# The result may contain any byte: "\x00" - "\xff".
#
# p SecureRandom.random_bytes #=> "\xD8\\\xE0\xF4\r\xB2\xFC*WM\xFF\x83\x18\xF45\xB6"
# p SecureRandom.random_bytes #=> "m\xDC\xFC/\a\x00Uf\xB2\xB2P\xBD\xFF6S\x97"
#
# If secure random number generator is not available,
# NotImplementedError is raised.
def self.random_bytes(n=nil)
# replaced below by native version
raise
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
def random_bytes(n=nil)
n = n ? n.to_int : 16
gen_random(n)
end

# SecureRandom.hex generates a random hex string.
# SecureRandom.hex generates a random hexadecimal string.
#
# The argument n specifies the length of the random length.
# The length of the result string is twice of n.
# The argument _n_ specifies the length, in bytes, of the random number to be generated.
# The length of the resulting hexadecimal string is twice of _n_.
#
# If n is not specified, 16 is assumed.
# It may be larger in future.
# If _n_ is not specified or is nil, 16 is assumed.
# It may be larger in the future.
#
# The result may contain 0-9 and a-f.
#
# p SecureRandom.hex #=> "eb693ec8252cd630102fd0d0fb7c3485"
# p SecureRandom.hex #=> "91dc3bfb4de5b11d029d376634589b61"
#
# If secure random number generator is not available,
# NotImplementedError is raised.
def self.hex(n=nil)
# replaced below by native version
raise
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
def hex(n=nil)
random_bytes(n).unpack("H*")[0]
end

# SecureRandom.base64 generates a random base64 string.
#
# The argument n specifies the length of the random length.
# The length of the result string is about 4/3 of n.
# The argument _n_ specifies the length, in bytes, of the random number
# to be generated. The length of the result string is about 4/3 of _n_.
#
# If n is not specified, 16 is assumed.
# It may be larger in future.
# If _n_ is not specified or is nil, 16 is assumed.
# It may be larger in the future.
#
# The result may contain A-Z, a-z, 0-9, "+", "/" and "=".
#
# p SecureRandom.base64 #=> "/2BuBuLf3+WfSKyQbRcc/A=="
# p SecureRandom.base64 #=> "6BbW0pxO0YENxn38HMUbcQ=="
#
# If secure random number generator is not available,
# NotImplementedError is raised.
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
#
# See RFC 3548 for base64.
def self.base64(n=nil)
# See RFC 3548 for the definition of base64.
def base64(n=nil)
[random_bytes(n)].pack("m*").delete("\n")
end

if RUBY_VERSION >= "1.9.0"
# SecureRandom.urlsafe_base64 generates a random URL-safe base64 string.
#
# The argument _n_ specifies the length of the random length.
# The length of the result string is about 4/3 of _n_.
# The argument _n_ specifies the length, in bytes, of the random number
# to be generated. The length of the result string is about 4/3 of _n_.
#
# If _n_ is not specified, 16 is assumed.
# It may be larger in future.
# If _n_ is not specified or is nil, 16 is assumed.
# It may be larger in the future.
#
# The boolean argument _padding_ specifies the padding.
# If it is false or nil, padding is not generated.
@@ -115,96 +129,69 @@ def self.base64(n=nil)
# p SecureRandom.urlsafe_base64(nil, true) #=> "i0XQ-7gglIsHGV2_BNPrdQ=="
# p SecureRandom.urlsafe_base64(nil, true) #=> "-M8rLhr7JEpJlqFGUMmOxg=="
#
# If secure random number generator is not available,
# NotImplementedError is raised.
# If a secure random number generator is not available,
# +NotImplementedError+ is raised.
#
# See RFC 3548 for URL-safe base64.
def self.urlsafe_base64(n=nil, padding=false)
# See RFC 3548 for the definition of URL-safe base64.
def urlsafe_base64(n=nil, padding=false)
s = [random_bytes(n)].pack("m*")
s.delete!("\n")
s.tr!("+/", "-_")
s.delete!("=") if !padding
s.delete!("=") unless padding
s
end
end

# SecureRandom.random_number generates a random number.
#
# If an positive integer is given as n,
# SecureRandom.random_number returns an integer:
# 0 <= SecureRandom.random_number(n) < n.
# If a positive integer is given as _n_,
# +SecureRandom.random_number+ returns an integer, such that:
# +0 <= SecureRandom.random_number(n) < n+.
#
# p SecureRandom.random_number(100) #=> 15
# p SecureRandom.random_number(100) #=> 88
#
# If 0 is given or an argument is not given,
# SecureRandom.random_number returns an float:
# 0.0 <= SecureRandom.random_number() < 1.0.
# +SecureRandom.random_number+ returns a float, such that:
# +0.0 <= SecureRandom.random_number() < 1.0+.
#
# p SecureRandom.random_number #=> 0.596506046187744
# p SecureRandom.random_number #=> 0.350621695741409
#
def self.random_number(n=0)
if 0 < n
hex = n.to_s(16)
hex = '0' + hex if (hex.length & 1) == 1
bin = [hex].pack("H*")
mask = string_ord_of_initial_byte(bin)
mask |= mask >> 1
mask |= mask >> 2
mask |= mask >> 4
begin
rnd = SecureRandom.random_bytes(bin.length)
rnd[0] = (string_ord_of_initial_byte(rnd) & mask).chr
end until rnd < bin
rnd.unpack("H*")[0].hex
else
# assumption: Float::MANT_DIG <= 64
# i64 = SecureRandom.random_bytes(8).unpack("Q")[0]
# Math.ldexp(i64 >> (64-Float::MANT_DIG), -Float::MANT_DIG)
java.security.SecureRandom.new.nextDouble
end
def random_number(n=0)
# implemented natively in JRuby (just like in MRI)
raise NotImplementedError("#{__method__}(#{n})")
end

if RUBY_VERSION >= "1.9.0"
# SecureRandom.uuid generates a v4 random UUID (Universally Unique IDentifier).
# SecureRandom.uuid generates a random v4 UUID (Universally Unique IDentifier).
#
# p SecureRandom.uuid #=> "2d931510-d99f-494a-8c67-87feb05e1594"
# p SecureRandom.uuid #=> "bad85eb9-0713-4da7-8d36-07a8e4b00eab"
# p SecureRandom.uuid #=> "62936e70-1815-439b-bf89-8492855a7e6b"
#
# The version 4 UUID is purely random (except the version).
# It doesn't contain meaningful information such as MAC address, time, etc.
# It doesn't contain meaningful information such as MAC addresses, timestamps, etc.
#
# The result contains 122 random bits (15.25 random bytes).
#
# See RFC 4122 for details of UUID.
#
def self.uuid
# replaced below by native version
raise
def uuid
ary = random_bytes(16).unpack("NnnnnN")
ary[2] = (ary[2] & 0x0fff) | 0x4000
ary[3] = (ary[3] & 0x3fff) | 0x8000
"%08x-%04x-%04x-%04x-%04x%08x" % ary
end

# Following code is based on David Garamond's GUID library for Ruby.
def self.lastWin32ErrorMessage # :nodoc:
get_last_error = Win32API.new("kernel32", "GetLastError", '', 'L')
format_message = Win32API.new("kernel32", "FormatMessageA", 'LPLLPLPPPPPPPP', 'L')
format_message_ignore_inserts = 0x00000200
format_message_from_system = 0x00001000

code = get_last_error.call
msg = "\0" * 1024
len = format_message.call(format_message_ignore_inserts + format_message_from_system, 0, code, 0, msg, 1024, nil, nil, nil, nil, nil, nil, nil, nil)
msg[0, len].tr("\r", '').chomp
private
def gen_random(n)
self.bytes(n)
end
end

# Load the JRuby native ext
JRuby.reference(self).define_annotated_methods(org.jruby.ext.securerandom.SecureRandomLibrary)
SecureRandom.extend(Random::Formatter)

class << self
private
# String[0].ord for both 1.8.7 & 1.9.2 compatible.
def string_ord_of_initial_byte(bin)
bin.bytes.first
end
end
end
# NOTE: JRuby's SecureRandom native part implement some of the methods from Random::Formatter
# - SecureRandom.random_bytes(n=nil)
# - SecureRandom.hex(n=nil)
# - SecureRandom.uuid
# - SecureRandom.random_number(n=0)
22 changes: 14 additions & 8 deletions rakelib/test.rake
Original file line number Diff line number Diff line change
@@ -58,14 +58,20 @@ namespace :test do
t.ruby_opts << '--disable-gems'
end
end


max_meta_size = ENV_JAVA['java.specification.version'] > '1.7' ? '-XX:MaxMetaspaceSize' : '-XX:MaxPermSize'
get_meta_size = proc do |default_size = 452|
(ENV['JAVA_OPTS'] || '').index(max_meta_size) || (ENV['JRUBY_OPTS'] || '').index(max_meta_size) ?
'' : "-J#{max_meta_size}=#{default_size}M"
end

compile_flags = {
:default => :int,
:int => ["-X-C"],
# Note: jit.background=false is implied by jit.threshold=0, but we add it here to be sure
:fullint => ["-X-C", "-Xjit.threshold=0", "-Xjit.background=false"],
:jit => ["-Xjit.threshold=0", "-Xjit.background=false", "-J-XX:MaxPermSize=512M"],
:aot => ["-X+C", "-J-XX:MaxPermSize=512M"],
:jit => ["-Xjit.threshold=0", "-Xjit.background=false", get_meta_size.call()],
:aot => ["-X+C", get_meta_size.call()],
:all => [:int, :jit, :aot]
}

@@ -81,22 +87,22 @@ namespace :test do
namespace :mri do
mri_test_files = File.readlines('test/mri.index').grep(/^[^#]\w+/).map(&:chomp).join(' ')
task :int do
ENV['JRUBY_OPTS'] = ENV['JRUBY_OPTS'].to_s + ' -Xbacktrace.style=mri -Xdebug.fullTrace -X-C'
ENV['JRUBY_OPTS'] = "#{ENV['JRUBY_OPTS']} -Xbacktrace.style=mri -Xdebug.fullTrace -X-C"
ruby "-r ./test/mri_test_env.rb test/mri/runner.rb #{ADDITIONAL_TEST_OPTIONS} -q -- #{mri_test_files}"
end

task :fullint do
ENV['JRUBY_OPTS'] = ENV['JRUBY_OPTS'].to_s + ' -Xbacktrace.style=mri -Xdebug.fullTrace -X-C -Xjit.threshold=0 -Xjit.background=false'
ENV['JRUBY_OPTS'] = "#{ENV['JRUBY_OPTS']} -Xbacktrace.style=mri -Xdebug.fullTrace -X-C -Xjit.threshold=0 -Xjit.background=false"
ruby "-r ./test/mri_test_env.rb test/mri/runner.rb #{ADDITIONAL_TEST_OPTIONS} -q -- #{mri_test_files}"
end

task :jit do
ENV['JRUBY_OPTS'] = ENV['JRUBY_OPTS'].to_s + ' -Xbacktrace.style=mri -Xdebug.fullTrace -J-XX:MaxPermSize=512M -Xjit.threshold=0 -Xjit.background=false'
ENV['JRUBY_OPTS'] = "#{ENV['JRUBY_OPTS']} -Xbacktrace.style=mri -Xdebug.fullTrace -Xjit.threshold=0 -Xjit.background=false #{get_meta_size.call()}"
ruby "-r ./test/mri_test_env.rb test/mri/runner.rb #{ADDITIONAL_TEST_OPTIONS} -q -- #{mri_test_files}"
end

task :aot do
ENV['JRUBY_OPTS'] = ENV['JRUBY_OPTS'].to_s + ' -Xbacktrace.style=mri -Xdebug.fullTrace -J-XX:MaxPermSize=512M -X+C -Xjit.background=false'
ENV['JRUBY_OPTS'] = "#{ENV['JRUBY_OPTS']} -Xbacktrace.style=mri -Xdebug.fullTrace -X+C -Xjit.background=false #{get_meta_size.call()}"
ruby "-r ./test/mri_test_env.rb test/mri/runner.rb #{ADDITIONAL_TEST_OPTIONS} -q -- #{mri_test_files}"
end

@@ -178,7 +184,7 @@ namespace :test do

desc "Run the main JUnit test suite"
task :main => 'test:compile' do
junit :classpath => test_class_path, :test => "org.jruby.test.MainTestSuite", :maxmemory => '512M' do
junit :classpath => test_class_path, :test => "org.jruby.test.MainTestSuite", :maxmemory => '500M' do
jvmarg :line => '-ea'
end
end
30 changes: 14 additions & 16 deletions spec/jrubyc/java/constructor_spec.rb
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@ def generate(script)
describe "with no initialize method" do
it "generates a default constructor" do
cls = generate("class Foo; end").classes[0]
cls.constructor?.should be false
expect( cls.has_constructor? ).to be false

java = cls.to_s
java.should match EMPTY_INITIALIZE_PATTERN
@@ -28,23 +28,21 @@ def generate(script)
describe "and a constructor java_signature on a another method" do
it "generates a default constructor" do
cls = generate("class Foo; def initialize(a); end; java_signature 'Foo()'; def default_cnstr(); end; end").classes[0]
cls.constructor?.should be true
expect( cls.has_constructor? ).to be true

init = cls.methods[0]
init.should_not be nil
init.name.should == "initialize"
init.constructor?.should == true
init.java_signature.to_s.should == "Object initialize(Object a)"
init.args.length.should == 1
expect( init = cls.methods[0] ).to_not be nil
expect( init.name ).to eql "initialize"
expect( init.constructor? ).to be true
expect( init.java_signature.to_s ).to eql "Object initialize(Object a)"
expect( init.args.length ).to eql 1

java = init.to_s
java.should match OBJECT_INITIALIZE_PATTERN

def_cnstr = cls.methods[1]
def_cnstr.should_not be nil
def_cnstr.constructor?.should == true
def_cnstr.java_signature.to_s.should == "Foo()"
def_cnstr.args.length.should == 0
expect( def_cnstr = cls.methods[1] ).to_not be nil
expect( def_cnstr.constructor? ).to be true
expect( def_cnstr.java_signature.to_s ).to eql "Foo()"
expect( def_cnstr.args.length ).to eql 0

java = def_cnstr.to_s
java.should match EMPTY_INITIALIZE_PATTERN
@@ -54,7 +52,7 @@ def generate(script)
describe "with no arguments" do
it "generates a default constructor" do
cls = generate("class Foo; def initialize; end; end").classes[0]
expect( cls.constructor? ).to be true
expect( cls.has_constructor? ).to be true

expect( init = cls.methods[0] ).to_not be nil
expect( init.name ).to eql 'initialize'
@@ -70,7 +68,7 @@ def generate(script)
describe "with one argument and no java_signature" do
it "generates an (Object) constructor" do
cls = generate("class Foo; def initialize(a); end; end").classes[0]
expect( cls.constructor? ).to be true
expect( cls.has_constructor? ).to be true

init = cls.methods[0]
expect( init.name ).to eql 'initialize'
@@ -86,7 +84,7 @@ def generate(script)
describe "with one argument and a java_signature" do
it "generates a type-appropriate constructor" do
cls = generate("class Foo; java_signature 'Foo(String)'; def initialize(a); end; end").classes[0]
expect( cls.constructor? ).to be true
expect( cls.has_constructor? ).to be true

init = cls.methods[0]
expect( init.name ).to eql 'initialize'
31 changes: 31 additions & 0 deletions spec/jrubyc/java/files/method_visibility.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class MethodVisibility

private

def priv_method; false end

def publ_method; true end
public :publ_method

def prot_method; end
protected :prot_method

public

def hello(arg = there)
"Hello #{arg}"
end

def there
@there ||= 'World!'
end
private :there

protected

java_signature 'public int protMethodWithJSign(String)'
def prot_method_with_java_signature(str_arg)
return str_arg.size
end

end
47 changes: 47 additions & 0 deletions spec/jrubyc/java/method_spec.rb
Original file line number Diff line number Diff line change
@@ -411,6 +411,53 @@ def bar_int(a); end
end
end

describe 'with private (Ruby) :method' do

it 'does not generate the given method' do
cls = generate("class Foo; def abar; end; private :abar; def afoo; true; end; end").classes[0]

java_source = cls.to_s

expect( java_source ).to_not include 'Object abar' + '()'
expect( java_source ).to include 'public Object afoo' + '()'
end

end

describe 'with (Ruby) visibility' do

METHOD_VISIBILITY_SOURCE = File.expand_path('files/method_visibility.rb', File.dirname(__FILE__))

it 'does map public and protected methods to Java with same modifiers' do
cls = generate(File.read(METHOD_VISIBILITY_SOURCE)).classes[0]

java_source = cls.to_s

expect( java_source ).to include 'public Object publ_method' + '()'
expect( java_source ).to include 'public Object hello' + '(Object arg)'

expect( java_source ).to include 'protected Object prot_method' + '()'
end

it 'does not generate private methods (by default)' do
cls = generate(File.read(METHOD_VISIBILITY_SOURCE)).classes[0]

java_source = cls.to_s

expect( java_source ).to_not include 'Object there' + '()'
expect( java_source ).to_not include 'Object priv_method' + '()'
end

it 'explicit java_signature with modifier overrides Ruby visibility' do
cls = generate(File.read(METHOD_VISIBILITY_SOURCE)).classes[0]

java_source = cls.to_s

expect( java_source ).to include 'public int protMethodWithJSign' + '(String str_arg)'
end

end

describe "when no class definitions are present in the target script" do
before do
@source = Tempfile.new('jrubyc_method_spec')
7 changes: 7 additions & 0 deletions spec/ruby/core/time/dup_spec.rb
Original file line number Diff line number Diff line change
@@ -36,4 +36,11 @@
t.dup.should be_an_instance_of(c)
t.dup.should_not be_an_instance_of(Time)
end

it "does not copy frozen status from the original" do
t = Time.now
t.freeze
t2 = t.dup
t2.frozen?.should be_false
end
end
4 changes: 4 additions & 0 deletions spec/ruby/core/time/localtime_spec.rb
Original file line number Diff line number Diff line change
@@ -22,6 +22,10 @@
t.utc_offset.should == 3630
end

it "raises a RuntimeError on a frozen time" do
lambda { Time.new.freeze.localtime }.should raise_error(RuntimeError)
end

describe "with an argument that responds to #to_int" do
it "coerces using #to_int" do
o = mock('integer')
4 changes: 4 additions & 0 deletions spec/ruby/core/time/shared/gmtime.rb
Original file line number Diff line number Diff line change
@@ -7,4 +7,8 @@
t.should == Time.gm(2007, 1, 9, 12, 0, 0)
end
end

it "raises a RuntimeError on a frozen time" do
lambda { Time.new.freeze.send(@method) }.should raise_error(RuntimeError)
end
end
46 changes: 46 additions & 0 deletions spec/ruby/library/securerandom/random_number_spec.rb
Original file line number Diff line number Diff line change
@@ -12,6 +12,16 @@
end
end

it "generates a random (potentially bignum) integer value for bignum argument" do
max = 12345678901234567890
11.times do
num = SecureRandom.random_number max
num.should be_kind_of(Integer)
(0 <= num).should == true
(num < max).should == true
end
end

it "generates a random float number between 0.0 and 1.0 if no argument provided" do
64.times do
num = SecureRandom.random_number
@@ -21,13 +31,49 @@
end
end

it "generates a random value in given (integer) range limits" do
64.times do
num = SecureRandom.random_number 11...13
num.should be_kind_of(Integer)
(11 <= num).should == true
(num < 13).should == true
end
end

it "generates a random value in given big (integer) range limits" do
lower = 12345678901234567890
upper = 12345678901234567890 + 5
32.times do
num = SecureRandom.random_number lower..upper
num.should be_kind_of(Integer)
(lower <= num).should == true
(num <= upper).should == true
end
end

it "generates a random value in given (float) range limits" do
64.times do
num = SecureRandom.random_number 0.6..0.9
num.should be_kind_of(Float)
(0.6 <= num).should == true
(num <= 0.9).should == true
end
end

it "generates a random float number between 0.0 and 1.0 if argument is negative" do
num = SecureRandom.random_number(-10)
num.should be_kind_of(Float)
(0.0 <= num).should == true
(num < 1.0).should == true
end

it "generates a random float number between 0.0 and 1.0 if argument is negative float" do
num = SecureRandom.random_number(-11.1)
num.should be_kind_of(Float)
(0.0 <= num).should == true
(num < 1.0).should == true
end

it "generates different float numbers with subsequent invocations" do
# quick and dirty check, but good enough
values = []
1 change: 1 addition & 0 deletions test/jruby.index
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ jruby/test_frame_self
jruby/test_hash
jruby/test_higher_javasupport
jruby/test_ifaddr
jruby/test_include_order
jruby/test_included_in_object_space
jruby/test_integer_overflows
jruby/test_ivar_table_integrity
Original file line number Diff line number Diff line change
@@ -44,4 +44,4 @@ class Y
def test_include_order
assert_equal Y.new.foo, [:Z, :A, :Q]
end
end
end
119 changes: 80 additions & 39 deletions test/jruby/test_jar_complete.rb
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
# This gets run from 'ant test-jar-complete' which ensures that a
# complete jar is built first.

# Make sure we're not affected by RVM or other gem envvars
ENV.delete 'GEM_HOME'
ENV.delete 'GEM_PATH'
# gets run from 'mvn -P jruby_complete_jar_extended' which ensures that a complete jar is built first

require 'test/unit'
require 'rbconfig'
require 'fileutils'
require 'pathname'

prefix = RbConfig::CONFIG['prefix']
abort "error: test must be launched from complete jar (found prefix = #{prefix})" unless prefix =~ %r{^file.*!/META-INF/jruby\.home}
def file_from_url(path)
if RbConfig::CONFIG['host_os'] =~ /Windows|mswin/
path[%r{^file:\/*?([a-zA-Z]:/[^!]+)}, 1]
else
path[%r{^file:\/*?(/[^!]+)}, 1]
end
end
COMPLETE_JAR = file_from_url prefix
abort "error: could not figure out complete jar from RbConfig::CONFIG['prefix'] (#{prefix})" unless COMPLETE_JAR

puts "Using jar: #{COMPLETE_JAR}"
complete_jar = ( prefix =~ %r{^uri:classloader:.*?META-INF/jruby\.home} )

class JarCompleteTest < Test::Unit::TestCase
include FileUtils

# Make sure we're not affected by RVM or other gem envvars
ENV.delete 'GEM_HOME'
ENV.delete 'GEM_PATH'

@@windows = RbConfig::CONFIG['host_os'] =~ /Windows|mswin/

def file_from_url(path)
if @@windows
path[%r{^(:?uri:classloader):\/*?([a-zA-Z]:/[^!]+)}, 1]
else
path[%r{^(:?uri:classloader):\/*?(/[^!]+)}, 1]
end
end

COMPLETE_JAR = ENV_JAVA["java.class.path"].split(File::PATH_SEPARATOR).find { |path| path =~ /jruby-complete-.*?\.jar/ }

abort "error: could not figure out complete jar from 'java.class.path' (#{ENV_JAVA["java.class.path"]})" unless COMPLETE_JAR
puts "Using jar: #{COMPLETE_JAR}"


TMP_DIR = begin
# Try not to use default $TMPDIR on OS X which contains +++
tmp = [ENV['TEMP'], ENV['TMPDIR'], '/tmp', '/var/tmp'].detect {|d| d && d !~ /[ +]/ && File.directory?(d)}
@@ -64,7 +69,8 @@ def test_complete_jar
end

def test_rubygems_home
assert jruby_complete("-rubygems -e 'puts Gem.dir'").chomp =~ /#{COMPLETE_JAR}/
gem_dir = jruby_complete("-rubygems -e 'puts Gem.dir'").chomp
assert_equal 'uri:classloader:/META-INF/jruby.home/lib/ruby/gems/shared', gem_dir
end

def test_contains_rake_gem
@@ -78,38 +84,70 @@ def test_rake_help_works
end

def test_prefix_in_path_with_spaces
tmp = File.join(TMP_DIR, "hi there")
mkdir_p tmp
mkdir_p tmp = File.join(TMP_DIR, "hi there")
complete_jar = File.expand_path(File.join(tmp, 'jruby-complete.jar'))
cp COMPLETE_JAR, complete_jar
output = jruby_complete(complete_jar, "-rrbconfig -e 'puts RbConfig::CONFIG[%{prefix}]'").chomp
file = file_from_url(output)
assert File.exists?(file)
assert_equal complete_jar, file
output = jruby_complete(complete_jar, "-rrbconfig -e 'puts RbConfig::CONFIG[%{prefix}];'").chomp
assert_equal 'uri:classloader://META-INF/jruby.home', output
end

def test_prefix_in_path_with_pluses
tmp = File.join(TMP_DIR, "hi+there")
mkdir_p tmp
mkdir_p tmp = File.join(TMP_DIR, "hi+there")
complete_jar = File.expand_path(File.join(tmp, 'jruby-complete.jar'))
cp COMPLETE_JAR, complete_jar
output = jruby_complete(complete_jar, "-rrbconfig -e 'puts RbConfig::CONFIG[%{prefix}]'").chomp
file = file_from_url(output)
assert File.exists?(file)
assert_equal complete_jar, file
#file = file_from_url(output)
#assert File.exists?(file), "file: #{file} (from #{output.inspect}) does not exist"
assert_equal 'uri:classloader://META-INF/jruby.home', output
end

# JRUBY-5337
def test_script_with__FILE__constant_in_jar_with_spaces
tmp = File.join(TMP_DIR, "hi there")
mkdir_p tmp
mkdir_p tmp = File.join(TMP_DIR, "hi there")
complete_jar = File.expand_path(File.join(tmp, 'jruby-complete.jar'))
cp COMPLETE_JAR, complete_jar
script = File.join(TMP_DIR, "_file_constant_.rb")
File.open(script, "wb") {|f| f.puts "puts __FILE__" }
script = File.join(TMP_DIR, '_file_constant_.rb')
File.open(script, 'wb') {|f| f.puts "puts __FILE__" }
Dir.chdir(File.dirname(script)) { system %{jar uf "#{complete_jar}" #{File.basename(script)}} }
output = jruby_complete(complete_jar, %{-e "require '_file_constant_'"}).chomp
assert output =~ /#{tmp}/, "'#{output}' does not match '#{tmp}'"
assert_match /uri:classloader\:\/_file_constant_\.rb/, output
end

def test_globing_with__dir__in_jar # GH-4611
mkdir_p tmp = File.join(TMP_DIR, __method__.to_s)
complete_jar = File.expand_path(File.join(tmp, 'jruby-complete.jar'))
cp COMPLETE_JAR, complete_jar

File.open(sample = File.join(TMP_DIR, 'sample.rb'), 'wb') do |f|
f.puts 'puts "sample " + __dir__'
end
File.open(_init_ = File.join(TMP_DIR, '_init_.rb'), 'wb') do |f|
f.puts 'puts __FILE__'
f.puts 'puts File.dirname(__FILE__)'
f.puts 'puts __dir__'
f.puts 'Dir[ "#{__dir__}/*.rb" ].each { |f| require f }'
end
File.open(_jruby = File.join(TMP_DIR, '.jrubydir'), 'wb') do |f|
f.puts '.'
f.puts '_init_.rb'
f.puts 'sample.rb'
f.puts ''
end

Dir.chdir(File.dirname(_init_)) do
files = [sample, _init_, _jruby].map { |f| File.basename(f) }
system %{jar uf "#{complete_jar}" #{files.join(' ')}}
end
output = jruby_complete(complete_jar, %{-e "require '_init_'"}).chomp

puts output.inspect if $VERBOSE

output = output.split("\n")

assert_equal 'uri:classloader:/_init_.rb', output[0] # __FILE
assert_equal 'uri:classloader:/', output[1] # File.dirname(__FILE__)
assert_equal 'uri:classloader:/', output[2] # __dir__
assert_equal 'sample uri:classloader:/', output[3] # sample: ...
end

def test_binscripts_can_be_run_from_classpath
@@ -119,10 +157,13 @@ def test_binscripts_can_be_run_from_classpath
end

def test_relative_require_from_gem_on_classpath
relative_require_gem = File.expand_path(File.join(File.dirname(__FILE__), "..", "samples", "relative_require.jar"))
relative_require_jar = File.expand_path('samples/relative_require.jar', File.join(File.dirname(__FILE__), '../..'))

`java -cp \"#{COMPLETE_JAR}:#{relative_require_gem}\" org.jruby.Main -rrelative_require -e "puts RelativeRequire::VERSION"`
`java -cp \"#{COMPLETE_JAR}:#{relative_require_jar}\" org.jruby.Main -rrelative_require -e "puts RelativeRequire::VERSION"`

assert $? == 0
assert $? == 0, "`java -cp ... org.jruby.Main -rrelative_require returned: #{$?.inspect}"
end
end

end if complete_jar

warn "#{__FILE__} must be launched from complete jar (found prefix = #{prefix})" unless complete_jar
13 changes: 7 additions & 6 deletions test/pom.rb
Original file line number Diff line number Diff line change
@@ -138,11 +138,11 @@
jar 'org.jruby:jruby-complete', '${project.version}', :scope => :provided

plugin :antrun do
[ 'jruby','objectspace', 'slow' ].each do |index|
files = ""
File.open(File.join(basedir, index + '.index')) do |f|
f.each_line.each do |line|
next if line =~ /^#/ or line.strip.empty?
[ 'jruby', 'objectspace', 'slow' ].each do |index|
files = []
File.open(File.join(basedir, index + '.index')) do |file|
file.each_line do |line|
next if line =~ /^#/ || line.strip.empty?
filename = "mri/#{line.chomp}"
filename = "jruby/#{line.chomp}.rb" unless File.exist? File.join(basedir, filename)
filename = "#{line.chomp}.rb" unless File.exist? File.join(basedir, filename)
@@ -152,9 +152,10 @@
files << "<arg value='test/#{filename}'/>"
end
end
files = files.join('')

execute_goals( 'run',
:id => 'jruby_complete_jar_' + index,
:id => "jruby_complete_jar_#{index}",
:phase => 'test',
:configuration => [ xml( "<target><exec dir='${jruby.home}' executable='java' failonerror='true'><arg value='-cp'/><arg value='core/target/test-classes:test/target/test-classes:maven/jruby-complete/target/jruby-complete-${project.version}.jar'/><arg value='-Djruby.aot.loadClasses=true'/><arg value='org.jruby.Main'/><arg value='-I.'/><arg value='-Itest/mri/ruby'/><arg value='-Itest/mri'/><arg value='-Itest'/><arg value='-rtest/mri_test_env'/><arg value='lib/ruby/stdlib/rake/rake_test_loader.rb'/>#{files}<arg value='-v'/></exec></target>" ) ] )
end
2 changes: 2 additions & 0 deletions test/slow.index
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
jruby/test_command_line_switches
jruby/test_launching_by_shell_script
jruby/test_ffi_pointer_leak
# only run when executed with jruby-complete.jar (jruby_complete_jar_extended profile)
jruby/test_jar_complete

0 comments on commit 132db9e

Please sign in to comment.