Mercurial > hg > graal-compiler
view graal/com.oracle.truffle.ruby.runtime/src/com/oracle/truffle/ruby/runtime/core/RubyModule.java @ 13514:0fbee3eb71f0
Ruby: import project.
author | Chris Seaton <chris.seaton@oracle.com> |
---|---|
date | Mon, 06 Jan 2014 17:12:09 +0000 |
parents | |
children |
line wrap: on
line source
/* * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved. This * code is released under a tri EPL/GPL/LGPL license. You can use it, * redistribute it and/or modify it under the terms of the: * * Eclipse Public License version 1.0 * GNU General Public License version 2 * GNU Lesser General Public License version 2.1 */ package com.oracle.truffle.ruby.runtime.core; import java.util.*; import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.ruby.runtime.*; import com.oracle.truffle.ruby.runtime.lookup.*; import com.oracle.truffle.ruby.runtime.methods.*; import com.oracle.truffle.ruby.runtime.objects.*; /** * Represents the Ruby {@code Module} class. */ public class RubyModule extends RubyObject implements LookupNode { /** * The class from which we create the object that is {@code Module}. A subclass of * {@link RubyClass} so that we can override {@link #newInstance} and allocate a * {@link RubyModule} rather than a normal {@link RubyBasicObject}. */ public static class RubyModuleClass extends RubyClass { public RubyModuleClass(RubyContext context) { super(context, null, null, null, "Module"); } @Override public RubyBasicObject newInstance() { return new RubyModule(this, null, "(unnamed module)"); } } /** * The slot within a module definition method frame where we store the implicit state that is * the current visibility for new methods. */ public static final Object VISIBILITY_FRAME_SLOT_ID = new Object(); /** * The slot within a module definition method frame where we store the implicit state that is * the flag for whether or not new methods will be module methods (functions is the term). */ public static final Object MODULE_FUNCTION_FLAG_FRAME_SLOT_ID = new Object(); // The context is stored here - objects can obtain it via their class (which is a module) private final RubyContext context; /* * The module in which this module was defined. By analogy, if superclass is the dynamic scope, * the parent module is the lexical scope. */ private final RubyModule parentModule; /* * The first thing to lookup names in. Not always the class, as we also have singleton classes, * included modules etc. */ private LookupNode lookupParent = LookupTerminal.INSTANCE; private final String name; private final Map<String, RubyMethod> methods = new HashMap<>(); private final Map<String, Object> constants = new HashMap<>(); private final Map<String, Object> classVariables = new HashMap<>(); private final CyclicAssumption unmodifiedAssumption; /** * Keep track of other modules that depend on the configuration of this module in some way. The * include subclasses and modules that include this module. */ private final Set<RubyModule> dependents = Collections.newSetFromMap(new WeakHashMap<RubyModule, Boolean>()); public RubyModule(RubyClass rubyClass, RubyModule parentModule, String name) { this(rubyClass.getContext(), rubyClass, parentModule, name); } public RubyModule(RubyContext context, RubyClass rubyClass, RubyModule parentModule, String name) { super(rubyClass); this.context = context; this.parentModule = parentModule; this.name = name; unmodifiedAssumption = new CyclicAssumption(name + " is unmodified"); /* * Modules always go into the object space manager. Manually allocate an objectID, because * the lazy mechanism uses the Ruby class of the object, which may not be set yet during * bootstrap. */ objectID = context.getNextObjectID(); context.getObjectSpaceManager().add(this); } public RubyModule getParentModule() { return parentModule; } public void include(RubyModule module) { checkFrozen(); lookupParent = new LookupFork(module, lookupParent); newVersion(); module.addDependent(this); } /** * Set the value of a constant, possibly redefining it. */ public void setConstant(String constantName, Object value) { assert RubyContext.shouldObjectBeVisible(value); checkFrozen(); getConstants().put(constantName, value); newVersion(); // TODO(CS): warn when redefining a constant } public void setClassVariable(String variableName, Object value) { assert RubyContext.shouldObjectBeVisible(value); checkFrozen(); if (!setClassVariableIfAlreadySet(variableName, value)) { classVariables.put(variableName, value); } } public boolean setClassVariableIfAlreadySet(String variableName, Object value) { assert RubyContext.shouldObjectBeVisible(value); checkFrozen(); if (lookupParent.setClassVariableIfAlreadySet(variableName, value)) { return true; } if (classVariables.containsKey(variableName)) { classVariables.put(variableName, value); return true; } return false; } public void removeClassVariable(String variableName) { checkFrozen(); classVariables.remove(variableName); } public void setModuleConstant(String constantName, Object value) { checkFrozen(); setConstant(constantName, value); getSingletonClass().setConstant(constantName, value); } public void addMethod(RubyMethod method) { checkFrozen(); getMethods().put(method.getName(), method); newVersion(); } /** * Remove a method from this module. */ public void removeMethod(String methodName) { checkFrozen(); getMethods().remove(methodName); newVersion(); } public void undefMethod(String methodName) { undefMethod(lookupMethod(methodName)); } public void undefMethod(RubyMethod method) { addMethod(method.undefined()); } /** * Alias a method. */ public void alias(String newName, String oldName) { final RubyMethod method = lookupMethod(oldName); if (method == null) { CompilerDirectives.transferToInterpreter(); throw new RuntimeException("Couldn't alias as coudln't find " + oldName); } addMethod(method.withNewName(newName)); } @Override public Object lookupConstant(String constantName) { Object value; // Look in this module value = getConstants().get(constantName); if (value != null) { return value; } // Look in the parent module if (parentModule != null) { value = parentModule.lookupConstant(constantName); if (value != null) { return value; } } // Look in the lookup parent return lookupParent.lookupConstant(constantName); } @Override public Object lookupClassVariable(String variableName) { // Look in this module final Object value = classVariables.get(variableName); if (value != null) { return value; } // Look in the parent return lookupParent.lookupClassVariable(variableName); } public Set<String> getClassVariables() { final Set<String> classVariablesSet = new HashSet<>(); classVariablesSet.addAll(classVariables.keySet()); classVariablesSet.addAll(lookupParent.getClassVariables()); return classVariablesSet; } @Override public RubyMethod lookupMethod(String methodName) { // Look in this module final RubyMethod method = getMethods().get(methodName); if (method != null) { return method; } // Look in the parent return lookupParent.lookupMethod(methodName); } public void appendFeatures(RubyModule other) { // TODO(CS): check only run once for (Map.Entry<String, Object> constantEntry : getConstants().entrySet()) { final String constantName = constantEntry.getKey(); final Object constantValue = constantEntry.getValue(); other.setModuleConstant(constantName, constantValue); } for (Map.Entry<String, RubyMethod> methodEntry : getMethods().entrySet()) { final String methodName = methodEntry.getKey(); final RubyMethod method = methodEntry.getValue(); other.addMethod(method.withNewName(methodName)); } } public RubyContext getContext() { return context; } public String getName() { return name; } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return name; } public void newVersion() { unmodifiedAssumption.invalidate(); // Make dependents new versions for (RubyModule dependent : dependents) { dependent.newVersion(); } } public void addDependent(RubyModule dependent) { dependents.add(dependent); } public Assumption getUnmodifiedAssumption() { return unmodifiedAssumption.getAssumption(); } public void getMethods(Map<String, RubyMethod> foundMethods) { lookupParent.getMethods(foundMethods); for (RubyMethod method : methods.values()) { foundMethods.put(method.getName(), method); } } public static void setCurrentVisibility(Frame frame, Visibility visibility) { final FrameSlot slot = frame.getFrameDescriptor().findFrameSlot(VISIBILITY_FRAME_SLOT_ID); frame.setObject(slot, visibility); } public void visibilityMethod(PackedFrame frame, Object[] arguments, Visibility visibility) { if (arguments.length == 0) { setCurrentVisibility(frame.unpack(), visibility); } else { for (Object arg : arguments) { final RubyMethod method = lookupMethod(arg.toString()); if (method == null) { throw new RuntimeException("Couldn't find method " + arg.toString()); } /* * If the method was already defined in this class, that's fine {@link addMethod} * will overwrite it, otherwise we do actually want to add a copy of the method with * a different visibility to this module. */ addMethod(method.withNewVisibility(visibility)); } } } public List<RubyMethod> getDeclaredMethods() { return new ArrayList<>(getMethods().values()); } public void moduleEval(String source) { getRubyClass().getContext().eval(source, this); } public Map<String, Object> getConstants() { return constants; } public Map<String, RubyMethod> getMethods() { return methods; } }