private Node buildSJSessionTypeCast(SJSessionTypeCast stc) throws SemanticException {
    SJTypeNode tn = disambiguateSJTypeNode(this, stc.sessionType());
    SJSessionType st = tn.type();

    if (stc instanceof SJAmbiguousCast) {
      SJNoAliasExprExt naee = getSJNoAliasExprExt(stc);

      Position pos = stc.position();
      Expr e = stc.expr();

      if (st instanceof SJBeginType) {
        stc = sjnf.SJChannelCast(pos, e, tn);
      } else {
        stc = sjnf.SJSessionCast(pos, e, tn);
      }

      stc = (SJSessionTypeCast) buildAndCheckTypes(this, stc);
      stc =
          (SJSessionTypeCast)
              SJNoAliasExprBuilder.setSJNoAliasExprExt(
                  sjef,
                  stc,
                  naee.isNoAlias(),
                  naee.isFinal(),
                  naee.fields(),
                  naee.locals(),
                  naee.arrayAccesses());
    } else {
      stc = stc.sessionType(tn);
    }

    stc = (SJSessionTypeCast) setSJTypeableExt(sjef, stc, st);

    return stc;
  }
  private Expr buildNewArray(Position pos, List contents) {
    NewArray na = sjnf.makeSocketsArray(pos, contents.size());

    ArrayInit ai = sjnf.ArrayInit(pos, contents);
    na = na.init(ai).dims(Collections.emptyList()).additionalDims(1);
    return na;
  }
  // FIXME: does not integrate with recursive session method calls: recursionEnter/Exit and also
  // recurse do not match the control flow of recursive calls, and hence runtime type monitoring
  // does not work.
  private Node translateSJRecursion(SJRecursion r, QQ qq)
        // recursionEnter inserted by node factory, but translation is finished here..
      {
    SJSessionOperationExt soe = getSJSessionOperationExt(r);

    Position pos = r.position();

    Collection<Object> mapping = new LinkedList<Object>();

    String bname = getRecursionBooleanName(soe.targetNames(), r.label());

    mapping.add(bname);
    mapping.add(bname);

    String translation = "for (boolean %s = true; %s; ) { }";
    For f = (For) qq.parseStmt(translation, mapping.toArray());

    mapping.clear();

    r = (SJRecursion) r.inits(f.inits());
    r = (SJRecursion) r.cond(f.cond());

    List stmts = new LinkedList();

    stmts.addAll(r.body().statements());

    translation = "%s = %E;";
    mapping.add(bname);
    mapping.add(((Eval) stmts.remove(0)).expr()); // Factor out constant.

    Eval e = (Eval) qq.parseStmt(translation, mapping.toArray());

    stmts.add(0, e);

    r = (SJRecursion) r.body(sjnf.Block(pos, stmts));

    /*// Appending the recursion-exit hook. // Disabled to support delegation from within recursion scopes (socket will be null on recursion-exit).
    List<Local> targets = new LinkedList<Local>(); // FIXME: should be SJLocalSockets.

    for (String sjname : soe.targetNames()) // Unicast optimisation for SJRecursionExit is done within the NodeFactory method - this pass comes after SJUnicastOptimiser.
    {
    	targets.add(sjnf.Local(pos, sjnf.Id(pos, sjname))); // Would it be bad to instead alias the recursionEnter targets?
    }

    SJRecursionExit re = sjnf.SJRecursionExit(pos, targets); // Problem: the sockets argument array is not yet filled (for other (internal) basic operations, this was done earlier by SJSessionOperationParser)...

    re = (SJRecursionExit) SJVariableParser.parseSJSessionOperation(this, re); // ...Current fix: use those routines form those earlier passes.
    re = (SJRecursionExit) SJSessionOperationParser.fixSJBasicOperationArguments(this, re);*/

    // return sjnf.Block(pos, r, sjnf.Eval(pos, re));
    return sjnf.Block(pos, r);
  }
  private Node translateSJInbranch(SJInbranch ib, QQ qq) {
    StringBuilder translation = new StringBuilder("{ ");
    Collection<Object> mapping = new LinkedList<Object>();

    String labVar = UniqueID.newID(SJ_INBRANCH_LABEL_FIELD_PREFIX);

    translation.append("%T %s = %E; ");
    mapping.add(qq.parseType(SJ_LABEL_CLASS));
    mapping.add(labVar);
    mapping.add(ib.inlabel());

    for (Iterator<SJInbranchCase> i = ib.branchCases().iterator(); i.hasNext(); ) {
      SJInbranchCase ibc = i.next();

      translation.append("if (%s.equals(%E)) { %LS } ");
      mapping.add(labVar);
      mapping.add(sjnf.StringLit(ib.position(), ibc.label().labelValue()));
      mapping.add(ibc.statements());

      if (i.hasNext()) {
        translation.append("else ");
      }
    }

    translation.append("else { throw new SJIOException(\"Unexpected inbranch label: \" + %s); }");
    mapping.add(labVar);

    // FIXME: need a final else case to better handle, if runtime monitoring is disabled,
    // non-sj-compatibility mode and in case of malicious peers.

    translation.append('}');

    return qq.parseStmt(translation.toString(), mapping.toArray());
  }
/**
 * @author Raymond
 *     <p>Also does SJSessionTypeCasts and SJFormals.
 */
public class SJProtocolDeclTypeBuilder extends ContextVisitor {
  private SJTypeSystem sjts = (SJTypeSystem) typeSystem();
  private SJNodeFactory sjnf = (SJNodeFactory) nodeFactory();
  private SJExtFactory sjef = sjnf.extFactory();

  /** */
  public SJProtocolDeclTypeBuilder(Job job, TypeSystem ts, NodeFactory nf) {
    super(job, ts, nf);
  }

