Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: fccf3a56e1a5
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 83901f3157a8
Choose a head ref
  • 3 commits
  • 5 files changed
  • 1 contributor

Commits on Dec 15, 2015

  1. Copy the full SHA
    c93581a View commit details
  2. Copy the full SHA
    0dab314 View commit details
  3. Copy the full SHA
    83901f3 View commit details
62 changes: 57 additions & 5 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -57,7 +57,6 @@
import org.jruby.internal.runtime.methods.DynamicMethod;
import org.jruby.internal.runtime.methods.Framing;
import org.jruby.internal.runtime.methods.JavaMethod;
import org.jruby.internal.runtime.methods.MixedModeIRMethod;
import org.jruby.internal.runtime.methods.ProcMethod;
import org.jruby.internal.runtime.methods.Scoping;
import org.jruby.internal.runtime.methods.SynchronizedDynamicMethod;
@@ -68,10 +67,8 @@
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.CallSite;
import org.jruby.runtime.ClassIndex;
import org.jruby.runtime.DynamicScope;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.IRBlockBody;
import org.jruby.runtime.MethodFactory;
@@ -3239,6 +3236,30 @@ public Collection<String> constantsCommon(Ruby runtime, boolean replaceModule, b
return constantNames;
}

public void deprecateConstant(Ruby runtime, String name) {
ConstantEntry entry = getConstantMap().get(name);
if (entry == null) {
throw runtime.newNameError("constant " + getName() + "::" + name + " not defined", name);
}

storeConstant(name, entry.value, entry.hidden, true);
invalidateConstantCache(name);
}

@JRubyMethod
public IRubyObject deprecate_constant(ThreadContext context, IRubyObject rname) {
deprecateConstant(context.runtime, validateConstant(rname));
return this;
}

@JRubyMethod(rest = true)
public IRubyObject deprecate_constant(ThreadContext context, IRubyObject[] names) {
for (IRubyObject rname : names) {
deprecate_constant(context, rname);
}
return this;
}

@JRubyMethod
public IRubyObject private_constant(ThreadContext context, IRubyObject rubyName) {
String name = validateConstant(rubyName);
@@ -3932,6 +3953,15 @@ public IRubyObject fetchConstant(String name, boolean includePrivate) {
if (entry.hidden && !includePrivate) {
throw getRuntime().newNameError("private constant " + getName() + "::" + name + " referenced", name);
}
if (entry.deprecated) {
final Ruby runtime = getRuntime();
if ( "Object".equals( getName() ) ) {
runtime.getWarnings().warn(ID.CONSTANT_DEPRECATED, "constant ::"+ name +" is deprecated");
}
else {
runtime.getWarnings().warn(ID.CONSTANT_DEPRECATED, "constant "+ getName() +"::"+ name +" is deprecated");
}
}

return entry.value;
}
@@ -3957,6 +3987,15 @@ public IRubyObject storeConstant(String name, IRubyObject value, boolean hidden)
return constantTableStore(name, value, hidden);
}

// NOTE: private for now - not sure about the API - maybe an int mask would be better?
private IRubyObject storeConstant(String name, IRubyObject value, boolean hidden, boolean deprecated) {
assert IdUtil.isConstant(name) : name + " is not a valid constant name";
assert value != null : "value is null";

ensureConstantsSettable();
return constantTableStore(name, value, hidden, deprecated);
}

