1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.xpath.internal;
  22 
  23 import javax.xml.transform.TransformerException;
  24 
  25 import com.sun.org.apache.xalan.internal.res.XSLMessages;
  26 import com.sun.org.apache.xpath.internal.objects.XObject;
  27 import com.sun.org.apache.xpath.internal.res.XPATHErrorResources;
  28 
  29 /**
  30  * Defines a class to keep track of a stack for
  31  * template arguments and variables.
  32  *
  33  * <p>This has been changed from the previous incarnations of this
  34  * class to be fairly low level.</p>
  35  * @xsl.usage internal
  36  * @LastModified: Nov 2017
  37  */
  38 public class VariableStack implements Cloneable
  39 {
  40   /**
  41    * limitation for 1K
  42    */
  43   public static final int CLEARLIMITATION= 1024;
  44 
  45   /**
  46    * Constructor for a variable stack.
  47    */
  48   public VariableStack()
  49   {
  50     reset();
  51   }
  52 
  53   /**
  54    * Returns a clone of this variable stack.
  55    *
  56    * @return  a clone of this variable stack.
  57    *
  58    * @throws CloneNotSupportedException
  59    */
  60   public synchronized Object clone() throws CloneNotSupportedException
  61   {
  62 
  63     VariableStack vs = (VariableStack) super.clone();
  64 
  65     // I *think* I can get away with a shallow clone here?
  66     vs._stackFrames = _stackFrames.clone();
  67     vs._links = _links.clone();
  68 
  69     return vs;
  70   }
  71 
  72   /**
  73    * The stack frame where all variables and params will be kept.
  74    * @serial
  75    */
  76   XObject[] _stackFrames = new XObject[XPathContext.RECURSIONLIMIT * 2];
  77 
  78   /**
  79    * The top of the stack frame (<code>_stackFrames</code>).
  80    * @serial
  81    */
  82   int _frameTop;
  83 
  84   /**
  85    * The bottom index of the current frame (relative to <code>_stackFrames</code>).
  86    * @serial
  87    */
  88   private int _currentFrameBottom;
  89 
  90   /**
  91    * The stack of frame positions.  I call 'em links because of distant
  92    * <a href="http://math.millikin.edu/mprogers/Courses/currentCourses/CS481-ComputerArchitecture/cs481.Motorola68000.html">
  93    * Motorola 68000 assembler</a> memories.  :-)
  94    * @serial
  95    */
  96   int[] _links = new int[XPathContext.RECURSIONLIMIT];
  97 
  98   /**
  99    * The top of the links stack.
 100    */
 101   int _linksTop;
 102 
 103   /**
 104    * Get the element at the given index, regardless of stackframe.
 105    *
 106    * @param i index from zero.
 107    *
 108    * @return The item at the given index.
 109    */
 110   public XObject elementAt(final int i)
 111   {
 112     return _stackFrames[i];
 113   }
 114 
 115   /**
 116    * Get size of the stack.
 117    *
 118    * @return the total size of the execution stack.
 119    */
 120   public int size()
 121   {
 122     return _frameTop;
 123   }
 124 
 125   /**
 126    * Reset the stack to a start position.
 127    *
 128    * @return the total size of the execution stack.
 129    */
 130   public void reset()
 131   {
 132 
 133     _frameTop = 0;
 134     _linksTop = 0;
 135 
 136     // Adding one here to the stack of frame positions will allow us always
 137     // to look one under without having to check if we're at zero.
 138     // (As long as the caller doesn't screw up link/unlink.)
 139     _links[_linksTop++] = 0;
 140     _stackFrames = new XObject[_stackFrames.length];
 141   }
 142 
 143   /**
 144    * Set the current stack frame.
 145    *
 146    * @param sf The new stack frame position.
 147    */
 148   public void setStackFrame(int sf)
 149   {
 150     _currentFrameBottom = sf;
 151   }
 152 
 153   /**
 154    * Get the position from where the search should start,
 155    * which is either the searchStart property, or the top
 156    * of the stack if that value is -1.
 157    *
 158    * @return The current stack frame position.
 159    */
 160   public int getStackFrame()
 161   {
 162     return _currentFrameBottom;
 163   }
 164 
 165   /**
 166    * Allocates memory (called a stackframe) on the stack; used to store
 167    * local variables and parameter arguments.
 168    *
 169    * <p>I use the link/unlink concept because of distant
 170    * <a href="http://math.millikin.edu/mprogers/Courses/currentCourses/CS481-ComputerArchitecture/cs481.Motorola68000.html">
 171    * Motorola 68000 assembler</a> memories.</p>
 172    *
 173    * @param size The size of the stack frame allocation.  This ammount should
 174    * normally be the maximum number of variables that you can have allocated
 175    * at one time in the new stack frame.
 176    *
 177    * @return The bottom of the stack frame, from where local variable addressing
 178    * should start from.
 179    */
 180   public int link(final int size)
 181   {
 182 
 183     _currentFrameBottom = _frameTop;
 184     _frameTop += size;
 185 
 186     if (_frameTop >= _stackFrames.length)
 187     {
 188       XObject newsf[] = new XObject[_stackFrames.length + XPathContext.RECURSIONLIMIT + size];
 189 
 190       System.arraycopy(_stackFrames, 0, newsf, 0, _stackFrames.length);
 191 
 192       _stackFrames = newsf;
 193     }
 194 
 195     if (_linksTop + 1 >= _links.length)
 196     {
 197       int newlinks[] = new int[_links.length + (CLEARLIMITATION * 2)];
 198 
 199       System.arraycopy(_links, 0, newlinks, 0, _links.length);
 200 
 201       _links = newlinks;
 202     }
 203 
 204     _links[_linksTop++] = _currentFrameBottom;
 205 
 206     return _currentFrameBottom;
 207   }
 208 
 209   /**
 210    * Free up the stack frame that was last allocated with
 211    * {@link #link(int size)}.
 212    */
 213   public  void unlink()
 214   {
 215     _frameTop = _links[--_linksTop];
 216     _currentFrameBottom = _links[_linksTop - 1];
 217   }
 218 
 219   /**
 220    * Free up the stack frame that was last allocated with
 221    * {@link #link(int size)}.
 222    * @param currentFrame The current frame to set to
 223    * after the unlink.
 224    */
 225   public  void unlink(int currentFrame)
 226   {
 227     _frameTop = _links[--_linksTop];
 228     _currentFrameBottom = currentFrame;
 229   }
 230 
 231   /**
 232    * Set a local variable or parameter in the current stack frame.
 233    *
 234    *
 235    * @param index Local variable index relative to the current stack
 236    * frame bottom.
 237    *
 238    * @param val The value of the variable that is being set.
 239    */
 240   public void setLocalVariable(int index, XObject val)
 241   {
 242     _stackFrames[index + _currentFrameBottom] = val;
 243   }
 244 
 245   /**
 246    * Set a local variable or parameter in the specified stack frame.
 247    *
 248    *
 249    * @param index Local variable index relative to the current stack
 250    * frame bottom.
 251    * NEEDSDOC @param stackFrame
 252    *
 253    * @param val The value of the variable that is being set.
 254    */
 255   public void setLocalVariable(int index, XObject val, int stackFrame)
 256   {
 257     _stackFrames[index + stackFrame] = val;
 258   }
 259 
 260   /**
 261    * Get a local variable or parameter in the current stack frame.
 262    *
 263    *
 264    * @param xctxt The XPath context, which must be passed in order to
 265    * lazy evaluate variables.
 266    *
 267    * @param index Local variable index relative to the current stack
 268    * frame bottom.
 269    *
 270    * @return The value of the variable.
 271    *
 272    * @throws TransformerException
 273    */
 274   public XObject getLocalVariable(XPathContext xctxt, int index)
 275           throws TransformerException
 276   {
 277 
 278     index += _currentFrameBottom;
 279 
 280     XObject val = _stackFrames[index];
 281 
 282     if(null == val)
 283       throw new TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VARIABLE_ACCESSED_BEFORE_BIND, null),
 284                      xctxt.getSAXLocator());
 285       // "Variable accessed before it is bound!", xctxt.getSAXLocator());
 286 
 287     // Lazy execution of variables.
 288     if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
 289       return (_stackFrames[index] = val.execute(xctxt));
 290 
 291     return val;
 292   }
 293 
 294   /**
 295    * Get a local variable or parameter in the current stack frame.
 296    *
 297    *
 298    * @param index Local variable index relative to the given
 299    * frame bottom.
 300    * NEEDSDOC @param frame
 301    *
 302    * @return The value of the variable.
 303    *
 304    * @throws TransformerException
 305    */
 306   public XObject getLocalVariable(int index, int frame)
 307           throws TransformerException
 308   {
 309 
 310     index += frame;
 311 
 312     XObject val = _stackFrames[index];
 313 
 314     return val;
 315   }
 316 
 317   /**
 318    * Get a local variable or parameter in the current stack frame.
 319    *
 320    *
 321    * @param xctxt The XPath context, which must be passed in order to
 322    * lazy evaluate variables.
 323    *
 324    * @param index Local variable index relative to the current stack
 325    * frame bottom.
 326    *
 327    * @return The value of the variable.
 328    *
 329    * @throws TransformerException
 330    */
 331   public XObject getLocalVariable(XPathContext xctxt, int index, boolean destructiveOK)
 332           throws TransformerException
 333   {
 334 
 335     index += _currentFrameBottom;
 336 
 337     XObject val = _stackFrames[index];
 338 
 339     if(null == val)
 340       throw new TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VARIABLE_ACCESSED_BEFORE_BIND, null),
 341                      xctxt.getSAXLocator());
 342       // "Variable accessed before it is bound!", xctxt.getSAXLocator());
 343 
 344     // Lazy execution of variables.
 345     if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
 346       return (_stackFrames[index] = val.execute(xctxt));
 347 
 348     return destructiveOK ? val : val.getFresh();
 349   }
 350 
 351   /**
 352    * Tell if a local variable has been set or not.
 353    *
 354    * @param index Local variable index relative to the current stack
 355    * frame bottom.
 356    *
 357    * @return true if the value at the index is not null.
 358    *
 359    * @throws TransformerException
 360    */
 361   public boolean isLocalSet(int index) throws TransformerException
 362   {
 363     return (_stackFrames[index + _currentFrameBottom] != null);
 364   }
 365 
 366   /** NEEDSDOC Field m_nulls          */
 367   private static XObject[] m_nulls = new XObject[CLEARLIMITATION];
 368 
 369   /**
 370    * Use this to clear the variables in a section of the stack.  This is
 371    * used to clear the parameter section of the stack, so that default param
 372    * values can tell if they've already been set.  It is important to note that
 373    * this function has a 1K limitation.
 374    *
 375    * @param start The start position, relative to the current local stack frame.
 376    * @param len The number of slots to be cleared.
 377    */
 378   public void clearLocalSlots(int start, int len)
 379   {
 380 
 381     start += _currentFrameBottom;
 382 
 383     System.arraycopy(m_nulls, 0, _stackFrames, start, len);
 384   }
 385 
 386   /**
 387    * Set a global variable or parameter in the global stack frame.
 388    *
 389    *
 390    * @param index Local variable index relative to the global stack frame
 391    * bottom.
 392    *
 393    * @param val The value of the variable that is being set.
 394    */
 395   public void setGlobalVariable(final int index, final XObject val)
 396   {
 397     _stackFrames[index] = val;
 398   }
 399 
 400   /**
 401    * Get a global variable or parameter from the global stack frame.
 402    *
 403    *
 404    * @param xctxt The XPath context, which must be passed in order to
 405    * lazy evaluate variables.
 406    *
 407    * @param index Global variable index relative to the global stack
 408    * frame bottom.
 409    *
 410    * @return The value of the variable.
 411    *
 412    * @throws TransformerException
 413    */
 414   public XObject getGlobalVariable(XPathContext xctxt, final int index)
 415           throws TransformerException
 416   {
 417 
 418     XObject val = _stackFrames[index];
 419 
 420     // Lazy execution of variables.
 421     if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
 422       return (_stackFrames[index] = val.execute(xctxt));
 423 
 424     return val;
 425   }
 426 
 427   /**
 428    * Get a global variable or parameter from the global stack frame.
 429    *
 430    *
 431    * @param xctxt The XPath context, which must be passed in order to
 432    * lazy evaluate variables.
 433    *
 434    * @param index Global variable index relative to the global stack
 435    * frame bottom.
 436    *
 437    * @return The value of the variable.
 438    *
 439    * @throws TransformerException
 440    */
 441   public XObject getGlobalVariable(XPathContext xctxt, final int index, boolean destructiveOK)
 442           throws TransformerException
 443   {
 444 
 445     XObject val = _stackFrames[index];
 446 
 447     // Lazy execution of variables.
 448     if (val.getType() == XObject.CLASS_UNRESOLVEDVARIABLE)
 449       return (_stackFrames[index] = val.execute(xctxt));
 450 
 451     return destructiveOK ? val : val.getFresh();
 452   }
 453 
 454   /**
 455    * Get a variable based on it's qualified name.
 456    * This is for external use only.
 457    *
 458    * @param xctxt The XPath context, which must be passed in order to
 459    * lazy evaluate variables.
 460    *
 461    * @param qname The qualified name of the variable.
 462    *
 463    * @return The evaluated value of the variable.
 464    *
 465    * @throws javax.xml.transform.TransformerException
 466    */
 467   public XObject getVariableOrParam(
 468           XPathContext xctxt, com.sun.org.apache.xml.internal.utils.QName qname)
 469             throws javax.xml.transform.TransformerException
 470   {
 471 
 472     // J2SE does not support Xalan interpretive
 473         /*
 474     com.sun.org.apache.xml.internal.utils.PrefixResolver prefixResolver =
 475       xctxt.getNamespaceContext();
 476 
 477     // Get the current ElemTemplateElement, which must be pushed in as the
 478     // prefix resolver, and then walk backwards in document order, searching
 479     // for an xsl:param element or xsl:variable element that matches our
 480     // qname.  If we reach the top level, use the StylesheetRoot's composed
 481     // list of top level variables and parameters.
 482 
 483     if (prefixResolver instanceof com.sun.org.apache.xalan.internal.templates.ElemTemplateElement)
 484     {
 485 
 486       com.sun.org.apache.xalan.internal.templates.ElemVariable vvar;
 487 
 488       com.sun.org.apache.xalan.internal.templates.ElemTemplateElement prev =
 489         (com.sun.org.apache.xalan.internal.templates.ElemTemplateElement) prefixResolver;
 490 
 491       if (!(prev instanceof com.sun.org.apache.xalan.internal.templates.Stylesheet))
 492       {
 493         while ( !(prev.getParentNode() instanceof com.sun.org.apache.xalan.internal.templates.Stylesheet) )
 494         {
 495           com.sun.org.apache.xalan.internal.templates.ElemTemplateElement savedprev = prev;
 496 
 497           while (null != (prev = prev.getPreviousSiblingElem()))
 498           {
 499             if (prev instanceof com.sun.org.apache.xalan.internal.templates.ElemVariable)
 500             {
 501               vvar = (com.sun.org.apache.xalan.internal.templates.ElemVariable) prev;
 502 
 503               if (vvar.getName().equals(qname))
 504                 return getLocalVariable(xctxt, vvar.getIndex());
 505             }
 506           }
 507           prev = savedprev.getParentElem();
 508         }
 509       }
 510 
 511       vvar = prev.getStylesheetRoot().getVariableOrParamComposed(qname);
 512       if (null != vvar)
 513         return getGlobalVariable(xctxt, vvar.getIndex());
 514     }
 515     */
 516 
 517     throw new javax.xml.transform.TransformerException(XSLMessages.createXPATHMessage(XPATHErrorResources.ER_VAR_NOT_RESOLVABLE, new Object[]{qname.toString()})); //"Variable not resolvable: " + qname);
 518   }
 519 }  // end VariableStack