private Expression transformInlineConstants(Expression exp) {
    if (exp instanceof PropertyExpression) {
      PropertyExpression pe = (PropertyExpression) exp;
      if (pe.getObjectExpression() instanceof ClassExpression) {
        ClassExpression ce = (ClassExpression) pe.getObjectExpression();
        ClassNode type = ce.getType();
        if (type.isEnum()) return exp;
        Expression constant = findConstant(type.getField(pe.getPropertyAsString()));
        // GRECLIPSE edit
        // if (constant != null) return constant;
        if (constant != null) {
          String name = pe.getText().replace('$', '.');
          Object alias = pe.getNodeMetaData("static.import.alias");
          if (alias != null && !alias.equals(pe.getPropertyAsString())) {
            name += " as " + alias;
          }
          // store the qualified name to facilitate organizing static imports
          constant.setNodeMetaData("static.import", name);

          return constant;
        }
        // GRECLIPSE end
      }
    } else if (exp instanceof ListExpression) {
      ListExpression le = (ListExpression) exp;
      ListExpression result = new ListExpression();
      for (Expression e : le.getExpressions()) {
        result.addExpression(transformInlineConstants(e));
      }
      return result;
    }

    return exp;
  }
Beispiel #2
0
 private void configureAnnotationFromDefinition(AnnotationNode definition, AnnotationNode root) {
   ClassNode type = definition.getClassNode();
   if (!type.isResolved()) return;
   Class clazz = type.getTypeClass();
   if (clazz == Retention.class) {
     Expression exp = definition.getMember("value");
     if (!(exp instanceof PropertyExpression)) return;
     PropertyExpression pe = (PropertyExpression) exp;
     String name = pe.getPropertyAsString();
     RetentionPolicy policy = RetentionPolicy.valueOf(name);
     setRetentionPolicy(policy, root);
   } else if (clazz == Target.class) {
     Expression exp = definition.getMember("value");
     if (!(exp instanceof ListExpression)) return;
     ListExpression le = (ListExpression) exp;
     int bitmap = 0;
     for (Expression e : le.getExpressions()) {
       PropertyExpression element = (PropertyExpression) e;
       String name = element.getPropertyAsString();
       ElementType value = ElementType.valueOf(name);
       bitmap |= getElementCode(value);
     }
     root.setAllowedTargets(bitmap);
   }
 }