@Deprecated
public IRubyObject fastStoreConstant(String internedName, IRubyObject value) {
return storeConstant(internedName, value);
@@ -4070,8 +4109,12 @@ protected IRubyObject constantTableStore(String name, IRubyObject value) {
}

protected IRubyObject constantTableStore(String name, IRubyObject value, boolean hidden) {
return constantTableStore(name, value, hidden, false);
}

protected IRubyObject constantTableStore(String name, IRubyObject value, boolean hidden, boolean deprecated) {
Map<String, ConstantEntry> constMap = getConstantMapForWrite();
constMap.put(name, new ConstantEntry(value, hidden));
constMap.put(name, new ConstantEntry(value, hidden, deprecated));
return value;
}

@@ -4264,6 +4307,7 @@ public IRubyObject initialize(Block block) {
*/
private String cachedName;

@SuppressWarnings("unchecked")
private volatile Map<String, ConstantEntry> constants = Collections.EMPTY_MAP;

/**
@@ -4272,14 +4316,22 @@ public IRubyObject initialize(Block block) {
public static class ConstantEntry {
public final IRubyObject value;
public final boolean hidden;
final boolean deprecated;

public ConstantEntry(IRubyObject value, boolean hidden) {
this.value = value;
this.hidden = hidden;
this.deprecated = false;
}

ConstantEntry(IRubyObject value, boolean hidden, boolean deprecated) {
this.value = value;
this.hidden = hidden;
this.deprecated = deprecated;
}

public ConstantEntry dup() {
return new ConstantEntry(value, hidden);
return new ConstantEntry(value, hidden, deprecated);
}
}

9 changes: 5 additions & 4 deletions core/src/main/java/org/jruby/common/IRubyWarnings.java
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@
* Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
* Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
* Copyright (C) 2004 Thomas E Enebo <enebo@acm.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"),
@@ -49,6 +49,7 @@ public enum ID {
BLOCK_NOT_ACCEPTED,
BLOCK_UNUSED,
CONSTANT_ALREADY_INITIALIZED,
CONSTANT_DEPRECATED,
CONSTANT_BAD_REFERENCE,
CVAR_FROM_TOPLEVEL_SINGLETON_METHOD,
DECLARING_SCLASS_VARIABLE,
@@ -74,7 +75,7 @@ public enum ID {
OBSOLETE_ARGUMENT,
PARENTHISE_ARGUMENTS,
PROXY_EXTENDED_LATE,
STATEMENT_NOT_REACHED,
STATEMENT_NOT_REACHED,
LITERAL_IN_CONDITIONAL_RANGE,
REDEFINING_DANGEROUS,
REGEXP_IGNORED_FLAGS,
@@ -100,15 +101,15 @@ public enum ID {
GC_DISABLE_UNIMPLEMENTED,
TRUFFLE,
RATIONAL_OUT_OF_RANGE,; // TODO(CS): divide up the Truffle warnings

public String getID() {
return name();
}
}

public abstract Ruby getRuntime();
public abstract boolean isVerbose();

public abstract void warn(ID id, ISourcePosition position, String message);
public abstract void warn(ID id, String fileName, int lineNumber, String message);
public abstract void warn(ID id, String fileName, String message);
1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ext/timeout/Timeout.java
Original file line number Diff line number Diff line change
@@ -76,6 +76,7 @@ public void load(Ruby runtime, boolean wrap) throws IOException {

// Toplevel defines
runtime.getObject().defineConstant("TimeoutError", TimeoutError);
runtime.getObject().deprecateConstant(runtime, "TimeoutError");
runtime.getObject().defineAnnotatedMethods(TimeoutToplevel.class);
}

5 changes: 2 additions & 3 deletions test/mri/excludes/TestModule.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
exclude :test_alias, "needs investigation"
exclude :test_attr, "needs investigation"
exclude :test_attr_inherited_visibility, "IR regression"
exclude :test_classpath, "needs investigation"
exclude :test_const_get_inherited, "needs investigation"
exclude :test_const_set_invalid_name, "UTF-16 invalid name is accepted as ok"
exclude :test_define_module_under_private_constant, "we don't evaluate colon2-like class opening via colon2 logic, can't tell if private are ok"
exclude :test_include_module_with_constants_does_not_invalidate_method_cache, "no RubyVM constant in JRuby"
exclude :test_initialize_copy_empty, "needs investigation"
exclude :test_inspect_segfault, "a minor inspect difference?"
exclude :test_invalid_attr, "needs investigation"
exclude :test_leading_colons, "needs investigation"
exclude :test_method_added, "needs investigation"
exclude :test_method_redefinition, "needs investigation"
exclude :test_private_constant_reopen, "needs investigation"
exclude :test_private_constant_with_no_args, "needs investigation"
exclude :test_protected_singleton_method, "needs investigation"
exclude :test_undef, "needs investigation"
exclude :test_prepend_each_classes, "needs investigation - ancestors do not match"
99 changes: 92 additions & 7 deletions test/mri/ruby/test_module.rb
Original file line number Diff line number Diff line change
@@ -678,14 +678,18 @@ def const_missing(x)

def test_const_set_invalid_name
c1 = Class.new
assert_raise(NameError) { c1.const_set(:foo, :foo) }
assert_raise(NameError) { c1.const_set("bar", :foo) }
assert_raise(TypeError) { c1.const_set(1, :foo) }
assert_raise_with_message(NameError, /foo/) { c1.const_set(:foo, :foo) }
assert_raise_with_message(NameError, /bar/) { c1.const_set("bar", :foo) }
assert_raise_with_message(TypeError, /1/) { c1.const_set(1, :foo) }
assert_nothing_raised(NameError) { c1.const_set("X\u{3042}", :foo) }
assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-16be"), :foo) }
assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-16le"), :foo) }
assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-32be"), :foo) }
assert_raise(NameError) { c1.const_set("X\u{3042}".encode("utf-32le"), :foo) }
cx = EnvUtil.labeled_class("X\u{3042}")
EnvUtil.with_default_internal(Encoding::UTF_8) {
assert_raise_with_message(TypeError, /X\u{3042}/) { c1.const_set(cx, :foo) }
}
end

def test_const_get_invalid_name
@@ -823,6 +827,9 @@ def test_remove_class_variable
c.class_eval('@@foo = :foo')
c.class_eval { remove_class_variable(:@@foo) }
assert_equal(false, c.class_variable_defined?(:@@foo))
assert_raise(NameError) do
c.class_eval { remove_class_variable(:@var) }
end
end

def test_export_method
@@ -1083,6 +1090,8 @@ def test_nonascii_name
assert_equal("C\u{df}", c.name, '[ruby-core:24600]')
c = eval("class C\u{df}; self; end")
assert_equal("TestModule::C\u{df}", c.name, '[ruby-core:24600]')
c = Module.new.module_eval("class X\u{df} < Module; self; end")
assert_match(/::X\u{df}:/, c.new.to_s)
end

def test_method_added
@@ -1352,6 +1361,13 @@ def test_public_constant
assert_equal("foo", c::FOO)
end

def test_deprecate_constant
c = Class.new
c.const_set(:FOO, "foo")
c.deprecate_constant(:FOO)
assert_warn(/deprecated/) {c::FOO}
end

def test_constants_with_private_constant
assert_not_include(::TestModule.constants, :PrivateClass)
end
@@ -1721,6 +1737,38 @@ def hello
assert_equal('hello!', foo.new.hello, bug9236)
end

def test_prepend_each_classes
m = labeled_module("M")
c1 = labeled_class("C1") {prepend m}
c2 = labeled_class("C2", c1) {prepend m}
assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should be able to prepend each classes")
end

def test_prepend_no_duplication
m = labeled_module("M")
c = labeled_class("C") {prepend m; prepend m}
assert_equal([m, c], c.ancestors[0, 2], "should never duplicate")
end

def test_prepend_in_superclass
m = labeled_module("M")
c1 = labeled_class("C1")
c2 = labeled_class("C2", c1) {prepend m}
c1.class_eval {prepend m}
assert_equal([m, c2, m, c1], c2.ancestors[0, 4], "should accesisble prepended module in superclass")
end

def test_prepend_call_super
assert_separately([], <<-'end;') #do
bug10847 = '[ruby-core:68093] [Bug #10847]'
module M; end
Float.prepend M
assert_nothing_raised(SystemStackError, bug10847) do
0.3.numerator
end
end;
end

def test_class_variables
m = Module.new
m.class_variable_set(:@@foo, 1)
@@ -1922,8 +1970,8 @@ def test_visibility_by_public_class_method
assert_raise(NoMethodError, bug8284) {Object.define_method}
end

def test_include_module_with_constants_invalidates_method_cache
assert_in_out_err([], <<-RUBY, %w(123 456), [])
def test_include_module_with_constants_does_not_invalidate_method_cache
assert_in_out_err([], <<-RUBY, %w(123 456 true), [])
A = 123
class Foo
@@ -1937,8 +1985,13 @@ module M
end
puts Foo.a
starting = RubyVM.stat[:global_method_state]
Foo.send(:include, M)
ending = RubyVM.stat[:global_method_state]
puts Foo.a
puts starting == ending
RUBY
end

@@ -1997,11 +2050,43 @@ class A
A.prepend InspectIsShallow
expect = "#<Method: A(Object)#inspect(shallow_inspect)>"
expect = "#<Method: A(ShallowInspect)#inspect(shallow_inspect)>"
assert_equal expect, A.new.method(:inspect).inspect, "#{bug_10282}"
RUBY
end

def test_define_method_with_unbound_method
# Passing an UnboundMethod to define_method succeeds if it is from an ancestor
assert_nothing_raised do
cls = Class.new(String) do
define_method('foo', String.instance_method(:to_s))
end

obj = cls.new('bar')
assert_equal('bar', obj.foo)
end

# Passing an UnboundMethod to define_method fails if it is not from an ancestor
assert_raise(TypeError) do
Class.new do
define_method('foo', String.instance_method(:to_s))
end
end
end

def test_redefinition_mismatch
m = Module.new
m.module_eval "A = 1"
assert_raise_with_message(TypeError, /is not a module/) {
m.module_eval "module A; end"
}
n = "M\u{1f5ff}"
m.module_eval "#{n} = 42"
assert_raise_with_message(TypeError, "#{n} is not a module") {
m.module_eval "module #{n}; end"
}
end

private

def assert_top_method_is_private(method)
@@ -2014,4 +2099,4 @@ def assert_top_method_is_private(method)
}
}
end
end
end