1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the  "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 
20 
21 // Class header file.
22 #include "VariablesStack.hpp"
23 
24 
25 
26 #include <algorithm>
27 
28 
29 
30 #include <xalanc/PlatformSupport/XalanMessageLoader.hpp>
31 
32 
33 
34 #include "ElemVariable.hpp"
35 #include "StylesheetExecutionContext.hpp"
36 
37 
38 
39 namespace XALAN_CPP_NAMESPACE {
40 
41 
42 
VariablesStack(MemoryManager & theManager)43 VariablesStack::VariablesStack(MemoryManager& theManager) :
44     m_stack(theManager),
45     m_globalStackFrameIndex(~0u),
46     m_globalStackFrameMarked(false),
47     m_currentStackFrameIndex(0),
48     m_guardStack(theManager),
49     m_elementFrameStack(theManager)
50 {
51     m_stack.reserve(eDefaultStackSize);
52 }
53 
54 
55 
~VariablesStack()56 VariablesStack::~VariablesStack()
57 {
58 }
59 
60 
61 
62 void
reset()63 VariablesStack::reset()
64 {
65     while(m_stack.empty() == false)
66     {
67         pop();
68     }
69 
70     m_stack.clear();
71     m_guardStack.clear();
72     m_elementFrameStack.clear();
73 
74     m_globalStackFrameMarked = false;
75     m_globalStackFrameIndex = ~0u;
76 }
77 
78 
79 
80 bool
elementFrameAlreadyPushed(const ElemTemplateElement * elem) const81 VariablesStack::elementFrameAlreadyPushed(const ElemTemplateElement*    elem) const
82 {
83     const VariableStackStackType::size_type     nElems = m_stack.size();
84     assert(nElems > 0);
85 
86     // There is guaranteed to be a context marker at
87     // the bottom of the stack, so i should stop at
88     // 1.
89     for(VariableStackStackType::size_type i = nElems - 1; i > 0; --i)
90     {
91         const StackEntry&   theEntry = m_stack[i];
92 
93         if(theEntry.getType() == StackEntry::eElementFrameMarker)
94         {
95             if(theEntry.getElement() == elem)
96             {
97                 return true;
98             }
99         }
100     }
101 
102     return false;
103 }
104 
105 
106 
107 void
pushContextMarker()108 VariablesStack::pushContextMarker()
109 {
110     push(StackEntry());
111 }
112 
113 
114 
115 void
popContextMarker()116 VariablesStack::popContextMarker()
117 {
118     VariableStackStackType::iterator    i = m_stack.end();
119 
120     for(; ;)
121     {
122         assert(i != m_stack.begin());
123 
124         const StackEntry&           theEntry = *--i;
125         assert(theEntry == back());
126 
127         const StackEntry::eType     type = theEntry.getType();
128         assert(type < StackEntry::eNextValue && type >= 0);
129 
130         pop();
131 
132         if (type == StackEntry::eContextMarker)
133         {
134             break;
135         }
136     }
137 
138     // This introduced a regression into 1.7, because of all the
139     // external fiddling with the stack frame index.  We need to
140     // re-write that code so no external meddling is necessary,
141     // so we can track the stack frame index properly.  In particular,
142     // this is an issue with xsl:apply-templates, because a context
143     // marker is pushed before its select attribute is evaluated, even
144     // though the select expression really should be evaluated in
145     // the previous stack context.
146 //  m_currentStackFrameIndex = size_type(m_stack.size());
147 }
148 
149 
150 
151 void
push(const StackEntry & theEntry)152 VariablesStack::push(const StackEntry&  theEntry)
153 {
154     assert(theEntry.getType() < StackEntry::eNextValue && theEntry.getType() >= 0);
155 
156     if(m_currentStackFrameIndex == m_stack.size())
157     {
158         ++m_currentStackFrameIndex;
159     }
160 
161     m_stack.push_back(theEntry);
162 
163     // Increment the global stack frame index as long as we're pushing variables, and
164     // it already hasn't been marked.  This is a temporary work-around for problems
165     // with evaluating top-level variables as they're pushed, rather than as they're
166     // used.
167     if (m_globalStackFrameMarked == false && theEntry.getType() == StackEntry::eVariable)
168     {
169         m_globalStackFrameIndex = m_currentStackFrameIndex;
170     }
171 }
172 
173 
174 
175 void
pop()176 VariablesStack::pop()
177 {
178     assert(m_stack.empty() == false);
179 
180     if(m_currentStackFrameIndex == m_stack.size())
181     {
182         --m_currentStackFrameIndex;
183     }
184 
185     m_stack.pop_back();
186 }
187 
188 
189 
190 void
operator ()(const VariablesStack::ParamsVectorType::value_type & theEntry) const191 VariablesStack::PushParamFunctor::operator()(const VariablesStack::ParamsVectorType::value_type&    theEntry) const
192 {
193     assert(theEntry.m_qname != 0);
194 
195     if (theEntry.m_value.null() == false)
196     {
197         m_variablesStack.push(VariablesStack::StackEntry(theEntry.m_qname, theEntry.m_value, true));
198     }
199     else
200     {
201         assert(theEntry.m_variable != 0);
202 
203         m_variablesStack.push(VariablesStack::StackEntry(theEntry.m_qname, theEntry.m_variable, true));
204     }
205 }
206 
207 
208 
CommitPushParams(VariablesStack & theVariablesStack)209 VariablesStack::CommitPushParams::CommitPushParams(VariablesStack&  theVariablesStack) :
210     m_variablesStack(&theVariablesStack),
211     m_stackSize(theVariablesStack.getStackSize())
212 {
213 }
214 
215 
216 
~CommitPushParams()217 VariablesStack::CommitPushParams::~CommitPushParams()
218 {
219     if (m_variablesStack != 0)
220     {
221         while(m_variablesStack->getStackSize() > m_stackSize)
222         {
223             m_variablesStack->pop();
224         }
225     }
226 }
227 
228 
229 
230 void
pushParams(const ParamsVectorType & theParams)231 VariablesStack::pushParams(const ParamsVectorType&  theParams)
232 {
233     // This object will push the params and pop them
234     // if we don't call it's commit() member function.  So
235     // if an exception is thrown while transferring the
236     // parameters, the stack stays in a consistent state.
237     CommitPushParams    thePusher(*this);
238 
239     using std::for_each;
240 
241     for_each(theParams.begin(), theParams.end(), PushParamFunctor(*this));
242 
243     thePusher.commit();
244 }
245 
246 
247 
248 void
pushVariable(const XalanQName & name,const ElemVariable * var,const ElemTemplateElement * e)249 VariablesStack::pushVariable(
250             const XalanQName&           name,
251             const ElemVariable*         var,
252             const ElemTemplateElement*  e)
253 {
254     if(elementFrameAlreadyPushed(e) == false)
255     {
256         pushElementFrame(e);
257     }
258 
259     push(StackEntry(&name, var));
260 }
261 
262 
263 
264 void
pushVariable(const XalanQName & name,const XObjectPtr & val,const ElemTemplateElement * e)265 VariablesStack::pushVariable(
266             const XalanQName&           name,
267             const XObjectPtr&           val,
268             const ElemTemplateElement*  e)
269 {
270     if(elementFrameAlreadyPushed(e) == false)
271     {
272         XalanDOMString theBuffer(m_stack.getMemoryManager());
273 
274         throw InvalidStackContextException(theBuffer);
275     }
276 
277     push(StackEntry(&name, val));
278 }
279 
280 
281 
282 void
start()283 VariablesStack::start()
284 {
285 }
286 
287 
288 
289 void
resetParams()290 VariablesStack::resetParams()
291 {
292     const size_type     nElems = getCurrentStackFrameIndex();
293     assert(nElems > 0);
294 
295     // There is guaranteed to be a context marker at
296     // the bottom of the stack, so i should stop at
297     // 1.
298     for(size_type i = nElems - 1; i > 0; --i)
299     {
300         StackEntry&     theEntry = m_stack[i];
301 
302         if(theEntry.getType() == StackEntry::eContextMarker)
303         {
304             break;
305         }
306         else
307         {
308             theEntry.deactivate();
309         }
310     }
311 }
312 
313 
314 
315 void
markGlobalStackFrame()316 VariablesStack::markGlobalStackFrame()
317 {
318     m_globalStackFrameIndex = size_type(m_stack.size());
319 
320     m_globalStackFrameMarked = true;
321 
322     pushContextMarker();
323 }
324 
325 
326 
327 void
unmarkGlobalStackFrame()328 VariablesStack::unmarkGlobalStackFrame()
329 {
330     popContextMarker();
331 
332     m_globalStackFrameIndex = ~0u;
333 
334     m_globalStackFrameMarked = false;
335 }
336 
337 
338 
339 const XObjectPtr
findXObject(const XalanQName & name,StylesheetExecutionContext & executionContext,bool fIsParam,bool fSearchGlobalSpace,bool & fNameFound)340 VariablesStack::findXObject(
341             const XalanQName&               name,
342             StylesheetExecutionContext&     executionContext,
343             bool                            fIsParam,
344             bool                            fSearchGlobalSpace,
345             bool&                           fNameFound)
346 {
347     typedef VariableStackStackType::size_type   size_type;
348 
349     // findEntry() returns an index into the stack.  We should
350     // _never_ take the address of anything in the stack, since
351     // the address could change at unexpected times.
352     const size_type     theEntryIndex =
353         findEntry(name, fIsParam, fSearchGlobalSpace);
354 
355     if (theEntryIndex == m_stack.size())
356     {
357         fNameFound = false;
358 
359         return XObjectPtr();
360     }
361     else
362     {
363         assert(theEntryIndex < m_stack.size());
364 
365         fNameFound = true;
366 
367         assert(m_stack[theEntryIndex].getType() == StackEntry::eVariable ||
368                m_stack[theEntryIndex].getType() == StackEntry::eParam ||
369                m_stack[theEntryIndex].getType() == StackEntry::eActiveParam);
370 
371         const XObjectPtr&   theValue = m_stack[theEntryIndex].getValue();
372 
373         if (theValue.null() == false)
374         {
375             return theValue;
376         }
377         else
378         {
379             const ElemVariable* const   var = m_stack[theEntryIndex].getVariable();
380 
381             XObjectPtr                  theNewValue;
382 
383             if (var != 0)
384             {
385                 XalanNode* const    doc = executionContext.getRootDocument();
386                 assert(doc != 0);
387 
388                 using std::find;
389 
390                 // See if the ElemVariable instance is already being evaluated...
391                 if (find(m_guardStack.begin(), m_guardStack.end(), var) != m_guardStack.end())
392                 {
393                     const StylesheetExecutionContext::GetCachedString   theGuard(executionContext);
394 
395                     executionContext.problem(
396                         StylesheetExecutionContext::eXSLTProcessor,
397                         StylesheetExecutionContext::eError,
398                         XalanMessageLoader::getMessage(
399                             theGuard.get(),
400                             XalanMessages::CircularVariableDefWasDetected),
401                         var->getLocator(),
402                         doc);
403                 }
404 
405                 m_guardStack.push_back(var);
406 
407 #if !defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION)
408                 executionContext.pushContextMarker();
409 #else
410                 // We need to set up a stack frame for the variable's execution...
411                 typedef StylesheetExecutionContext::PushAndPopContextMarker PushAndPopContextMarker;
412 
413                 const PushAndPopContextMarker   theContextMarkerPushPop(executionContext);
414 #endif
415 
416                 theNewValue = var->getValue(executionContext, doc);
417                 assert(theNewValue.null() == false);
418 
419 #if !defined(XALAN_RECURSIVE_STYLESHEET_EXECUTION)
420                 executionContext.popContextMarker();
421 #endif
422 
423                 assert(m_guardStack.empty() == false);
424 
425                 m_guardStack.pop_back();
426 
427                 m_stack[theEntryIndex].setValue(theNewValue);
428                 m_stack[theEntryIndex].activate();
429             }
430 
431             return theNewValue;
432         }
433     }
434 }
435 
436 
437 
438 VariablesStack::size_type
findEntry(const XalanQName & qname,bool fIsParam,bool fSearchGlobalSpace)439 VariablesStack::findEntry(
440             const XalanQName&   qname,
441             bool                fIsParam,
442             bool                fSearchGlobalSpace)
443 {
444     size_type   theEntryIndex = size_type(m_stack.size());
445 
446     const size_type     nElems = getCurrentStackFrameIndex();
447 
448     // There is guaranteed to be a context marker at
449     // the bottom of the stack, so i should stop at
450     // 1.
451     for(size_type i = nElems - 1; i > 0; --i)
452     {
453         StackEntry&                 theEntry = m_stack[i];
454 
455         const StackEntry::eType     theType = theEntry.getType();
456 
457         if(theType == StackEntry::eVariable ||
458            theType == StackEntry::eActiveParam)
459         {
460             assert(theEntry.getName() != 0);
461 
462             if(theEntry.getName()->equals(qname))
463             {
464                 theEntryIndex = i;
465 
466                 break;
467             }
468         }
469         else if (theType == StackEntry::eParam)
470         {
471             if (fIsParam == true)
472             {
473                 if(theEntry.getName()->equals(qname))
474                 {
475                     theEntry.activate();
476 
477                     theEntryIndex = i;
478 
479                     break;
480                 }
481             }
482         }
483         else if(theType == StackEntry::eContextMarker)
484         {
485             break;
486         }
487     }
488 
489     if(theEntryIndex == m_stack.size() && fIsParam == false && true == fSearchGlobalSpace && m_globalStackFrameIndex > 1)
490     {
491         // Look in the global space
492         for(size_type i = m_globalStackFrameIndex - 1; i > 0; i--)
493         {
494             StackEntry&     theEntry = m_stack[i];
495 
496             const StackEntry::eType     theType = theEntry.getType();
497 
498             if(theType == StackEntry::eVariable)
499             {
500                 assert(theEntry.getName() != 0);
501 
502                 if(theEntry.getName()->equals(qname))
503                 {
504                     theEntryIndex = i;
505 
506                     break;
507                 }
508             }
509             else if(theType == StackEntry::eContextMarker)
510             {
511                 break;
512             }
513         }
514     }
515 
516     return theEntryIndex;
517 }
518 
519 
520 
521 void
pushElementFrame(const ElemTemplateElement * elem)522 VariablesStack::pushElementFrame(const ElemTemplateElement* elem)
523 {
524     push(StackEntry(elem));
525 
526 #if !defined(NDEBUG)
527     m_elementFrameStack.push_back(elem);
528 #endif
529 }
530 
531 
532 
533 class EnsurePop
534 {
535 public:
536 
EnsurePop(VariablesStack & theVariablesStack)537     EnsurePop(VariablesStack&   theVariablesStack) :
538         m_variablesStack(theVariablesStack)
539     {
540     }
541 
~EnsurePop()542     ~EnsurePop()
543     {
544         m_variablesStack.pop();
545     }
546 
547 private:
548 
549     VariablesStack&     m_variablesStack;
550 };
551 
552 
553 
554 void
popElementFrame()555 VariablesStack::popElementFrame()
556 {
557     const VariableStackStackType::size_type     nElems = m_stack.size();
558     assert(nElems > 0);
559 
560     // There is guaranteed to be a context marker at
561     // the bottom of the stack, so i should stop at
562     // 1.
563     for(VariableStackStackType::size_type i = nElems - 1; i > 0; --i)
564     {
565         const StackEntry&   theEntry = m_stack[i];
566 
567         // Guarantee that it will be popped when we're done.
568         const EnsurePop     theEnsurePop(*this);
569 
570         if(theEntry.getType() == StackEntry::eContextMarker)
571         {
572             XalanDOMString theBuffer(m_stack.getMemoryManager());
573 
574             throw InvalidStackContextException(theBuffer);
575         }
576         else if (theEntry.getType() == StackEntry::eElementFrameMarker)
577         {
578 #if !defined(NDEBUG)
579             const ElemTemplateElement* const    theElement =
580                 theEntry.getElement();
581 
582             if (m_elementFrameStack.empty() == true)
583             {
584                 XalanDOMString theBuffer(m_stack.getMemoryManager());
585 
586                 throw InvalidStackContextException(theBuffer);
587             }
588 
589             const ElemTemplateElement* const    theStackBack =
590                 m_elementFrameStack.back();
591 
592             m_elementFrameStack.pop_back();
593 
594             if (theElement != theStackBack)
595             {
596                 XalanDOMString theBuffer(m_stack.getMemoryManager());
597 
598                 throw InvalidStackContextException(theBuffer);
599             }
600 #endif
601 
602             break;
603         }
604     }
605 }
606 
607 
608 
StackEntry()609 VariablesStack::StackEntry::StackEntry() :
610     m_type(eContextMarker),
611     m_qname(0),
612     m_value(),
613     m_variable(0),
614     m_element(0)
615 {
616 }
617 
618 
619 
StackEntry(const XalanQName * name,const XObjectPtr & val,bool isParam)620 VariablesStack::StackEntry::StackEntry(
621             const XalanQName*   name,
622             const XObjectPtr&   val,
623             bool                isParam) :
624     m_type(isParam == true ? eParam : eVariable),
625     m_qname(name),
626     m_value(val),
627     m_variable(0),
628     m_element(0)
629 {
630 }
631 
632 
633 
StackEntry(const XalanQName * name,const ElemVariable * var,bool isParam)634 VariablesStack::StackEntry::StackEntry(
635             const XalanQName*       name,
636             const ElemVariable*     var,
637             bool                    isParam) :
638     m_type(isParam == true ? eParam : eVariable),
639     m_qname(name),
640     m_value(),
641     m_variable(var),
642     m_element(0)
643 {
644 }
645 
646 
647 
StackEntry(const ElemTemplateElement * elem)648 VariablesStack::StackEntry::StackEntry(const ElemTemplateElement*   elem) :
649     m_type(eElementFrameMarker),
650     m_qname(0),
651     m_value(),
652     m_variable(0),
653     m_element(elem)
654 {
655 }
656 
657 
658 
StackEntry(const StackEntry & theSource)659 VariablesStack::StackEntry::StackEntry(const StackEntry&    theSource) :
660     m_type(theSource.m_type),
661     m_qname(theSource.m_qname),
662     m_value(theSource.m_value),
663     m_variable(theSource.m_variable),
664     m_element(theSource.m_element)
665 {
666 }
667 
668 
669 
~StackEntry()670 VariablesStack::StackEntry::~StackEntry()
671 {
672 }
673 
674 
675 
676 VariablesStack::StackEntry&
operator =(const StackEntry & theRHS)677 VariablesStack::StackEntry::operator=(const StackEntry&     theRHS)
678 {
679     if (this != &theRHS)
680     {
681         m_type = theRHS.m_type;
682 
683         m_qname = theRHS.m_qname;
684 
685         m_value = theRHS.m_value;
686 
687         m_variable = theRHS.m_variable;
688 
689         m_element = theRHS.m_element;
690     }
691 
692     return *this;
693 }
694 
695 
696 
697 // Equality for StackEntry instances is probably bogus,
698 // so it might be worthwhile to just get rid of this.
699 bool
operator ==(const StackEntry & theRHS) const700 VariablesStack::StackEntry::operator==(const StackEntry&    theRHS) const
701 {
702     bool    fResult = false;
703 
704     if (m_type == theRHS.m_type)
705     {
706         if (m_type == eContextMarker)
707         {
708             if (&theRHS == this)
709             {
710                 fResult = true;
711             }
712         }
713         else if (m_type == eVariable || m_type == eParam || m_type == eActiveParam)
714         {
715             // We only need to compare the variable related members...
716             if (m_value == theRHS.m_value ||
717                 m_variable == theRHS.m_variable)
718             {
719                 fResult = true;
720             }
721         }
722         else if (m_type == eElementFrameMarker)
723         {
724             if (m_element == theRHS.m_element)
725             {
726                 fResult = true;
727             }
728         }
729         else
730         {
731             assert(0);
732         }
733     }
734 
735     return fResult;
736 }
737 
738 
739 
740 void
activate()741 VariablesStack::StackEntry::activate()
742 {
743     if (m_type == eParam)
744     {
745         m_type = eActiveParam;
746     }
747 }
748 
749 
750 
751 void
deactivate()752 VariablesStack::StackEntry::deactivate()
753 {
754     if (m_type == eActiveParam)
755     {
756         m_type = eParam;
757     }
758 }
759 
760 
761 const XalanDOMChar  VariablesStack::InvalidStackContextException::m_type[] =
762 {
763     XalanUnicode::charLetter_I,
764     XalanUnicode::charLetter_n,
765     XalanUnicode::charLetter_v,
766     XalanUnicode::charLetter_a,
767     XalanUnicode::charLetter_l,
768     XalanUnicode::charLetter_i,
769     XalanUnicode::charLetter_d,
770     XalanUnicode::charLetter_S,
771     XalanUnicode::charLetter_t,
772     XalanUnicode::charLetter_a,
773     XalanUnicode::charLetter_c,
774     XalanUnicode::charLetter_k,
775     XalanUnicode::charLetter_C,
776     XalanUnicode::charLetter_o,
777     XalanUnicode::charLetter_n,
778     XalanUnicode::charLetter_t,
779     XalanUnicode::charLetter_e,
780     XalanUnicode::charLetter_x,
781     XalanUnicode::charLetter_t,
782     XalanUnicode::charLetter_E,
783     XalanUnicode::charLetter_x,
784     XalanUnicode::charLetter_c,
785     XalanUnicode::charLetter_e,
786     XalanUnicode::charLetter_p,
787     XalanUnicode::charLetter_t,
788     XalanUnicode::charLetter_i,
789     XalanUnicode::charLetter_o,
790     XalanUnicode::charLetter_n,
791     0
792 };
793 
794 
795 
InvalidStackContextException(XalanDOMString & theResult)796 VariablesStack::InvalidStackContextException::InvalidStackContextException(XalanDOMString& theResult) :
797     XSLTProcessorException(
798         theResult.getMemoryManager(),
799         XalanMessageLoader::getMessage(
800             theResult,
801             XalanMessages::InvalidStackContext))
802 {
803 }
804 
805 
806 
~InvalidStackContextException()807 VariablesStack::InvalidStackContextException::~InvalidStackContextException()
808 {
809 }
810 
811 
812 
813 }
814