1 /*
   2  * Copyright (c) 2015, 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  * $Id: ForEach.java,v 1.2.4.1 2005/09/01 15:23:46 pvedula Exp $
  22  */
  23 
  24 package com.sun.org.apache.xalan.internal.xsltc.compiler;
  25 
  26 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
  27 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  28 import com.sun.org.apache.bcel.internal.generic.GOTO;
  29 import com.sun.org.apache.bcel.internal.generic.IFGT;
  30 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  31 import com.sun.org.apache.bcel.internal.generic.InstructionList;
  32 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ClassGenerator;
  33 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
  34 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.MethodGenerator;
  35 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeSetType;
  36 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.NodeType;
  37 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ReferenceType;
  38 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ResultTreeType;
  39 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type;
  40 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.TypeCheckError;
  41 import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
  42 import java.util.ArrayList;
  43 import java.util.Iterator;
  44 import java.util.List;
  45 
  46 /**
  47  * @author Jacek Ambroziak
  48  * @author Santiago Pericas-Geertsen
  49  * @author Morten Jorgensen
  50  * @LastModified: Oct 2017
  51  */
  52 final class ForEach extends Instruction {
  53 
  54     private Expression _select;
  55     private Type       _type;
  56 
  57     public void display(int indent) {
  58         indent(indent);
  59         Util.println("ForEach");
  60         indent(indent + IndentIncrement);
  61         Util.println("select " + _select.toString());
  62         displayContents(indent + IndentIncrement);
  63     }
  64 
  65     public void parseContents(Parser parser) {
  66         _select = parser.parseExpression(this, "select", null);
  67 
  68         parseChildren(parser);
  69 
  70         // make sure required attribute(s) have been set
  71         if (_select.isDummy()) {
  72             reportError(this, parser, ErrorMsg.REQUIRED_ATTR_ERR, "select");
  73         }
  74     }
  75 
  76     public Type typeCheck(SymbolTable stable) throws TypeCheckError {
  77         _type = _select.typeCheck(stable);
  78 
  79         if (_type instanceof ReferenceType || _type instanceof NodeType) {
  80             _select = new CastExpr(_select, Type.NodeSet);
  81             typeCheckContents(stable);
  82             return Type.Void;
  83         }
  84         if (_type instanceof NodeSetType||_type instanceof ResultTreeType) {
  85             typeCheckContents(stable);
  86             return Type.Void;
  87         }
  88         throw new TypeCheckError(this);
  89     }
  90 
  91     public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
  92         final ConstantPoolGen cpg = classGen.getConstantPool();
  93         final InstructionList il = methodGen.getInstructionList();
  94 
  95         // Save current node and current iterator on the stack
  96         il.append(methodGen.loadCurrentNode());
  97         il.append(methodGen.loadIterator());
  98 
  99         // Collect sort objects associated with this instruction
 100         final List<Sort> sortObjects = new ArrayList<>();
 101         Iterator<SyntaxTreeNode> children = elements();
 102         while (children.hasNext()) {
 103             final SyntaxTreeNode child = children.next();
 104             if (child instanceof Sort) {
 105                 sortObjects.add((Sort)child);
 106             }
 107         }
 108 
 109         if ((_type != null) && (_type instanceof ResultTreeType)) {
 110             // Store existing DOM on stack - must be restored when loop is done
 111             il.append(methodGen.loadDOM());
 112 
 113             // <xsl:sort> cannot be applied to a result tree - issue warning
 114             if (sortObjects.size() > 0) {
 115                 ErrorMsg msg = new ErrorMsg(ErrorMsg.RESULT_TREE_SORT_ERR,this);
 116                 getParser().reportError(WARNING, msg);
 117             }
 118 
 119             // Put the result tree on the stack (DOM)
 120             _select.translate(classGen, methodGen);
 121             // Get an iterator for the whole DOM - excluding the root node
 122             _type.translateTo(classGen, methodGen, Type.NodeSet);
 123             // Store the result tree as the default DOM
 124             il.append(SWAP);
 125             il.append(methodGen.storeDOM());
 126         }
 127         else {
 128             // Compile node iterator
 129             if (sortObjects.size() > 0) {
 130                 Sort.translateSortIterator(classGen, methodGen,
 131                                            _select, sortObjects);
 132             }
 133             else {
 134                 _select.translate(classGen, methodGen);
 135             }
 136 
 137             if (_type instanceof ReferenceType == false) {
 138                 il.append(methodGen.loadContextNode());
 139                 il.append(methodGen.setStartNode());
 140             }
 141         }
 142 
 143 
 144         // Overwrite current iterator
 145         il.append(methodGen.storeIterator());
 146 
 147         // Give local variables (if any) default values before starting loop
 148         initializeVariables(classGen, methodGen);
 149 
 150         final BranchHandle nextNode = il.append(new GOTO(null));
 151         final InstructionHandle loop = il.append(NOP);
 152 
 153         translateContents(classGen, methodGen);
 154 
 155         nextNode.setTarget(il.append(methodGen.loadIterator()));
 156         il.append(methodGen.nextNode());
 157         il.append(DUP);
 158         il.append(methodGen.storeCurrentNode());
 159         il.append(new IFGT(loop));
 160 
 161         // Restore current DOM (if result tree was used instead for this loop)
 162         if ((_type != null) && (_type instanceof ResultTreeType)) {
 163             il.append(methodGen.storeDOM());
 164         }
 165 
 166         // Restore current node and current iterator from the stack
 167         il.append(methodGen.storeIterator());
 168         il.append(methodGen.storeCurrentNode());
 169     }
 170 
 171     /**
 172      * The code that is generated by nested for-each loops can appear to some
 173      * JVMs as if it is accessing un-initialized variables. We must add some
 174      * code that pushes the default variable value on the stack and pops it
 175      * into the variable slot. This is done by the Variable.initialize()
 176      * method. The code that we compile for this loop looks like this:
 177      *
 178      *           initialize iterator
 179      *           initialize variables <-- HERE!!!
 180      *           goto   Iterate
 181      *  Loop:    :
 182      *           : (code for <xsl:for-each> contents)
 183      *           :
 184      *  Iterate: node = iterator.next();
 185      *           if (node != END) goto Loop
 186      */
 187     public void initializeVariables(ClassGenerator classGen,
 188                                    MethodGenerator methodGen) {
 189         final int n = elementCount();
 190         for (int i = 0; i < n; i++) {
 191             final Object child = getContents().get(i);
 192             if (child instanceof Variable) {
 193                 Variable var = (Variable)child;
 194                 var.initialize(classGen, methodGen);
 195             }
 196         }
 197     }
 198 
 199 }