  protected NodeVisitor enterCall(Node parent, Node n) throws SemanticException {
    return this;
  }

  protected Node leaveCall(Node old, Node n, NodeVisitor v) throws SemanticException {
    if (n instanceof VarInit) {
      if (n instanceof FieldDecl) {
        n = buildFieldDecl((FieldDecl) n);
      } else // if (n instanceof LocalDecl)
      {
        n = buildLocalDecl((LocalDecl) n);
      }
    } else if (n instanceof SJSessionTypeCast) {
      n = buildSJSessionTypeCast((SJSessionTypeCast) n);
    } else if (n instanceof Formal) {
      n = buildFormal((Formal) n);
    }

    return n;
  }

  private Node buildFieldDecl(FieldDecl fd) throws SemanticException {
    Type t = fd.declType();

    if (t.isSubtype(SJ_PROTOCOL_TYPE)) {
      if (!(fd instanceof SJFieldProtocolDecl)) {
        throw new SemanticException(
            "[SJProtocolDeclTypeBuilder] Protocols may only be declared using the protocol keyword: "
                + fd);
      }

      SJTypeNode tn = disambiguateSJTypeNode(this, ((SJProtocolDecl) fd).sessionType());
      SJSessionType st = tn.type();
      String sjname = fd.name(); // Should match that given by SJVariable.sjname.

      SJFieldInstance fi = (SJFieldInstance) fd.fieldInstance();
      SJFieldProtocolInstance fpi =
          sjts.SJFieldProtocolInstance((SJFieldInstance) fd.fieldInstance(), st, sjname);

      fpi.setConstantValue(
          fi
              .constantValue()); // Currently, constant checker not run on custom nodes/type
                                 // objects. (Previously done by SJNoAliasTypeBuilder.)

      fd = fd.fieldInstance(fpi);
      fd = (FieldDecl) setSJProtocolDeclExt((SJProtocolDecl) fd, tn, sjname);

      updateSJFieldInstance(fi, fpi);
    }

    return fd;
  }

  private Node buildLocalDecl(LocalDecl ld) throws SemanticException {
    Type t = ld.declType();
    SJLocalInstance li = (SJLocalInstance) ld.localInstance();
    // SJNamedInstance ni = null;

    if (t.isSubtype(SJ_PROTOCOL_TYPE)) // Mostly the same as for LocalDecl.
    {
      if (!(ld instanceof SJLocalProtocolDecl)) {
        throw new SemanticException(
            "[SJProtocolDeclTypeBuilder] Protocols may only be declared using the protocol keyword: "
                + ld);
      }

      SJTypeNode tn = disambiguateSJTypeNode(this, ((SJProtocolDecl) ld).sessionType());
      SJSessionType st = tn.type();
      String sjname = ld.name(); // Should match that given by SJVariable.sjname.

      ld = ld.localInstance(sjts.SJLocalProtocolInstance(li, st, sjname));
      ld = (LocalDecl) setSJProtocolDeclExt((SJProtocolDecl) ld, tn, sjname);
    }

    return ld;
  }

  private SJProtocolDecl setSJProtocolDeclExt(SJProtocolDecl pd, SJTypeNode tn, String sjname) {
    pd = pd.sessionType(tn);
    pd = (SJProtocolDecl) setSJNamedExt(sjef, pd, tn.type(), sjname);

    return pd;
  }

  private Node buildSJSessionTypeCast(SJSessionTypeCast stc) throws SemanticException {
    SJTypeNode tn = disambiguateSJTypeNode(this, stc.sessionType());
    SJSessionType st = tn.type();

    if (stc instanceof SJAmbiguousCast) {
      SJNoAliasExprExt naee = getSJNoAliasExprExt(stc);

      Position pos = stc.position();
      Expr e = stc.expr();

      if (st instanceof SJBeginType) {
        stc = sjnf.SJChannelCast(pos, e, tn);
      } else {
        stc = sjnf.SJSessionCast(pos, e, tn);
      }

      stc = (SJSessionTypeCast) buildAndCheckTypes(this, stc);
      stc =
          (SJSessionTypeCast)
              SJNoAliasExprBuilder.setSJNoAliasExprExt(
                  sjef,
                  stc,
                  naee.isNoAlias(),
                  naee.isFinal(),
                  naee.fields(),
                  naee.locals(),
                  naee.arrayAccesses());
    } else {
      stc = stc.sessionType(tn);
    }

    stc = (SJSessionTypeCast) setSJTypeableExt(sjef, stc, st);

    return stc;
  }

  private Node buildFormal(Formal f) throws SemanticException // Based on buildLocalDecl.
      {
    Type t = f.declType();
    SJLocalInstance li = (SJLocalInstance) f.localInstance();

    if (t.isSubtype(SJ_ABSTRACT_CHANNEL_TYPE)) {
      if (!(f instanceof SJFormal)) {
        throw new SemanticException(
            "[SJProtocolDeclTypeBuilder] Session socket parameters should be declared by their session type: "
                + f);
      }

      SJTypeNode tn = disambiguateSJTypeNode(this, ((SJFormal) f).sessionType());
      SJSessionType st = tn.type();
      String sjname = f.name(); // Should match that given by SJVariable.sjname.

      f = f.localInstance(sjts.SJLocalProtocolInstance(li, st, sjname));
      f = setSJFormalExt((SJFormal) f, tn, sjname);
    }

    return f;
  }

  private Formal setSJFormalExt(
      SJFormal f, SJTypeNode tn, String sjname) // Based on setSJProtocolDeclExt
      {
    f = f.sessionType(tn);
    f = (SJFormal) setSJNamedExt(sjef, f, tn.type(), sjname);

    return f;
  }
}