View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jexl3.internal;
18  
19  import java.util.Arrays;
20  
21  /**
22   * A call frame, created from a scope, stores the arguments and local variables in a "stack frame" (sic).
23   * @since 3.0
24   */
25  public final class Frame {
26      /** The scope. */
27      private final Scope scope;
28      /** The actual stack frame. */
29      private final Object[] stack;
30      /** Number of curried parameters. */
31      private final int curried;
32  
33      /**
34       * Creates a new frame.
35       * @param s the scope
36       * @param r the stack frame
37       * @param c the number of curried parameters
38       */
39      Frame(final Scope s, final Object[] r, final int c) {
40          scope = s;
41          stack = r;
42          curried = c;
43      }
44  
45      /**
46       * Assign values to this frame.
47       * @param values the values
48       * @return this frame
49       */
50      Frame assign(final Object... values) {
51          if (stack != null) {
52              final int nparm = scope.getArgCount();
53              final Object[] copy = stack.clone();
54              int ncopy = 0;
55              if (values != null && values.length > 0) {
56                  ncopy = Math.min(nparm - curried, Math.min(nparm, values.length));
57                  System.arraycopy(values, 0, copy, curried, ncopy);
58              }
59              // unbound parameters are defined as null
60              Arrays.fill(copy, curried + ncopy, nparm, null);
61              return new Frame(scope, copy, curried + ncopy);
62          }
63          return this;
64      }
65  
66      /**
67       * Gets a value.
68       * @param s the offset in this frame
69       * @return the stacked value
70       */
71      Object get(final int s) {
72          return stack[s];
73      }
74  
75      /**
76       * Gets the scope.
77       * @return this frame scope
78       */
79      public Scope getScope() {
80          return scope;
81      }
82  
83      /**
84       * Gets this script unbound parameters, i.e. parameters not bound through curry().
85       * @return the parameter names
86       */
87      public String[] getUnboundParameters() {
88          return scope.getParameters(curried);
89      }
90  
91      /**
92       * Whether this frame defines a symbol, ie declared it and assigned it a value.
93       * @param s the offset in this frame
94       * @return true if this symbol has been assigned a value, false otherwise
95       */
96      boolean has(final int s) {
97          return s >= 0 && s < stack.length && stack[s] != Scope.UNDECLARED;
98      }
99  
100     /**
101      * Replace any instance of a closure in this stack by its (fuzzy encoded) offset in it.
102      * <p>This is to avoid the cyclic dependency between the closure and its frame stack that
103      * may point back to it that occur with recursive function definitions.</p>
104      * @param closure the owning closure
105      * @return the cleaned-up stack or the stack itself (most of the time)
106      */
107     Object[] nocycleStack(final Closure closure) {
108         Object[] ns = stack;
109         for(int i = 0; i < stack.length; ++i) {
110             if (stack[i] == closure) {
111                 if (ns == stack) {
112                     ns = stack.clone();
113                 }
114                 // fuzz it a little
115                 ns[i] = Closure.class.hashCode() + i;
116             }
117         }
118         return ns;
119     }
120 
121     /**
122      * Sets a value.
123      * @param r the offset in this frame
124      * @param value the value to set in this frame
125      */
126     void set(final int r, final Object value) {
127         stack[r] = value;
128     }
129 
130 }