Beispiel #3
0
 public static void configureAnnotationFromDefinition(
     AnnotationNode definition, AnnotationNode root) {
   ClassNode type = definition.getClassNode();
   if ("java.lang.annotation.Retention".equals(type.getName())) {
     Expression exp = definition.getMember("value");
     if (!(exp instanceof PropertyExpression)) return;
     PropertyExpression pe = (PropertyExpression) exp;
     String name = pe.getPropertyAsString();
     RetentionPolicy policy = RetentionPolicy.valueOf(name);
     setRetentionPolicy(policy, root);
   } else if ("java.lang.annotation.Target".equals(type.getName())) {
     Expression exp = definition.getMember("value");
     if (!(exp instanceof ListExpression)) return;
     ListExpression le = (ListExpression) exp;
     int bitmap = 0;
     for (Expression e : le.getExpressions()) {
       if (!(e instanceof PropertyExpression)) return;
       PropertyExpression element = (PropertyExpression) e;
       String name = element.getPropertyAsString();
       ElementType value = ElementType.valueOf(name);
       bitmap |= getElementCode(value);
     }
     root.setAllowedTargets(bitmap);
   }
 }
  private void callGrabAsStaticInitIfNeeded(
      ClassNode classNode,
      ClassNode grapeClassNode,
      AnnotationNode node,
      List<Map<String, Object>> grabExcludeMaps) {
    if ((node.getMember("initClass") == null)
        || (node.getMember("initClass") == ConstantExpression.TRUE)) {
      List<Statement> grabInitializers = new ArrayList<Statement>();

      // add Grape.grab(excludeArgs, [group:group, module:module, version:version,
      // classifier:classifier])
      // or Grape.grab([group:group, module:module, version:version, classifier:classifier])
      MapExpression me = new MapExpression();
      for (String s : GRAB_REQUIRED) {
        me.addMapEntryExpression(new ConstantExpression(s), node.getMember(s));
      }

      for (String s : GRAB_OPTIONAL) {
        if (node.getMember(s) != null)
          me.addMapEntryExpression(new ConstantExpression(s), node.getMember(s));
      }

      if (autoDownload != null) {
        me.addMapEntryExpression(
            new ConstantExpression(AUTO_DOWNLOAD_SETTING), new ConstantExpression(autoDownload));
      }

      if (disableChecksums != null) {
        me.addMapEntryExpression(
            new ConstantExpression(DISABLE_CHECKSUMS_SETTING),
            new ConstantExpression(disableChecksums));
      }

      ArgumentListExpression grabArgs;
      if (grabExcludeMaps.isEmpty()) {
        grabArgs = new ArgumentListExpression(me);
      } else {
        MapExpression args = new MapExpression();
        ListExpression list = new ListExpression();
        for (Map<String, Object> map : grabExcludeMaps) {
          Set<Map.Entry<String, Object>> entries = map.entrySet();
          MapExpression inner = new MapExpression();
          for (Map.Entry<String, Object> entry : entries) {
            inner.addMapEntryExpression(
                new ConstantExpression(entry.getKey()), new ConstantExpression(entry.getValue()));
          }
          list.addExpression(inner);
        }
        args.addMapEntryExpression(new ConstantExpression("excludes"), list);
        grabArgs = new ArgumentListExpression(args, me);
      }
      grabInitializers.add(
          new ExpressionStatement(
              new StaticMethodCallExpression(grapeClassNode, "grab", grabArgs)));

      // insert at beginning so we have the classloader set up before the class is called
      classNode.addStaticInitializerStatements(grabInitializers, true);
    }
  }
  private Class[] getTransformClasses(ClassNode classNode) {
    if (!classNode.hasClass()) {
      List<AnnotationNode> annotations = classNode.getAnnotations();
      AnnotationNode transformAnnotation = null;
      for (AnnotationNode anno : annotations) {
        if (anno.getClassNode().getName().equals(GroovyASTTransformationClass.class.getName())) {
          transformAnnotation = anno;
          break;
        }
      }
      if (transformAnnotation != null) {
        Expression expr = (Expression) transformAnnotation.getMember("classes");
        if (expr == null) {
          return NO_CLASSES;
        }
        Class<?>[] values = NO_CLASSES;
        // Will need to extract the classnames
        if (expr instanceof ListExpression) {
          List<Class<?>> loadedClasses = new ArrayList<Class<?>>();
          ListExpression expression = (ListExpression) expr;
          List<Expression> expressions = expression.getExpressions();
          for (Expression oneExpr : expressions) {
            String classname = ((ClassExpression) oneExpr).getType().getName();
            try {
              Class<?> clazz = Class.forName(classname, false, transformLoader);
              loadedClasses.add(clazz);
            } catch (ClassNotFoundException cnfe) {
              source
                  .getErrorCollector()
                  .addError(
                      new SimpleMessage(
                          "Ast transform processing, cannot find " + classname, source));
            }
          }
          if (loadedClasses.size() != 0) {
            values = loadedClasses.toArray(new Class<?>[loadedClasses.size()]);
          }
          return values;
        } else {

        }

        throw new RuntimeException(
            "nyi implemented in eclipse: need to support: "
                + expr
                + " (class="
                + expr.getClass()
                + ")");
      }
      return null;
    } else {
      Annotation transformClassAnnotation = getTransformClassAnnotation(classNode);
      if (transformClassAnnotation == null) {
        return null;
      }
      return getTransformClasses(transformClassAnnotation);
    }
  }
  private void callGrabAsStaticInitIfNeeded(
      ClassNode classNode,
      ClassNode grapeClassNode,
      List<Map<String, Object>> grabMapsInit,
      List<Map<String, Object>> grabExcludeMaps) {
    List<Statement> grabInitializers = new ArrayList<Statement>();
    MapExpression basicArgs = new MapExpression();
    if (autoDownload != null) {
      basicArgs.addMapEntryExpression(
          new ConstantExpression(AUTO_DOWNLOAD_SETTING), new ConstantExpression(autoDownload));
    }

    if (disableChecksums != null) {
      basicArgs.addMapEntryExpression(
          new ConstantExpression(DISABLE_CHECKSUMS_SETTING),
          new ConstantExpression(disableChecksums));
    }
    if (!grabExcludeMaps.isEmpty()) {
      ListExpression list = new ListExpression();
      for (Map<String, Object> map : grabExcludeMaps) {
        Set<Map.Entry<String, Object>> entries = map.entrySet();
        MapExpression inner = new MapExpression();
        for (Map.Entry<String, Object> entry : entries) {
          inner.addMapEntryExpression(
              new ConstantExpression(entry.getKey()), new ConstantExpression(entry.getValue()));
        }
        list.addExpression(inner);
      }
      basicArgs.addMapEntryExpression(new ConstantExpression("excludes"), list);
    }

    List<Expression> argList = new ArrayList<Expression>();
    argList.add(basicArgs);
    if (grabMapsInit.size() == 0) return;
    for (Map<String, Object> grabMap : grabMapsInit) {
      // add Grape.grab(excludeArgs, [group:group, module:module, version:version,
      // classifier:classifier])
      // or Grape.grab([group:group, module:module, version:version, classifier:classifier])
      MapExpression dependencyArg = new MapExpression();
      for (String s : GRAB_REQUIRED) {
        dependencyArg.addMapEntryExpression(
            new ConstantExpression(s), new ConstantExpression(grabMap.get(s)));
      }
      for (String s : GRAB_OPTIONAL) {
        if (grabMap.containsKey(s))
          dependencyArg.addMapEntryExpression(
              new ConstantExpression(s), new ConstantExpression(grabMap.get(s)));
      }
      argList.add(dependencyArg);
    }
    ArgumentListExpression grabArgs = new ArgumentListExpression(argList);
    grabInitializers.add(
        new ExpressionStatement(new StaticMethodCallExpression(grapeClassNode, "grab", grabArgs)));

    // insert at beginning so we have the classloader set up before the class is called
    classNode.addStaticInitializerStatements(grabInitializers, true);
  }
 /**
  * For the supplied classnode, this method will check if there is an annotation on it of kind
  * 'GroovyASTTransformationClass'. If there is then the 'value' member of that annotation will be
  * retrieved and the value considered to be the class name of a transformation.
  *
  * @return null if no annotation found, otherwise a String[] of classnames - this will be size 0
  *     if no value was specified
  */
 private String[] getTransformClassNames(ClassNode cn) {
   if (!cn.hasClass()) {
     List<AnnotationNode> annotations = cn.getAnnotations();
     AnnotationNode transformAnnotation = null;
     for (AnnotationNode anno : annotations) {
       if (anno.getClassNode().getName().equals(GroovyASTTransformationClass.class.getName())) {
         transformAnnotation = anno;
         break;
       }
     }
     if (transformAnnotation != null) {
       // will work so long as signature for the member 'value' is String[]
       Expression expr2 = transformAnnotation.getMember("value");
       String[] values = null;
       if (expr2 == null) {
         return NONE;
       }
       if (expr2 instanceof ListExpression) {
         ListExpression expression = (ListExpression) expr2;
         List<Expression> expressions = expression.getExpressions();
         values = new String[expressions.size()];
         int e = 0;
         for (Expression expr : expressions) {
           values[e++] = ((ConstantExpression) expr).getText();
         }
       } else if (expr2 instanceof ConstantExpression) {
         values = new String[1];
         values[0] = ((ConstantExpression) expr2).getText();
       } else {
         throw new IllegalStateException(
             "NYI: eclipse doesn't understand this kind of expression in an Ast transform definition: "
                 + expr2
                 + " (class="
                 + expr2.getClass().getName()
                 + ")");
       }
       return values;
     }
     return null;
   } else {
     // FIXASC check haven't broken transforms for 'vanilla' (outside of eclipse) execution of
     // groovyc
     Annotation transformClassAnnotation = getTransformClassAnnotation(cn);
     if (transformClassAnnotation == null) {
       return null;
     }
     return getTransformClassNames(transformClassAnnotation);
   }
 }
 private List<groovy.transform.PackageScopeTarget> determineTargets(Expression expr) {
   List<groovy.transform.PackageScopeTarget> list =
       new ArrayList<groovy.transform.PackageScopeTarget>();
   if (expr instanceof PropertyExpression) {
     list.add(extractTarget((PropertyExpression) expr));
   } else if (expr instanceof ListExpression) {
     final ListExpression expressionList = (ListExpression) expr;
     final List<Expression> expressions = expressionList.getExpressions();
     for (Expression ex : expressions) {
       if (ex instanceof PropertyExpression) {
         list.add(extractTarget((PropertyExpression) ex));
       }
     }
   }
   return list;
 }
