001: package org.jruby.runtime;
002:
003: import org.jruby.parser.BlockStaticScope;
004: import org.jruby.parser.StaticScope;
005: import org.jruby.runtime.builtin.IRubyObject;
006:
007: /**
008: * Represents the the dynamic portion of scoping information. The variableValues are the
009: * values of assigned local or block variables. The staticScope identifies which sort of
010: * scope this is (block or local).
011: *
012: * Properties of Dynamic Scopes:
013: * 1. static and dynamic scopes have the same number of names to values
014: * 2. size of variables (and thus names) is determined during parsing. So those structured do
015: * not need to change
016: *
017: * FIXME: When creating dynamic scopes we sometimes accidentally pass in extra parents. This
018: * is harmless (other than wasting memory), but we should not do that. We can fix this in two
019: * ways:
020: * 1. Fix all callers
021: * 2. Check parent that is passed in and make if new instance is local, then its parent is not local
022: */
023: public class DynamicScope {
024: // Our values holder (name of variables are kept in staticScope)
025: private IRubyObject[] variableValues;
026:
027: // Static scoping information for this scope
028: private StaticScope staticScope;
029:
030: // Captured dyanmic scopes
031: private DynamicScope parent;
032:
033: // A place to store that special hiding space that bindings need to implement things like:
034: // eval("a = 1", binding); eval("p a"). All binding instances must get access to this
035: // hidden shared scope. We store it here. This will be null if no binding has yet
036: // been called.
037: private DynamicScope bindingScope;
038:
039: public DynamicScope(StaticScope staticScope, DynamicScope parent) {
040: this (staticScope);
041: this .parent = parent;
042: }
043:
044: public DynamicScope(StaticScope staticScope) {
045: this .staticScope = staticScope;
046:
047: }
048:
049: private void lazy() {
050: if (variableValues == null) {
051: int size = staticScope.getNumberOfVariables();
052: variableValues = new IRubyObject[size];
053: }
054: }
055:
056: public DynamicScope cloneScope() {
057: return new DynamicScope(staticScope, parent);
058: }
059:
060: /**
061: * Get all variable names captured (visible) by this scope (sans $~ and $_).
062: *
063: * @return a list of variable names
064: */
065: public String[] getAllNamesInScope() {
066: return staticScope.getAllNamesInScope(this );
067: }
068:
069: public IRubyObject[] getValues() {
070: lazy();
071: return variableValues;
072: }
073:
074: /**
075: * Get value from current scope or one of its captured scopes.
076: *
077: * FIXME: block variables are not getting primed to nil so we need to null check those
078: * until we prime them properly. Also add assert back in.
079: *
080: * @param offset zero-indexed value that represents where variable lives
081: * @param depth how many captured scopes down this variable should be set
082: * @return the value here
083: */
084: public IRubyObject getValue(int offset, int depth) {
085: if (depth > 0) {
086: return parent.getValue(offset, depth - 1);
087: }
088: lazy();
089: assert variableValues != null : "No variables in getValue for Off: "
090: + offset + ", Dep: " + depth;
091: assert offset < variableValues.length : "Index to big for getValue Off: "
092: + offset + ", Dep: " + depth + ", O: " + this ;
093: // &foo are not getting set from somewhere...I want the following assert to be true though
094: //assert variableValues[offset] != null : "Getting unassigned: " + staticScope.getVariables()[offset];
095: return variableValues[offset];
096: }
097:
098: /**
099: * Set value in current dynamic scope or one of its captured scopes.
100: *
101: * @param offset zero-indexed value that represents where variable lives
102: * @param value to set
103: * @param depth how many captured scopes down this variable should be set
104: */
105: public void setValue(int offset, IRubyObject value, int depth) {
106: if (depth > 0) {
107: assert parent != null : "If depth > 0, then parent should not ever be null";
108:
109: parent.setValue(offset, value, depth - 1);
110: } else {
111: assert offset < variableValues.length : "Setting " + offset
112: + " to " + value + ", O: " + this ;
113:
114: lazy();
115: variableValues[offset] = value;
116: }
117: }
118:
119: /**
120: * Set all values which represent 'normal' parameters in a call list to this dynamic
121: * scope. Function calls bind to local scopes by assuming that the indexes or the
122: * arg list correspond to that of the local scope (plus 2 since $_ and $~ always take
123: * the first two slots). We pass in a second argument because we sometimes get more
124: * values than we are expecting. The rest get compacted by original caller into
125: * rest args.
126: *
127: * @param values up to size specified to be mapped as ordinary parm values
128: * @param size is the number of values to assign as ordinary parm values
129: */
130: public void setArgValues(IRubyObject[] values, int size) {
131: lazy();
132: System.arraycopy(values, 0, variableValues, 2, size);
133: }
134:
135: public void setBlockArgValues(IRubyObject[] blockArgValues, int size) {
136: lazy();
137: System.arraycopy(blockArgValues, 0, variableValues, 0, size);
138: }
139:
140: /**
141: * Copy variable values back for ZSuper call.
142: */
143: public void getArgValues(IRubyObject[] args, int size) {
144: lazy();
145: if (variableValues != null && args != null
146: && variableValues.length >= (size + 2)) {
147: System.arraycopy(variableValues, 2, args, 0, size);
148: }
149: }
150:
151: /**
152: *
153: * Make a larger dynamic scope if the static scope grew.
154: *
155: * Eval's with bindings require us to possibly change the size of the dynamic scope if
156: * things like 'eval "b = 2", binding' happens.
157: *
158: */
159: public void growIfNeeded() {
160: lazy();
161: int dynamicSize = variableValues == null ? 0
162: : variableValues.length;
163:
164: if (staticScope.getNumberOfVariables() > dynamicSize) {
165: IRubyObject values[] = new IRubyObject[staticScope
166: .getNumberOfVariables()];
167:
168: if (dynamicSize > 0) {
169: System.arraycopy(variableValues, 0, values, 0,
170: dynamicSize);
171: }
172:
173: variableValues = values;
174: }
175: }
176:
177: // FIXME: Depending on profiling we may want to cache information on location and depth of
178: // both $_ and/or $~ since in some situations they may happen a lot. isDefined should be
179: // fairly cheap, but you never know...
180:
181: public void setLastLine(IRubyObject value) {
182: lazy();
183: int location = staticScope.isDefined("$_");
184:
185: setValue(location & 0xffff, value, location >> 16);
186: }
187:
188: public IRubyObject getLastLine() {
189: lazy();
190: int location = staticScope.isDefined("$_");
191:
192: return getValue(location & 0xffff, location >> 16);
193: }
194:
195: public void setBackRef(IRubyObject value) {
196: lazy();
197: int location = staticScope.isDefined("$~");
198:
199: setValue(location & 0xffff, value, location >> 16);
200: }
201:
202: public IRubyObject getBackRef() {
203: lazy();
204: int location = staticScope.isDefined("$~");
205:
206: return getValue(location & 0xffff, location >> 16);
207: }
208:
209: public DynamicScope getBindingScope() {
210: return bindingScope;
211: }
212:
213: public void setBindingScope(DynamicScope bindingScope) {
214: this .bindingScope = bindingScope;
215: }
216:
217: /**
218: * Get next 'captured' scope.
219: *
220: * @return the scope captured by this scope for implementing closures
221: *
222: */
223: public DynamicScope getNextCapturedScope() {
224: return parent;
225: }
226:
227: /**
228: * Get the static scope associated with this DynamicScope.
229: *
230: * @return static complement to this scope
231: */
232: public StaticScope getStaticScope() {
233: return staticScope;
234: }
235:
236: public String toString() {
237: return toString(new StringBuffer(), "");
238: }
239:
240: // Helper function to give a good view of current dynamic scope with captured scopes
241: private String toString(StringBuffer buf, String indent) {
242: lazy();
243: buf
244: .append(indent)
245: .append(
246: "Static Type["
247: + hashCode()
248: + "]: "
249: + (staticScope instanceof BlockStaticScope ? "block"
250: : "local") + " [");
251: int size = staticScope.getNumberOfVariables();
252:
253: if (size != 0) {
254: String names[] = staticScope.getVariables();
255: for (int i = 0; i < size - 1; i++) {
256: buf.append(names[i]).append("=");
257:
258: if (variableValues[i] == null) {
259: buf.append("null");
260: } else {
261: buf.append(variableValues[i]);
262: }
263:
264: buf.append(",");
265: }
266: buf.append(names[size - 1]).append("=");
267:
268: assert variableValues.length == names.length : "V: "
269: + variableValues.length + " != N: " + names.length
270: + " for " + buf;
271:
272: if (variableValues[size - 1] == null) {
273: buf.append("null");
274: } else {
275: buf.append(variableValues[size - 1]);
276: }
277:
278: }
279:
280: buf.append("]");
281: if (parent != null) {
282: buf.append("\n");
283: parent.toString(buf, indent + " ");
284: }
285:
286: return buf.toString();
287: }
288: }
|