Skip to content

Commit

Permalink
Merge pull request #4179 from jruby/truffle-module-deprecate-constant
Browse files Browse the repository at this point in the history
[Truffle] Module#deprecate_constant
  • Loading branch information
eregon committed Sep 29, 2016
2 parents 8b406d5 + dc20ea8 commit 439882b
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 19 deletions.
5 changes: 0 additions & 5 deletions spec/truffle/tags/core/module/deprecate_constant_tags.txt

This file was deleted.

Expand Up @@ -312,7 +312,8 @@ public void setConstantInternal(RubyContext context, Node currentNode, String na
while (true) {
final RubyConstant previous = constants.get(name);
final boolean isPrivate = previous != null && previous.isPrivate();
final RubyConstant newValue = new RubyConstant(rubyModuleObject, value, isPrivate, autoload);
final boolean isDeprecated = previous != null && previous.isDeprecated();
final RubyConstant newValue = new RubyConstant(rubyModuleObject, value, isPrivate, autoload, isDeprecated);

if ((previous == null) ? (constants.putIfAbsent(name, newValue) == null) : constants.replace(name, previous, newValue)) {
newLexicalVersion();
Expand Down Expand Up @@ -453,6 +454,22 @@ public void changeConstantVisibility(
}
}

@TruffleBoundary
public void deprecateConstant(RubyContext context, Node currentNode, String name) {
while (true) {
final RubyConstant previous = constants.get(name);

if (previous == null) {
throw new RaiseException(context.getCoreExceptions().nameErrorUninitializedConstant(rubyModuleObject, name, currentNode));
}

if (constants.replace(name, previous, previous.withDeprecated())) {
newLexicalVersion();
break;
}
}
}

public RubyContext getContext() {
return context;
}
Expand Down
Expand Up @@ -1629,6 +1629,26 @@ public DynamicObject privateConstant(VirtualFrame frame, DynamicObject module, O
}
}

@CoreMethod(names = "deprecate_constant", rest = true, raiseIfFrozenSelf = true)
public abstract static class DeprecateConstantNode extends CoreMethodArrayArgumentsNode {

@Child NameToJavaStringNode nameToJavaStringNode = NameToJavaStringNode.create();

public DeprecateConstantNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
this.nameToJavaStringNode = NameToJavaStringNode.create();
}

@Specialization
public DynamicObject deprecateConstant(VirtualFrame frame, DynamicObject module, Object[] args) {
for (Object arg : args) {
String name = nameToJavaStringNode.executeToJavaString(frame, arg);
Layouts.MODULE.getFields(module).deprecateConstant(getContext(), this, name);
}
return module;
}
}