Beispiel #9
0
  private Expression annotationValueToExpression(Object value) {
    if (value == null
        || value instanceof String
        || value instanceof Number
        || value instanceof Character
        || value instanceof Boolean) return new ConstantExpression(value);

    if (value instanceof Class)
      return new ClassExpression(ClassHelper.makeWithoutCaching((Class) value));

    if (value.getClass().isArray()) {
      ListExpression elementExprs = new ListExpression();
      int len = Array.getLength(value);
      for (int i = 0; i != len; ++i)
        elementExprs.addExpression(annotationValueToExpression(Array.get(value, i)));
      return elementExprs;
    }

    return null;
  }
 int[] extractLineNumberArray(AnnotationNode node) {
   ListExpression lineNumberArray = (ListExpression) node.getMember("lines");
   // make assumption that this is a simple array of constants
   List<Integer> numbers = new ArrayList<Integer>();
   for (Expression e : lineNumberArray.getExpressions()) {
     if (e instanceof ConstantExpression) {
       numbers.add((Integer) ((ConstantExpression) e).getValue());
     } else {
       numbers.add(-1);
     }
   }
   // LOG.debug "We have transformed: ${numbers}"
   if (numbers.isEmpty()) {
     return null;
   }
   int[] array = new int[numbers.size()];
   for (int i = 0, count = numbers.size(); i < count; i++) {
     array[i] = numbers.get(i);
   }
   return array;
 }
  private Expression transformInlineConstants(Expression exp) {
    if (exp instanceof PropertyExpression) {
      PropertyExpression pe = (PropertyExpression) exp;
      if (pe.getObjectExpression() instanceof ClassExpression) {
        ClassExpression ce = (ClassExpression) pe.getObjectExpression();
        ClassNode type = ce.getType();
        if (type.isEnum()) return exp;
        Expression constant = findConstant(type.getField(pe.getPropertyAsString()));
        if (constant != null) return constant;
      }
    } else if (exp instanceof ListExpression) {
      ListExpression le = (ListExpression) exp;
      ListExpression result = new ListExpression();
      for (Expression e : le.getExpressions()) {
        result.addExpression(transformInlineConstants(e));
      }
      return result;
    }

    return exp;
  }
 private Set<String> getPropertyNamesExpressedInTransientsList(final ClassNode classNode) {
   final Set<String> transientFields = new HashSet<String>();
   final FieldNode transientsField = classNode.getField("transients");
   if (transientsField != null && transientsField.isStatic()) {
     final Expression initialValueExpression = transientsField.getInitialValueExpression();
     if (initialValueExpression instanceof ListExpression) {
       final ListExpression le = (ListExpression) initialValueExpression;
       final List<Expression> expressions = le.getExpressions();
       for (Expression expr : expressions) {
         if (expr instanceof ConstantExpression) {
           final ConstantExpression ce = (ConstantExpression) expr;
           final Object contantValue = ce.getValue();
           if (contantValue instanceof String) {
             transientFields.add((String) contantValue);
           }
         }
       }
     }
   }
   return transientFields;
 }
