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