@CoreMethod(names = "public_constant", rest = true)
public abstract static class PublicConstantNode extends CoreMethodArrayArgumentsNode {

Expand Down
18 changes: 16 additions & 2 deletions truffle/src/main/java/org/jruby/truffle/language/RubyConstant.java
Expand Up @@ -20,13 +20,15 @@ public class RubyConstant {
private final Object value;
private final boolean isPrivate;
private final boolean autoload;
private final boolean isDeprecated;

public RubyConstant(DynamicObject declaringModule, Object value, boolean isPrivate, boolean autoload) {
public RubyConstant(DynamicObject declaringModule, Object value, boolean isPrivate, boolean autoload, boolean isDeprecated) {
assert RubyGuards.isRubyModule(declaringModule);
this.declaringModule = declaringModule;
this.value = value;
this.isPrivate = isPrivate;
this.autoload = autoload;
this.isDeprecated = isDeprecated;
}

public DynamicObject getDeclaringModule() {
Expand All @@ -41,11 +43,23 @@ public boolean isPrivate() {
return isPrivate;
}

public boolean isDeprecated() {
return isDeprecated;
}

public RubyConstant withPrivate(boolean isPrivate) {
if (isPrivate == this.isPrivate) {
return this;
} else {
return new RubyConstant(declaringModule, value, isPrivate, autoload);
return new RubyConstant(declaringModule, value, isPrivate, autoload, isDeprecated);
}
}

public RubyConstant withDeprecated() {
if (this.isDeprecated()) {
return this;
} else {
return new RubyConstant(declaringModule, value, isPrivate, autoload, true);
}
}

Expand Down
23 changes: 23 additions & 0 deletions truffle/src/main/java/org/jruby/truffle/language/WarnNode.java
@@ -0,0 +1,23 @@
package org.jruby.truffle.language;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.truffle.language.dispatch.CallDispatchHeadNode;

public class WarnNode extends RubyBaseNode {

@Child CallDispatchHeadNode warnMethod = CallDispatchHeadNode.createMethodCall();

public Object execute(VirtualFrame frame, String... arguments) {
final String warningMessage = concatArgumentsToString(arguments);
final DynamicObject warningString = createString(warningMessage.getBytes(), UTF8Encoding.INSTANCE);
return warnMethod.call(frame, getContext().getCoreLibrary().getKernelModule(), "warn", warningString);
}

@TruffleBoundary
private String concatArgumentsToString(String... arguments) {
return String.join("", arguments);
}
}
Expand Up @@ -10,6 +10,7 @@
package org.jruby.truffle.language.constants;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
Expand All @@ -23,6 +24,7 @@
import org.jruby.truffle.language.LexicalScope;
import org.jruby.truffle.language.RubyConstant;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.WarnNode;
import org.jruby.truffle.language.control.RaiseException;

/**
Expand All @@ -37,6 +39,7 @@ public abstract class LookupConstantNode extends RubyNode implements LookupConst

private final boolean ignoreVisibility;
private final boolean lookInObject;
@Child private WarnNode warnNode;

public static LookupConstantNode create(boolean ignoreVisibility, boolean lookInObject) {
return LookupConstantNodeGen.create(ignoreVisibility, lookInObject, null, null);
Expand Down Expand Up @@ -74,22 +77,37 @@ protected RubyConstant lookupConstant(
if (!isVisible) {
throw new RaiseException(coreExceptions().nameErrorPrivateConstant(module, name, this));
}
if (constant != null && constant.isDeprecated()) {
warnDeprecatedConstant(frame, name);
}
return constant;
}

private void warnDeprecatedConstant(VirtualFrame frame, String name) {
if (warnNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
warnNode = insert(new WarnNode());
}
warnNode.execute(frame, "constant ", name, " is deprecated");
}

public Assumption getUnmodifiedAssumption(DynamicObject module) {
return Layouts.MODULE.getFields(module).getUnmodifiedAssumption();
}

@TruffleBoundary
@Specialization(guards = "isRubyModule(module)")
protected RubyConstant lookupConstantUncached(DynamicObject module, String name) {
protected RubyConstant lookupConstantUncached(VirtualFrame frame, DynamicObject module, String name,
@Cached("createBinaryProfile()") ConditionProfile isVisibleProfile,
@Cached("createBinaryProfile()") ConditionProfile isDeprecatedProfile) {
RubyConstant constant = doLookup(module, name);
boolean isVisible = isVisible(module, constant);

if (!isVisible) {
if (isVisibleProfile.profile(!isVisible)) {
throw new RaiseException(coreExceptions().nameErrorPrivateConstant(module, name, this));
}
if (isDeprecatedProfile.profile(constant != null && constant.isDeprecated())) {
warnDeprecatedConstant(frame, name);
}
return constant;
}

Expand All @@ -108,6 +126,7 @@ protected boolean guardName(String name, String cachedName, ConditionProfile sam
}
}

@TruffleBoundary
protected RubyConstant doLookup(DynamicObject module, String name) {
if (lookInObject) {
return ModuleOperations.lookupConstantAndObject(getContext(), module, name);
Expand All @@ -116,6 +135,7 @@ protected RubyConstant doLookup(DynamicObject module, String name) {
}
}

@TruffleBoundary
protected boolean isVisible(DynamicObject module, RubyConstant constant) {
return ignoreVisibility ||
constant == null ||
Expand Down
Expand Up @@ -10,22 +10,27 @@
package org.jruby.truffle.language.constants;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node.Child;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.core.module.ModuleOperations;
import org.jruby.truffle.language.LexicalScope;
import org.jruby.truffle.language.RubyConstant;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.WarnNode;
import org.jruby.truffle.language.control.RaiseException;

public abstract class LookupConstantWithLexicalScopeNode extends RubyNode implements LookupConstantInterface {

private final LexicalScope lexicalScope;
private final String name;
@Child private WarnNode warnNode;

public LookupConstantWithLexicalScopeNode(LexicalScope lexicalScope, String name) {
this.lexicalScope = lexicalScope;
Expand All @@ -44,35 +49,52 @@ public RubyConstant lookupConstant(VirtualFrame frame, Object module, String nam
}

@Specialization(assumptions = "getUnmodifiedAssumption(getModule())")
protected RubyConstant lookupConstant(
protected RubyConstant lookupConstant(VirtualFrame frame,
@Cached("doLookup()") RubyConstant constant,
@Cached("isVisible(constant)") boolean isVisible) {
if (!isVisible) {
throw new RaiseException(coreExceptions().nameErrorPrivateConstant(getModule(), name, this));
}
if (constant != null && constant.isDeprecated()) {
warnDeprecatedConstant(frame, name);
}
return constant;
}

@TruffleBoundary
@Specialization(assumptions = "getUnmodifiedAssumption(getModule())")
protected RubyConstant lookupConstantUncached() {
protected RubyConstant lookupConstantUncached(VirtualFrame frame,
@Cached("createBinaryProfile()") ConditionProfile isVisibleProfile,
@Cached("createBinaryProfile()") ConditionProfile isDeprecatedProfile) {
RubyConstant constant = doLookup();
if (!isVisible(constant)) {
if (isVisibleProfile.profile(!isVisible(constant))) {
throw new RaiseException(coreExceptions().nameErrorPrivateConstant(getModule(), name, this));
}
if (isDeprecatedProfile.profile(constant != null && constant.isDeprecated())) {
warnDeprecatedConstant(frame, name);
}
return constant;
}

public Assumption getUnmodifiedAssumption(DynamicObject module) {
return Layouts.MODULE.getFields(module).getUnmodifiedAssumption();
}

@TruffleBoundary
protected RubyConstant doLookup() {
return ModuleOperations.lookupConstantWithLexicalScope(getContext(), lexicalScope, name);
}

@TruffleBoundary
protected boolean isVisible(RubyConstant constant) {
return constant == null || constant.isVisibleTo(getContext(), lexicalScope, getModule());
}

private void warnDeprecatedConstant(VirtualFrame frame, String name) {
if (warnNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
warnNode = insert(new WarnNode());
}
warnNode.execute(frame, "constant ", name, " is deprecated");
}

}
Expand Up @@ -23,20 +23,22 @@
import org.jruby.truffle.language.LexicalScope;
import org.jruby.truffle.language.RubyConstant;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.WarnNode;
import org.jruby.truffle.language.control.RaiseException;
import org.jruby.truffle.language.loader.RequireNode;

@NodeChildren({ @NodeChild("name"), @NodeChild("lexicalParent") })
public abstract class LookupForExistingModuleNode extends RubyNode {

@Child private RequireNode requireNode;
@Child private WarnNode warnNode;

public abstract RubyConstant executeLookupForExistingModule(VirtualFrame frame, String name, DynamicObject lexicalParent);

@Specialization(guards = "isRubyModule(lexicalParent)")
public RubyConstant lookupForExistingModule(VirtualFrame frame, String name, DynamicObject lexicalParent,
@Cached("createBinaryProfile()") ConditionProfile autoloadProfile) {
RubyConstant constant = deepConstantSearch(name, lexicalParent);
RubyConstant constant = deepConstantSearch(frame, name, lexicalParent);

// If a constant already exists with this class/module name and it's an autoload module, we have to trigger
// the autoload behavior before proceeding.
Expand All @@ -49,14 +51,13 @@ public RubyConstant lookupForExistingModule(VirtualFrame frame, String name, Dyn

Layouts.MODULE.getFields(lexicalParent).removeConstant(getContext(), this, name);
getRequireNode().executeRequire(frame, StringOperations.getString((DynamicObject) constant.getValue()));
return deepConstantSearch(name, lexicalParent);
return deepConstantSearch(frame, name, lexicalParent);
}

return constant;
}

@TruffleBoundary
private RubyConstant deepConstantSearch(String name, DynamicObject lexicalParent) {
private RubyConstant deepConstantSearch(VirtualFrame frame, String name, DynamicObject lexicalParent) {
RubyConstant constant = Layouts.MODULE.getFields(lexicalParent).getConstant(name);

final DynamicObject objectClass = getContext().getCoreLibrary().getObjectClass();
Expand All @@ -74,6 +75,9 @@ private RubyConstant deepConstantSearch(String name, DynamicObject lexicalParent
if (constant != null && !constant.isVisibleTo(getContext(), LexicalScope.NONE, lexicalParent)) {
throw new RaiseException(getContext().getCoreExceptions().nameErrorPrivateConstant(lexicalParent, name, this));
}
if (constant != null && constant.isDeprecated()) {
warnDeprecatedConstant(frame, name);
}
return constant;
}

Expand All @@ -85,4 +89,12 @@ public RequireNode getRequireNode() {
return requireNode;
}

private void warnDeprecatedConstant(VirtualFrame frame, String name) {
if (warnNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
warnNode = insert(new WarnNode());
}
warnNode.execute(frame, "constant ", name, " is deprecated");
}

}

0 comments on commit 439882b

Please sign in to comment.