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: 0845943c06e9
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: d2ed0c12160e
Choose a head ref
  • 4 commits
  • 16 files changed
  • 1 contributor

Commits on Nov 7, 2014

  1. Copy the full SHA
    cb1fe73 View commit details
  2. [Truffle] Implement private constants and pass all their specs.

    * The ReadConstantNode is now passed its LexicalScope.
      We still need to have it in SharedMethodInfo only for Module.nesting (if aliased).
    * Add an RubyModule.includedModules() iterator,
      as ancestors() but stopping at the first class seen.
    eregon committed Nov 7, 2014
    Copy the full SHA
    6abc370 View commit details
  3. Copy the full SHA
    719e970 View commit details
  4. Copy the full SHA
    d2ed0c1 View commit details
21 changes: 7 additions & 14 deletions core/src/main/java/org/jruby/truffle/nodes/ReadConstantNode.java
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.source.*;
import com.oracle.truffle.api.frame.*;
import org.jruby.truffle.nodes.core.TruffleDebugNodes;
import org.jruby.truffle.nodes.dispatch.Dispatch;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.runtime.*;
@@ -23,24 +24,18 @@
public class ReadConstantNode extends RubyNode {

private final String name;
private final LexicalScope lexicalScope;
@Child protected RubyNode receiver;
@Child protected DispatchHeadNode dispatch;

public ReadConstantNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver) {
public ReadConstantNode(RubyContext context, SourceSection sourceSection, String name, RubyNode receiver, LexicalScope lexicalScope) {
super(context, sourceSection);
this.name = name;
this.lexicalScope = lexicalScope;
this.receiver = receiver;
dispatch = new DispatchHeadNode(context, Dispatch.MissingBehavior.CALL_CONST_MISSING);
}

private LexicalScope getLexicalScope(VirtualFrame frame) {
MethodLike method = RubyArguments.getMethod(frame.getArguments());
if (method != null) {
return method.getSharedMethodInfo().getLexicalScope();
}
return null;
}

@Override
public Object execute(VirtualFrame frame) {
final Object receiverObject = receiver.execute(frame);
@@ -50,8 +45,6 @@ public Object execute(VirtualFrame frame) {
throw new RaiseException(getContext().getCoreLibrary().typeErrorIsNotA(receiverObject.toString(), "class/module", this));
}

LexicalScope lexicalScope = getLexicalScope(frame);

return dispatch.dispatch(
frame,
getContext().getCoreLibrary().getNilObject(),
@@ -97,10 +90,10 @@ public Object isDefined(VirtualFrame frame) {
throw e;
}

LexicalScope lexicalScope = getLexicalScope(frame);
RubyConstant constant = ModuleOperations.lookupConstant(lexicalScope, (RubyModule) receiverObject, name);
RubyModule module = (RubyModule) receiverObject; // TODO(cs): cast
RubyConstant constant = ModuleOperations.lookupConstant(context, lexicalScope, module, name);

if (constant == null) {
if (constant == null || !constant.isVisibleTo(context, lexicalScope, module)) {
return getContext().getCoreLibrary().getNilObject();
} else {
return context.makeString("constant");
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ private static void addMethod(RubyClass rubyObjectClass, MethodDetails methodDet
module = rubyObjectClass;

for (String moduleName : methodDetails.getClassAnnotation().name().split("::")) {
module = (RubyModule) ModuleOperations.lookupConstant(null, module, moduleName).getValue();
module = (RubyModule) ModuleOperations.lookupConstant(context, null, module, moduleName).getValue();
}
}

Original file line number Diff line number Diff line change
@@ -491,15 +491,15 @@ public ConstDefinedNode(ConstDefinedNode prev) {
public boolean isConstDefined(RubyModule module, RubyString name, @SuppressWarnings("unused") UndefinedPlaceholder inherit) {
notDesignedForCompilation();

return ModuleOperations.lookupConstant(null, module, name.toString()) != null;
return ModuleOperations.lookupConstant(getContext(), null, module, name.toString()) != null;
}

@Specialization
public boolean isConstDefined(RubyModule module, RubyString name, boolean inherit) {
notDesignedForCompilation();

if (inherit) {
return ModuleOperations.lookupConstant(null, module, name.toString()) != null;
return ModuleOperations.lookupConstant(getContext(), null, module, name.toString()) != null;
} else {
return module.getConstants().containsKey(name.toString());
}
@@ -509,7 +509,7 @@ public boolean isConstDefined(RubyModule module, RubyString name, boolean inheri
public boolean isConstDefined(RubyModule module, RubySymbol name, @SuppressWarnings("unused") UndefinedPlaceholder inherit) {
notDesignedForCompilation();

return ModuleOperations.lookupConstant(null, module, name.toString()) != null;
return ModuleOperations.lookupConstant(getContext(), null, module, name.toString()) != null;
}

}
Original file line number Diff line number Diff line change
@@ -60,7 +60,18 @@ protected RubyConstant lookupConstant(
String name,
boolean ignoreVisibility,
Dispatch.DispatchAction dispatchAction) {
return ModuleOperations.lookupConstant(lexicalScope, module, name);
RubyConstant constant = ModuleOperations.lookupConstant(getContext(), lexicalScope, module, name);

// If no constant was found, use #const_missing
if (constant == null) {
return null;
}

if (!ignoreVisibility && !constant.isVisibleTo(getContext(), lexicalScope, module)) {
throw new RaiseException(getContext().getCoreLibrary().nameErrorPrivateConstant(module, name, this));
}

return constant;
}

@CompilerDirectives.SlowPath
Original file line number Diff line number Diff line change
@@ -20,16 +20,12 @@
/**
* Define a new class, or get the existing one of the same name.
*/
public class DefineOrGetClassNode extends RubyNode {
public class DefineOrGetClassNode extends DefineOrGetModuleNode {

private final String name;
@Child protected RubyNode lexicalParentModule;
@Child protected RubyNode superClass;

public DefineOrGetClassNode(RubyContext context, SourceSection sourceSection, String name, RubyNode lexicalParentModule, RubyNode superClass) {
super(context, sourceSection);
this.name = name;
this.lexicalParentModule = lexicalParentModule;
super(context, sourceSection, name, lexicalParentModule);
this.superClass = superClass;
}

@@ -39,17 +35,10 @@ public Object execute(VirtualFrame frame) {

final RubyContext context = getContext();

RubyModule lexicalParentModuleObject;

try {
lexicalParentModuleObject = lexicalParentModule.executeRubyModule(frame);
} catch (UnexpectedResultException e) {
throw new RaiseException(context.getCoreLibrary().typeErrorIsNotA(e.getResult().toString(), "module", this));
}

// Look for a current definition of the class, or create a new one

final RubyConstant constant = lexicalParentModuleObject.getConstants().get(name);
RubyModule lexicalParent = getLexicalParentModule(frame);
final RubyConstant constant = lookupForExistingModule(frame, lexicalParent);

RubyClass definingClass;
RubyClass superClassObject = getRubySuperClass(frame, context);
@@ -60,15 +49,14 @@ public Object execute(VirtualFrame frame) {
} else if (superClassObject instanceof RubyString.RubyStringClass) {
definingClass = new RubyString.RubyStringClass(superClassObject);
} else {
definingClass = new RubyClass(this, lexicalParentModuleObject, superClassObject, name);
definingClass = new RubyClass(this, lexicalParent, superClassObject, name);
}

lexicalParentModuleObject.setConstant(this, name, definingClass);
lexicalParent.setConstant(this, name, definingClass);
} else {
if (constant.getValue() instanceof RubyClass) {
definingClass = (RubyClass) constant.getValue();
checkSuperClassCompatibility(context, superClassObject, definingClass);

} else {
throw new RaiseException(context.getCoreLibrary().typeErrorIsNotA(constant.getValue().toString(), "class", this));
}
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
*/
public class DefineOrGetModuleNode extends RubyNode {

private final String name;
protected final String name;
@Child protected RubyNode lexicalParentModule;

public DefineOrGetModuleNode(RubyContext context, SourceSection sourceSection, String name, RubyNode lexicalParentModule) {
@@ -35,35 +35,58 @@ public DefineOrGetModuleNode(RubyContext context, SourceSection sourceSection, S
public Object execute(VirtualFrame frame) {
notDesignedForCompilation();

final RubyContext context = getContext();

// Look for a current definition of the module, or create a new one

RubyModule lexicalParentModuleObject;
RubyModule lexicalParent = getLexicalParentModule(frame);
final RubyConstant constant = lookupForExistingModule(frame, lexicalParent);

RubyModule definingModule;

if (constant == null) {
definingModule = new RubyModule(getContext().getCoreLibrary().getModuleClass(), lexicalParent, name);
lexicalParent.setConstant(this, name, definingModule);
} else {
Object module = constant.getValue();
if (!(module instanceof RubyModule) || !((RubyModule) module).isOnlyAModule()) {
throw new RaiseException(getContext().getCoreLibrary().typeErrorIsNotA(name, "module", this));
}
definingModule = (RubyModule) module;
}

return definingModule;
}

protected RubyModule getLexicalParentModule(VirtualFrame frame) {
RubyModule lexicalParent;

try {
lexicalParentModuleObject = lexicalParentModule.executeRubyModule(frame);
lexicalParent = lexicalParentModule.executeRubyModule(frame);
} catch (UnexpectedResultException e) {
throw new RaiseException(context.getCoreLibrary().typeErrorIsNotA(e.getResult().toString(), "module", this));
throw new RaiseException(getContext().getCoreLibrary().typeErrorIsNotA(e.getResult().toString(), "module", this));
}

final RubyConstant constant = lexicalParentModuleObject.getConstants().get(name);
return lexicalParent;
}

RubyModule definingModule;
protected RubyConstant lookupForExistingModule(VirtualFrame frame, RubyModule lexicalParent) {
RubyConstant constant = lexicalParent.getConstants().get(name);

if (constant == null) {
definingModule = new RubyModule(context.getCoreLibrary().getModuleClass(), lexicalParentModuleObject, name);
lexicalParentModuleObject.setConstant(this, name, definingModule);
} else {
Object constantValue = constant.getValue();
if (!(constantValue instanceof RubyModule) || !((RubyModule) constantValue).isOnlyAModule()) {
throw new RaiseException(context.getCoreLibrary().typeErrorIsNotA(name, "module", this));
final RubyClass objectClass = getContext().getCoreLibrary().getObjectClass();

if (constant == null && lexicalParent == objectClass) {
for (RubyModule included : objectClass.includedModules()) {
constant = included.getConstants().get(name);
if (constant != null) {
break;
}
}
}

definingModule = (RubyModule) constantValue;
if (constant != null && !constant.isVisibleTo(getContext(), LexicalScope.NONE, lexicalParent)) {
throw new RaiseException(getContext().getCoreLibrary().nameErrorPrivateConstant(lexicalParent, name, this));
}

return definingModule;
return constant;
}

}
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@
import org.jruby.truffle.runtime.core.RubyModule;

public class LexicalScope {
public static final LexicalScope NONE = null;

private final LexicalScope parent;
private RubyModule liveModule;

Original file line number Diff line number Diff line change
@@ -63,7 +63,7 @@ public static Map<String, RubyConstant> getAllConstants(RubyModule module) {
* @param module The receiver of the constant lookup.
* Identical to lexicalScope.getLiveModule() if there no qualifier (Constant).
*/
public static RubyConstant lookupConstant(LexicalScope lexicalScope, RubyModule module, String name) {
public static RubyConstant lookupConstant(RubyContext context, LexicalScope lexicalScope, RubyModule module, String name) {
CompilerAsserts.neverPartOfCompilation();

RubyConstant constant;
@@ -76,7 +76,6 @@ public static RubyConstant lookupConstant(LexicalScope lexicalScope, RubyModule
}

// Look in lexical scope
final RubyContext context = module.getContext();
final RubyClass objectClass = context.getCoreLibrary().getObjectClass();

if (lexicalScope != null) {
@@ -105,9 +104,9 @@ public static RubyConstant lookupConstant(LexicalScope lexicalScope, RubyModule
}
}

// Look in Object and its ancestors for modules
// Look in Object and its included modules
if (module.isOnlyAModule()) {
for (RubyModule ancestor : objectClass.ancestors()) {
for (RubyModule ancestor : objectClass.selfAndIncludedModules()) {
constant = ancestor.getConstants().get(name);

if (constant != null) {
42 changes: 41 additions & 1 deletion core/src/main/java/org/jruby/truffle/runtime/RubyConstant.java
Original file line number Diff line number Diff line change
@@ -9,12 +9,17 @@
*/
package org.jruby.truffle.runtime;

import org.jruby.truffle.runtime.core.RubyClass;
import org.jruby.truffle.runtime.core.RubyModule;

public class RubyConstant {

private final RubyModule declaringModule;
private final Object value;
private boolean isPrivate;

public RubyConstant(Object value, boolean isPrivate) {
public RubyConstant(RubyModule declaringModule, Object value, boolean isPrivate) {
this.declaringModule = declaringModule;
this.value = value;
this.isPrivate = isPrivate;
}
@@ -31,4 +36,39 @@ public void setPrivate(boolean isPrivate) {
this.isPrivate = isPrivate;
}

public boolean isVisibleTo(RubyContext context, LexicalScope lexicalScope, RubyModule module) {
if (!isPrivate) {
return true;
}

final LexicalScope topLexicalScope = lexicalScope;

// Look in lexical scope
if (lexicalScope != null) {
while (lexicalScope != context.getRootLexicalScope()) {
if (lexicalScope.getLiveModule() == declaringModule) {
return true;
}
lexicalScope = lexicalScope.getParent();
}
}

// Look in included modules
if (module instanceof RubyClass) {
for (RubyModule included : module.includedModules()) {
if (included == declaringModule) {
return true;
}
}
}

// Look in Object if there is no qualifier (just CONST, neither Mod::CONST nor ::CONST).
if (topLexicalScope != null && topLexicalScope.getLiveModule() == module && // This is a guess, we should have that info from AST
context.getCoreLibrary().getObjectClass() == declaringModule) {
return true;
}

return false;
}

}
Original file line number Diff line number Diff line change
@@ -527,6 +527,11 @@ public RubyException nameErrorUninitializedConstant(String name, Node currentNod
return nameError(String.format("uninitialized constant %s", name), currentNode);
}

public RubyException nameErrorPrivateConstant(RubyModule module, String name, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return nameError(String.format("private constant %s::%s referenced", module.getName(), name), currentNode);
}

public RubyException nameErrorNoMethod(String name, String object, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return nameError(String.format("undefined method `%s' for %s", name, object), currentNode);
Loading