Beispiel #13
0
 private void configureAnnotation(AnnotationNode node, Annotation annotation) {
   Class type = annotation.annotationType();
   if (type == Retention.class) {
     Retention r = (Retention) annotation;
     RetentionPolicy value = r.value();
     setRetentionPolicy(value, node);
     node.setMember(
         "value",
         new PropertyExpression(
             new ClassExpression(ClassHelper.makeWithoutCaching(RetentionPolicy.class, false)),
             value.toString()));
   } else if (type == Target.class) {
     Target t = (Target) annotation;
     ElementType[] elements = t.value();
     ListExpression elementExprs = new ListExpression();
     for (ElementType element : elements) {
       elementExprs.addExpression(
           new PropertyExpression(
               new ClassExpression(ClassHelper.ELEMENT_TYPE_TYPE), element.name()));
     }
     node.setMember("value", elementExprs);
   } else {
     Method[] declaredMethods;
     try {
       declaredMethods = type.getDeclaredMethods();
     } catch (SecurityException se) {
       declaredMethods = new Method[0];
     }
     for (Method declaredMethod : declaredMethods) {
       try {
         Object value = declaredMethod.invoke(annotation);
         Expression valueExpression = annotationValueToExpression(value);
         if (valueExpression == null) continue;
         node.setMember(declaredMethod.getName(), valueExpression);
       } catch (IllegalAccessException e) {
       } catch (InvocationTargetException e) {
       }
     }
   }
 }
  private void addDefaultDatabindingWhitelistField(
      final SourceUnit sourceUnit, final ClassNode classNode) {
    final FieldNode defaultWhitelistField =
        classNode.getDeclaredField(DEFAULT_DATABINDING_WHITELIST);
    if (defaultWhitelistField == null) {
      final Set<String> propertyNamesToIncludeInWhiteList =
          getPropertyNamesToIncludeInWhiteList(sourceUnit, classNode);

      final ListExpression listExpression = new ListExpression();
      for (String propertyName : propertyNamesToIncludeInWhiteList) {
        listExpression.addExpression(new ConstantExpression(propertyName));

        final FieldNode declaredField = classNode.getDeclaredField(propertyName);
        if (declaredField != null) {
          final ClassNode type = declaredField.getType();
          if (type != null && !SIMPLE_TYPES.contains(type)) {
            if (STRUCTURED_EDITOR_TYPES.contains(type)) {
              listExpression.addExpression(new ConstantExpression(propertyName + "_*"));
            } else {
              String packageName = type.getPackageName();
              if (packageName != null && packageName.startsWith(JODATIME_PACKAGE)) {
                listExpression.addExpression(new ConstantExpression(propertyName + "_*"));
              } else {
                listExpression.addExpression(new ConstantExpression(propertyName + ".*"));
              }
            }
          }
        }
      }

      classNode.addField(
          DEFAULT_DATABINDING_WHITELIST,
          Modifier.STATIC | Modifier.PUBLIC | Modifier.FINAL,
          new ClassNode(List.class),
          listExpression);
    }
  }
 private String getAnnotationValue(Object memberValue) {
   String val = "null";
   if (memberValue instanceof ListExpression) {
     StringBuilder sb = new StringBuilder("{");
     boolean first = true;
     ListExpression le = (ListExpression) memberValue;
     for (Expression e : le.getExpressions()) {
       if (first) first = false;
       else sb.append(",");
       sb.append(getAnnotationValue(e));
     }
     sb.append("}");
     val = sb.toString();
   } else if (memberValue instanceof ConstantExpression) {
     ConstantExpression ce = (ConstantExpression) memberValue;
     Object constValue = ce.getValue();
     if (constValue instanceof AnnotationNode) {
       StringWriter writer = new StringWriter();
       PrintWriter out = new PrintWriter(writer);
       printAnnotation(out, (AnnotationNode) constValue);
       val = writer.toString();
     } else if (constValue instanceof Number || constValue instanceof Boolean)
       val = constValue.toString();
     else val = "\"" + escapeSpecialChars(constValue.toString()) + "\"";
   } else if (memberValue instanceof PropertyExpression
       || memberValue instanceof VariableExpression) {
     // assume must be static class field or enum value or class that Java can resolve
     val = ((Expression) memberValue).getText();
   } else if (memberValue instanceof ClosureExpression) {
     // annotation closure; replaced with this specific class literal to cover the
     // case where annotation type uses Class<? extends Closure> for the closure's type
     val = "groovy.lang.Closure.class";
   } else if (memberValue instanceof ClassExpression) {
     val = ((Expression) memberValue).getText() + ".class";
   }
   return val;
 }
  private List<String> getKnownImmutables(AnnotationNode node) {
    final ArrayList<String> immutables = new ArrayList<String>();

    final Expression expression = node.getMember(MEMBER_KNOWN_IMMUTABLES);
    if (expression == null) return immutables;

    if (!(expression instanceof ListExpression)) {
      addError(
          "Use the Groovy list notation [el1, el2] to specify known immutable property names via \""
              + MEMBER_KNOWN_IMMUTABLES
              + "\"",
          node);
      return immutables;
    }

    final ListExpression listExpression = (ListExpression) expression;
    for (Expression listItemExpression : listExpression.getExpressions()) {
      if (listItemExpression instanceof ConstantExpression) {
        immutables.add((String) ((ConstantExpression) listItemExpression).getValue());
      }
    }

    return immutables;
  }
    @Override
    public void visitListExpression(ListExpression expression) {
      // LOG.debug "Transforming expression '${expression}':"

      if (expression.getLineNumber() >= 0 && expression.getLineNumber() < lineNumbers.length) {
        // LOG.debug "   start from ${expression.lineNumber} to ${lineNumbers[expression.lineNumber
        // - 1]}"
        expression.setLineNumber(lineNumbers[expression.getLineNumber() - 1]);
      }

      if (expression.getLastLineNumber() > 0
          && expression.getLastLineNumber() < lineNumbers.length) {
        // LOG.debug "   end from ${expression.lastLineNumber} to
        // ${lineNumbers[expression.lastLineNumber - 1]}"
        expression.setLastLineNumber(lineNumbers[expression.getLastLineNumber() - 1]);
      }
      super.visitListExpression(expression);
    }
 protected void weaveMixinClass(ClassNode classNode, Class mixinClass) {
   ListExpression listExpression = new ListExpression();
   listExpression.addExpression(new ClassExpression(new ClassNode(mixinClass)));
   weaveMixinsIntoClass(classNode, listExpression);
 }
  public void evaluateEqual(BinaryExpression expression, boolean defineVariable) {
    AsmClassGenerator acg = controller.getAcg();
    CompileStack compileStack = controller.getCompileStack();
    OperandStack operandStack = controller.getOperandStack();
    Expression rightExpression = expression.getRightExpression();
    Expression leftExpression = expression.getLeftExpression();
    ClassNode lhsType =
        controller.getTypeChooser().resolveType(leftExpression, controller.getClassNode());

    if (defineVariable
        && rightExpression instanceof EmptyExpression
        && !(leftExpression instanceof TupleExpression)) {
      VariableExpression ve = (VariableExpression) leftExpression;
      BytecodeVariable var =
          compileStack.defineVariable(
              ve, controller.getTypeChooser().resolveType(ve, controller.getClassNode()), false);
      operandStack.loadOrStoreVariable(var, false);
      return;
    }

    // let's evaluate the RHS and store the result
    ClassNode rhsType;
    if (rightExpression instanceof ListExpression && lhsType.isArray()) {
      ListExpression list = (ListExpression) rightExpression;
      ArrayExpression array =
          new ArrayExpression(lhsType.getComponentType(), list.getExpressions());
      array.setSourcePosition(list);
      array.visit(acg);
    } else if (rightExpression instanceof EmptyExpression) {
      rhsType = leftExpression.getType();
      loadInitValue(rhsType);
    } else {
      rightExpression.visit(acg);
    }
    rhsType = operandStack.getTopOperand();

    boolean directAssignment = defineVariable && !(leftExpression instanceof TupleExpression);
    int rhsValueId;
    if (directAssignment) {
      VariableExpression var = (VariableExpression) leftExpression;
      if (var.isClosureSharedVariable() && ClassHelper.isPrimitiveType(rhsType)) {
        // GROOVY-5570: if a closure shared variable is a primitive type, it must be boxed
        rhsType = ClassHelper.getWrapper(rhsType);
        operandStack.box();
      }

      // ensure we try to unbox null to cause a runtime NPE in case we assign
      // null to a primitive typed variable, even if it is used only in boxed
      // form as it is closure shared
      if (var.isClosureSharedVariable()
          && ClassHelper.isPrimitiveType(var.getOriginType())
          && isNull(rightExpression)) {
        operandStack.doGroovyCast(var.getOriginType());
        // these two are never reached in bytecode and only there
        // to avoid verifyerrors and compiler infrastructure hazzle
        operandStack.box();
        operandStack.doGroovyCast(lhsType);
      }
      // normal type transformation
      if (!ClassHelper.isPrimitiveType(lhsType) && isNull(rightExpression)) {
        operandStack.replace(lhsType);
      } else {
        operandStack.doGroovyCast(lhsType);
      }
      rhsType = lhsType;
      rhsValueId = compileStack.defineVariable(var, lhsType, true).getIndex();
    } else {
      rhsValueId = compileStack.defineTemporaryVariable("$rhs", rhsType, true);
    }
    // TODO: if rhs is VariableSlotLoader already, then skip crating a new one
    BytecodeExpression rhsValueLoader = new VariableSlotLoader(rhsType, rhsValueId, operandStack);

    // assignment for subscript
    if (leftExpression instanceof BinaryExpression) {
      BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
      if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
        assignToArray(
            expression,
            leftBinExpr.getLeftExpression(),
            leftBinExpr.getRightExpression(),
            rhsValueLoader);
      }
      compileStack.removeVar(rhsValueId);
      return;
    }

    compileStack.pushLHS(true);

    // multiple declaration
    if (leftExpression instanceof TupleExpression) {
      TupleExpression tuple = (TupleExpression) leftExpression;
      int i = 0;
      for (Expression e : tuple.getExpressions()) {
        VariableExpression var = (VariableExpression) e;
        MethodCallExpression call =
            new MethodCallExpression(
                rhsValueLoader, "getAt", new ArgumentListExpression(new ConstantExpression(i)));
        call.visit(acg);
        i++;
        if (defineVariable) {
          operandStack.doGroovyCast(var);
          compileStack.defineVariable(var, true);
          operandStack.remove(1);
        } else {
          acg.visitVariableExpression(var);
        }
      }
    }
    // single declaration
    else if (defineVariable) {
      rhsValueLoader.visit(acg);
      operandStack.remove(1);
      compileStack.popLHS();
      return;
    }
    // normal assignment
    else {
      int mark = operandStack.getStackLength();
      // to leave a copy of the rightExpression value on the stack after the assignment.
      rhsValueLoader.visit(acg);
      TypeChooser typeChooser = controller.getTypeChooser();
      ClassNode targetType = typeChooser.resolveType(leftExpression, controller.getClassNode());
      operandStack.doGroovyCast(targetType);
      leftExpression.visit(acg);
      operandStack.remove(operandStack.getStackLength() - mark);
    }
    compileStack.popLHS();

    // return value of assignment
    rhsValueLoader.visit(acg);
    compileStack.removeVar(rhsValueId);
  }
 public void visitListExpression(ListExpression expression) {
   visitListOfExpressions(expression.getExpressions());
 }