StaticScope.java

  1. /*
  2.  ***** BEGIN LICENSE BLOCK *****
  3.  * Version: EPL 1.0/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Eclipse Public
  6.  * License Version 1.0 (the "License"); you may not use this file
  7.  * except in compliance with the License. You may obtain a copy of
  8.  * the License at http://www.eclipse.org/legal/epl-v10.html
  9.  *
  10.  * Software distributed under the License is distributed on an "AS
  11.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  12.  * implied. See the License for the specific language governing
  13.  * rights and limitations under the License.
  14.  *
  15.  * Copyright (C) 2006-2007 Thomas E Enebo <enebo@acm.org>
  16.  *
  17.  * Alternatively, the contents of this file may be used under the terms of
  18.  * either of the GNU General Public License Version 2 or later (the "GPL"),
  19.  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  20.  * in which case the provisions of the GPL or the LGPL are applicable instead
  21.  * of those above. If you wish to allow use of your version of this file only
  22.  * under the terms of either the GPL or the LGPL, and not to allow others to
  23.  * use your version of this file under the terms of the EPL, indicate your
  24.  * decision by deleting the provisions above and replace them with the notice
  25.  * and other provisions required by the GPL or the LGPL. If you do not delete
  26.  * the provisions above, a recipient may use your version of this file under
  27.  * the terms of any one of the EPL, the GPL or the LGPL.
  28.  ***** END LICENSE BLOCK *****/
  29. package org.jruby.parser;

  30. import java.io.Serializable;
  31. import java.util.Arrays;

  32. import org.jruby.RubyModule;
  33. import org.jruby.RubyObject;
  34. import org.jruby.ast.AssignableNode;
  35. import org.jruby.ast.DAsgnNode;
  36. import org.jruby.ast.DVarNode;
  37. import org.jruby.ast.LocalAsgnNode;
  38. import org.jruby.ast.LocalVarNode;
  39. import org.jruby.ast.Node;
  40. import org.jruby.ast.VCallNode;
  41. import org.jruby.ir.IRScope;
  42. import org.jruby.ir.IRScopeType;
  43. import org.jruby.lexer.yacc.ISourcePosition;
  44. import org.jruby.runtime.Arity;
  45. import org.jruby.runtime.DynamicScope;
  46. import org.jruby.runtime.builtin.IRubyObject;
  47. import org.jruby.runtime.scope.DummyDynamicScope;

  48. /**
  49.  * StaticScope represents lexical scoping of variables and module/class constants.
  50.  *
  51.  * At a very high level every scopes enclosing scope contains variables in the next outer
  52.  * lexical layer.  The enclosing scopes variables may or may not be reachable depending
  53.  * on the scoping rules for variables (governed by BlockStaticScope and LocalStaticScope).
  54.  *
  55.  * StaticScope also keeps track of current module/class that is in scope.  previousCRefScope
  56.  * will point to the previous scope of the enclosing module/class (cref).
  57.  *
  58.  */
  59. public class StaticScope implements Serializable {
  60.     private static final long serialVersionUID = 3423852552352498148L;

  61.     // Next immediate scope.  Variable and constant scoping rules make use of this variable
  62.     // in different ways.
  63.     final protected StaticScope enclosingScope;

  64.     // Live reference to module
  65.     private transient RubyModule cref = null;

  66.     // Next CRef down the lexical structure
  67.     private StaticScope previousCRefScope = null;

  68.     // Our name holder (offsets are assigned as variables are added
  69.     private String[] variableNames;

  70.     // number of variables in this scope representing required arguments
  71.     private int requiredArgs = 0;

  72.     // number of variables in this scope representing optional arguments
  73.     private int optionalArgs = 0;

  74.     // index of variable that represents a "rest" arg
  75.     private int restArg = -1;

  76.     private DynamicScope dummyScope;

  77.     protected IRScopeType scopeType;

  78.     private static final String[] NO_NAMES = new String[0];

  79.     private Type type;
  80.     private boolean isBlockOrEval;
  81.     private boolean isArgumentScope; // Is this block and argument scope of a define_method (for the purposes of zsuper).

  82.     private int scopeId;
  83.     private IRScope irScope; // Method/Closure that this static scope corresponds to

  84.     public enum Type {
  85.         LOCAL, BLOCK, EVAL;

  86.         public static Type fromOrdinal(int value) {
  87.             return value < 0 || value >= values().length ? null : values()[value];
  88.         }
  89.     }

  90.     /**
  91.      * Construct a new static scope.
  92.      *
  93.      * @param type           the type of scope
  94.      * @param enclosingScope the lexically containing scope.
  95.      */
  96.     protected StaticScope(Type type, StaticScope enclosingScope) {
  97.         this(type, enclosingScope, NO_NAMES);
  98.     }

  99.     /**
  100.      * Construct a new static scope. The array of strings should all be the
  101.      * interned versions, since several other optimizations depend on being
  102.      * able to do object equality checks.
  103.      *
  104.      * @param type           the type of scope
  105.      * @param enclosingScope the lexically containing scope.
  106.      * @param names          The list of interned String variable names.
  107.      */
  108.     protected StaticScope(Type type, StaticScope enclosingScope, String[] names) {
  109.         assert names != null : "names is not null";
  110.         assert namesAreInterned(names);

  111.         this.enclosingScope = enclosingScope;
  112.         this.variableNames = names;
  113.         this.type = type;
  114.         this.irScope = null;
  115.         this.isBlockOrEval = (type != Type.LOCAL);
  116.         this.isArgumentScope = !isBlockOrEval;
  117.     }

  118.     public IRScope getIRScope() {
  119.         return irScope;
  120.     }

  121.     public int getScopeId() {
  122.         return scopeId;
  123.     }

  124.     public IRScopeType getScopeType() {
  125.         return scopeType;
  126.     }

  127.     public void setScopeType(IRScopeType scopeType) {
  128.         this.scopeType = scopeType;
  129.     }

  130.     public void setIRScope(IRScope irScope, boolean isForLoopBody) {
  131.         if (!isForLoopBody) {
  132.             this.irScope = irScope;
  133.         }
  134.         this.scopeId = irScope.getScopeId();
  135.         this.scopeType = irScope.getScopeType();
  136.     }

  137.     public void setIRScope(IRScope irScope) {
  138.         setIRScope(irScope, false);
  139.     }

  140.     /**
  141.      * Check that all strings in the given array are the interned versions.
  142.      *
  143.      * @param names The array of strings
  144.      * @return true if they are all interned, false otherwise
  145.      */
  146.     private static boolean namesAreInterned(String[] names) {
  147.         for (String name : names) {
  148.             // Note that this object equality check is intentional, to ensure
  149.             // the string and its interned version are the same object.
  150.             if (name != name.intern()) return false;
  151.         }
  152.         return true;
  153.     }

  154.     /**
  155.      * Add a new variable to this (current) scope unless it is already defined in the
  156.      * current scope.
  157.      *
  158.      * @param name of new variable
  159.      * @return index+depth merged location of scope
  160.      */
  161.     public int addVariableThisScope(String name) {
  162.         // Ignore duplicate "_" args in blocks
  163.         // (duplicate _ args are named "_$0")
  164.         // Dont allocate slots for them.
  165.         if (name.equals("_$0")) {
  166.             return -1;
  167.         }

  168.         int slot = exists(name);

  169.         if (slot >= 0) return slot;

  170.         // This is perhaps innefficient timewise?  Optimal spacewise
  171.         growVariableNames(name);

  172.         // Returns slot of variable
  173.         return variableNames.length - 1;
  174.     }

  175.     /**
  176.      * Add a new variable to this (current) scope unless it is already defined in any
  177.      * reachable scope.
  178.      *
  179.      * @param name of new variable
  180.      * @return index+depth merged location of scope
  181.      */
  182.     public int addVariable(String name) {
  183.         int slot = isDefined(name);

  184.         if (slot >= 0) return slot;

  185.         // This is perhaps innefficient timewise?  Optimal spacewise
  186.         growVariableNames(name);

  187.         // Returns slot of variable
  188.         return variableNames.length - 1;
  189.     }

  190.     public String[] getVariables() {
  191.         return variableNames;
  192.     }

  193.     public int getNumberOfVariables() {
  194.         return irScope == null ? variableNames.length : irScope.getUsedVariablesCount();
  195.     }

  196.     public void setVariables(String[] names) {
  197.         assert names != null : "names is not null";

  198.         variableNames = new String[names.length];
  199.         System.arraycopy(names, 0, variableNames, 0, names.length);
  200.     }

  201.     /* Note: Only used by compiler until it can use getConstant again or use some other refactoring */
  202.     public IRubyObject getConstantWithConstMissing(String internedName) {
  203.         IRubyObject result = getConstantInner(internedName);

  204.         // If we could not find the constant from cref..then try getting from inheritence hierarchy
  205.         return result == null ? cref.getConstant(internedName) : result;
  206.     }

  207.     public boolean isConstantDefined(String internedName) {
  208.         return getConstant(internedName) != null;
  209.     }

  210.     public IRubyObject getConstant(String internedName) {
  211.         IRubyObject result = getConstantInner(internedName);

  212.         // If we could not find the constant from cref..then try getting from inheritence hierarchy
  213.         return result == null ? cref.getConstantNoConstMissing(internedName) : result;
  214.     }

  215.     public IRubyObject getConstantInner(String internedName) {
  216.         IRubyObject result = cref.fetchConstant(internedName);

  217.         if (result != null) {
  218.             return result == RubyObject.UNDEF ? cref.resolveUndefConstant(internedName) : result;
  219.         }

  220.         return previousCRefScope == null ? null : previousCRefScope.getConstantInnerNoObject(internedName);
  221.     }

  222.     private IRubyObject getConstantInnerNoObject(String internedName) {
  223.         if (previousCRefScope == null) return null;

  224.         return getConstantInner(internedName);
  225.     }

  226.     public IRubyObject setConstant(String internedName, IRubyObject result) {
  227.         RubyModule module;

  228.         if ((module = getModule()) != null) {
  229.             module.setConstant(internedName, result);
  230.             return result;
  231.         }

  232.         // TODO: wire into new exception handling mechanism
  233.         throw result.getRuntime().newTypeError("no class/module to define constant");
  234.     }

  235.     /**
  236.      * Next outer most scope in list of scopes.  An enclosing scope may have no direct scoping
  237.      * relationship to its child.  If I am in a localScope and then I enter something which
  238.      * creates another localScope the enclosing scope will be the first scope, but there are
  239.      * no valid scoping relationships between the two.  Methods which walk the enclosing scopes
  240.      * are responsible for enforcing appropriate scoping relationships.
  241.      *
  242.      * @return the parent scope
  243.      */
  244.     public StaticScope getEnclosingScope() {
  245.         return enclosingScope;
  246.     }

  247.     /**
  248.      * Does the variable exist?
  249.      *
  250.      * @param name of the variable to find
  251.      * @return index of variable or -1 if it does not exist
  252.      */
  253.     public int exists(String name) {
  254.         return findVariableName(name);
  255.     }

  256.     private int findVariableName(String name) {
  257.         for (int i = 0; i < variableNames.length; i++) {
  258.             if (name == variableNames[i]) return i;
  259.         }
  260.         return -1;
  261.     }

  262.     /**
  263.      * Is this name in the visible to the current scope
  264.      *
  265.      * @param name to be looked for
  266.      * @return a location where the left-most 16 bits of number of scopes down it is and the
  267.      * right-most 16 bits represents its index in that scope
  268.      */
  269.     public int isDefined(String name) {
  270.         return isDefined(name, 0);
  271.     }

  272.     /**
  273.      * Make a DASgn or LocalAsgn node based on scope logic
  274.      *
  275.      * @param position
  276.      * @param name
  277.      * @param value
  278.      * @return
  279.      */
  280.     public AssignableNode assign(ISourcePosition position, String name, Node value) {
  281.         return assign(position, name, value, this, 0);
  282.     }

  283.     /**
  284.      * Get all visible variables that we can see from this scope that have been assigned
  285.      * (e.g. seen so far)
  286.      *
  287.      * @return a list of all names (sans $~ and $_ which are special names)
  288.      */
  289.     public String[] getAllNamesInScope() {
  290.         String[] names = getVariables();
  291.         if (isBlockOrEval) {
  292.             String[] ourVariables = names;
  293.             String[] variables = enclosingScope.getAllNamesInScope();

  294.             // we know variables cannot be null since this IRStaticScope always returns a non-null array
  295.             names = new String[variables.length + ourVariables.length];

  296.             System.arraycopy(variables, 0, names, 0, variables.length);
  297.             System.arraycopy(ourVariables, 0, names, variables.length, ourVariables.length);
  298.         }

  299.         return names;
  300.     }

  301.     public int isDefined(String name, int depth) {
  302.         if (isBlockOrEval) {
  303.             int slot = exists(name);
  304.             if (slot >= 0) return (depth << 16) | slot;

  305.             return enclosingScope.isDefined(name, depth + 1);
  306.         } else {
  307.             return (depth << 16) | exists(name);
  308.         }
  309.     }

  310.     public AssignableNode addAssign(ISourcePosition position, String name, Node value) {
  311.         int slot = addVariable(name);
  312.         // No bit math to store level since we know level is zero for this case
  313.         return new DAsgnNode(position, name, slot, value);
  314.     }

  315.     public AssignableNode assign(ISourcePosition position, String name, Node value,
  316.                                  StaticScope topScope, int depth) {
  317.         int slot = exists(name);

  318.         // We can assign if we already have variable of that name here or we are the only
  319.         // scope in the chain (which Local scopes always are).
  320.         if (slot >= 0) {
  321.             return isBlockOrEval ? new DAsgnNode(position, name, ((depth << 16) | slot), value)
  322.                     : new LocalAsgnNode(position, name, ((depth << 16) | slot), value);
  323.         } else if (!isBlockOrEval && (topScope == this)) {
  324.             slot = addVariable(name);

  325.             return new LocalAsgnNode(position, name, slot, value);
  326.         }

  327.         // If we are not a block-scope and we go there, we know that 'topScope' is a block scope
  328.         // because a local scope cannot be within a local scope
  329.         // If topScope was itself it would have created a LocalAsgnNode above.
  330.         return isBlockOrEval ? enclosingScope.assign(position, name, value, topScope, depth + 1)
  331.                 : topScope.addAssign(position, name, value);
  332.     }

  333.     public Node declare(ISourcePosition position, String name, int depth) {
  334.         int slot = exists(name);

  335.         if (slot >= 0) {
  336.             return isBlockOrEval ? new DVarNode(position, ((depth << 16) | slot), name) : new LocalVarNode(position, ((depth << 16) | slot), name);
  337.         }

  338.         return isBlockOrEval ? enclosingScope.declare(position, name, depth + 1) : new VCallNode(position, name);
  339.     }

  340.     /**
  341.      * Make a DVar or LocalVar node based on scoping logic
  342.      *
  343.      * @param position the location that in the source that the new node will come from
  344.      * @param name     of the variable to be created is named
  345.      * @return a DVarNode or LocalVarNode
  346.      */
  347.     public Node declare(ISourcePosition position, String name) {
  348.         return declare(position, name, 0);
  349.     }

  350.     /**
  351.      * Gets the Local Scope relative to the current Scope.  For LocalScopes this will be itself.
  352.      * Blocks will contain the LocalScope it contains.
  353.      *
  354.      * @return localScope
  355.      */

  356.     public StaticScope getLocalScope() {
  357.         return (type != Type.BLOCK) ? this : enclosingScope.getLocalScope();
  358.     }

  359.     /**
  360.      * Get the live CRef module associated with this scope.
  361.      *
  362.      * @return the live module
  363.      */
  364.     public RubyModule getModule() {
  365.         return cref;
  366.     }

  367.     public StaticScope getPreviousCRefScope() {
  368.         return previousCRefScope;
  369.     }

  370.     public void setPreviousCRefScope(StaticScope crefScope) {
  371.         this.previousCRefScope = crefScope;
  372.     }

  373.     public void setModule(RubyModule module) {
  374.         this.cref = module;

  375.         for (StaticScope scope = getEnclosingScope(); scope != null; scope = scope.getEnclosingScope()) {
  376.             if (scope.cref != null) {
  377.                 previousCRefScope = scope;
  378.                 return;
  379.             }
  380.         }
  381.     }

  382.     /**
  383.      * Update current scoping structure to populate with proper cref scoping values.  This should
  384.      * be called at any point when you reference a scope for the first time.  For the interpreter
  385.      * this is done in a small number of places (defnNode, defsNode, and getBlock).  The compiler
  386.      * does this in the same places.
  387.      *
  388.      * @return the current cref, though this is largely an implementation detail
  389.      */
  390.     public RubyModule determineModule() {
  391.         if (cref == null) {
  392.             cref = getEnclosingScope().determineModule();

  393.             assert cref != null : "CRef is always created before determine happens";

  394.             previousCRefScope = getEnclosingScope().previousCRefScope;
  395.         }

  396.         return cref;
  397.     }

  398.     public int getOptionalArgs() {
  399.         return optionalArgs;
  400.     }

  401.     public int getRequiredArgs() {
  402.         return requiredArgs;
  403.     }

  404.     public void setRequiredArgs(int requiredArgs) {
  405.         this.requiredArgs = requiredArgs;
  406.     }

  407.     public int getRestArg() {
  408.         return restArg;
  409.     }

  410.     public void setRestArg(int restArg) {
  411.         this.restArg = restArg;
  412.     }

  413.     public boolean isBlockScope() {
  414.         return isBlockOrEval;
  415.     }

  416.     /**
  417.      * Argument scopes represent scopes which contain arguments for zsuper.  All LocalStaticScopes
  418.      * are argument scopes and BlockStaticScopes can be when they are used by define_method.
  419.      */
  420.     public boolean isArgumentScope() {
  421.         return isArgumentScope;
  422.     }

  423.     public void makeArgumentScope() {
  424.         this.isArgumentScope = true;
  425.     }

  426.     public Arity getArity() {
  427.         if (optionalArgs > 0) {
  428.             if (restArg >= 0) {
  429.                 return Arity.optional();
  430.             }
  431.             return Arity.required(requiredArgs);
  432.         } else {
  433.             if (restArg >= 0) {
  434.                 return Arity.optional();
  435.             }
  436.             return Arity.fixed(requiredArgs);
  437.         }
  438.     }

  439.     public void setArities(int required, int optional, int rest) {
  440.         this.requiredArgs = required;
  441.         this.optionalArgs = optional;
  442.         this.restArg = rest;
  443.     }

  444.     public DynamicScope getDummyScope() {
  445.         return dummyScope == null ? dummyScope = new DummyDynamicScope(this) : dummyScope;
  446.     }

  447.     private void growVariableNames(String name) {
  448.         String[] newVariableNames = new String[variableNames.length + 1];
  449.         System.arraycopy(variableNames, 0, newVariableNames, 0, variableNames.length);
  450.         variableNames = newVariableNames;
  451.         variableNames[variableNames.length - 1] = name;
  452.     }

  453.     @Override
  454.     public String toString() {
  455.         // FIXME: Do we need to persist cref as well?
  456.         return "StaticScope(" + type + "):" + Arrays.toString(variableNames);
  457.     }

  458.     public Type getType() {
  459.         return type;
  460.     }

  461.     public StaticScope duplicate() {
  462.         StaticScope dupe = new StaticScope(type, enclosingScope, variableNames == null ? NO_NAMES : variableNames);
  463.         // irScope is not guaranteed to be set onto StaticScope until it is executed for the first time.
  464.         // We can call duplicate before its first execution.
  465.         if (irScope != null) dupe.setIRScope(irScope);
  466.         dupe.setScopeType(scopeType);
  467.         dupe.setPreviousCRefScope(previousCRefScope);
  468.         dupe.setModule(cref);

  469.         return dupe;
  470.     }
  471. }