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    *      https://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  import java.util.concurrent.atomic.AtomicReference;
21  
22  /**
23   * A call frame, created from a scope, stores the arguments and local variables in a "stack frame" (sic).
24   *
25   * @since 3.0
26   */
27  public class Frame {
28  
29      /** The scope. */
30      private final Scope scope;
31  
32      /** The actual stack frame. */
33      protected final Object[] stack;
34  
35      /** Number of curried parameters. */
36      private final int curried;
37  
38      /**
39       * Creates a new frame.
40       *
41       * @param s the scope
42       * @param r the stack frame
43       * @param c the number of curried parameters
44       */
45      protected Frame(final Scope s, final Object[] r, final int c) {
46          scope = s;
47          stack = r;
48          curried = c;
49          final String[] symbols = scope.getSymbols();
50          if (symbols.length != r.length) {
51              throw new IllegalArgumentException("Scope and stack frame size mismatch: "
52                      + symbols.length + " != " + r.length);
53          }
54      }
55  
56      /**
57       * Assign values to this frame.
58       *
59       * @param values the values
60       * @return this frame
61       */
62      Frame assign(final Object... values) {
63          if (stack != null) {
64              final int nparm = scope.getArgCount();
65              final Object[] copy = stack.clone();
66              int ncopy = 0;
67              if (values != null && values.length > 0) {
68                  ncopy = Math.min(nparm - curried, Math.min(nparm, values.length));
69                  System.arraycopy(values, 0, copy, curried, ncopy);
70              }
71              // unbound parameters are defined as null
72              Arrays.fill(copy, curried + ncopy, nparm, null);
73              return newFrame(scope, copy, curried + ncopy);
74          }
75          return this;
76      }
77  
78      /**
79       * Creates a new from of this frame"s class.
80       *
81       * @param s the scope
82       * @param r the arguments
83       * @param c the number of curried parameters
84       * @return a new instance of frame
85       */
86      Frame newFrame(final Scope s, final Object[] r, final int c) {
87          return new Frame(s, r, c);
88      }
89  
90      /**
91       * Captures a value.
92       *
93       * @param s the offset in this frame
94       * @param lexical true if this captured symbol is redefined locally
95       * @return the stacked value
96       */
97      Object capture(final int s, final boolean lexical) {
98          return stack[s];
99      }
100 
101     /**
102      * Gets a value.
103      *
104      * @param s the offset in this frame
105      * @return the stacked value
106      */
107     Object get(final int s) {
108         return stack[s];
109     }
110 
111     /**
112      * Sets a value.
113      *
114      * @param r the offset in this frame
115      * @param value the value to set in this frame
116      */
117     void set(final int r, final Object value) {
118         stack[r] = value;
119     }
120 
121     /**
122      * Gets the scope.
123      *
124      * @return this frame scope
125      */
126     public Scope getScope() {
127         return scope;
128     }
129 
130     /**
131      * Gets this script unbound parameters, i.e. parameters not bound through curry().
132      *
133      * @return the parameter names
134      */
135     public String[] getUnboundParameters() {
136         return scope.getParameters(curried);
137     }
138 
139     /**
140      * Tests whether this frame defines a symbol, ie declared it and assigned it a value.
141      *
142      * @param s the offset in this frame
143      * @return true if this symbol has been assigned a value, false otherwise
144      */
145     boolean has(final int s) {
146         return s >= 0 && s < stack.length && stack[s] != Scope.UNDECLARED;
147     }
148 
149     /**
150      * Replace any instance of a closure in this stack by its (fuzzy encoded) offset in it.
151      * <p>This is to avoid the cyclic dependency between the closure and its frame stack that
152      * may point back to it that occur with recursive function definitions.</p>
153      *
154      * @param closure the owning closure
155      * @return the cleaned-up stack or the stack itself (most of the time)
156      */
157     Object[] nocycleStack(final Closure closure) {
158         Object[] ns = stack;
159         for(int i = 0; i < stack.length; ++i) {
160             if (stack[i] == closure) {
161                 if (ns == stack) {
162                     ns = stack.clone();
163                 }
164                 // fuzz it a little
165                 ns[i] = Closure.class.hashCode() + i;
166             }
167         }
168         return ns;
169     }
170 }
171 
172 /**
173  * Pass-by-reference frame.
174  */
175 class ReferenceFrame extends Frame {
176     ReferenceFrame(final Scope s, final Object[] r, final int c) {
177         super(s, r, c);
178     }
179 
180     @Override
181     Frame newFrame(final Scope s, final Object[] r, final int c) {
182         return new ReferenceFrame(s, r, c);
183     }
184 
185     @Override
186     Object capture(final int s, final boolean lexical) {
187         final Object o = stack[s];
188         if (o instanceof CaptureReference) {
189             final CaptureReference ref = (CaptureReference) o;
190             // if the captured symbol is lexical, it is redefined locally; a new reference is needed to share it with callees
191             // otherwise share the reference
192             return lexical ? (stack[s] = new CaptureReference(ref.get())) : ref;
193         }
194         // capture the register, wrap the value in a reference to share it with callees
195         return stack[s] = new CaptureReference(o);
196     }
197 
198     @Override
199     Object get(final int s) {
200         final Object o = stack[s];
201         return o instanceof CaptureReference ? ((CaptureReference) o).get() : o;
202     }
203 
204     @Override
205     void set(final int r, final Object value) {
206         final Object o = stack[r];
207         if (o instanceof CaptureReference) {
208             if (value != Scope.UNDEFINED && value != Scope.UNDECLARED) {
209                 ((CaptureReference) o).set(value);
210             }
211         } else {
212             stack[r] = value;
213         }
214     }
215 }
216 
217 /**
218  * Captured variable reference.
219  */
220 class CaptureReference extends AtomicReference<Object> {
221     CaptureReference(final Object o) {
222         super(o);
223     }
224 }