<commit_before>/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.j2cl.transpiler.ast;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;

import java.util.ArrayList;
import java.util.List;

/**
 * Utility functions for expanding assignment, increment and decrement expressions.
 *
 * <p>For example:
 *
 * <ul>
 *   <li>i++ becomes i = i + 1
 *   <li>a = i++ becomes a = (v = i, i = i + 1, v)
 *   <li>a = change().i++ becomes a = (q = change(), v = q.i, q.i = q.i + 1, v)
 * </ul>
 *
 * <p>This kind of rewrite is useful when the numeric type in question is represented by an
 * immutable emulation class or when a conversion operation (such as boxing or unboxing) needs to be
 * inserted between the numeric and assignment stages of an operation.
 */
public class OperationExpansionUtils {
  public static Expression expandCompoundExpression(BinaryExpression binaryExpression) {
    checkArgument(binaryExpression.getOperator().isCompoundAssignment());

    BinaryOperator operator = binaryExpression.getOperator();
    Expression leftOperand = binaryExpression.getLeftOperand();
    Expression rightOperand = binaryExpression.getRightOperand();

    List<VariableDeclarationFragment> temporaryVariableDeclarations = new ArrayList<>();
    Expression lhs =
        leftOperand.isIdempotent()
            ? leftOperand
            : decomposeLhs(leftOperand, temporaryVariableDeclarations);

    return constructReturnedExpression(
        temporaryVariableDeclarations,
        assignToLeftOperand(lhs, operator.getUnderlyingBinaryOperator(), rightOperand));
  }

  /**
   * Decomposes the lhs of a compound assignment (or the operand of a prefix/postfix expressions
   * introducing temporary variables if necessary.
   */
  private static Expression decomposeLhs(
      Expression lhs, List<VariableDeclarationFragment> temporaryVariableDeclarations) {
    if (lhs instanceof VariableReference) {
      // The lhs will be modified but it can be safely evaluated twice in a row without caring to
      // avoid double side-effects if expanded. See the counter example showing an incorrect
      // rewrite:
      //     a[++i] += i => a[++i] = a[++i] + x
      return lhs;
    }

    if (lhs instanceof ArrayAccess) {
      return decomposeArrayAccess((ArrayAccess) lhs, temporaryVariableDeclarations);
    }
    checkState(lhs instanceof FieldAccess);
    return decomposeFieldAccess((FieldAccess) lhs, temporaryVariableDeclarations);
  }

  private static FieldAccess decomposeFieldAccess(
      FieldAccess lhs, List<VariableDeclarationFragment> temporaryVariableDeclarations) {

    if (lhs.getTarget().isStatic()) {
      // The qualifier here should not be extracted since it is a constructor reference.
      //
      // Note that checking idempotence of the qualifier is not correct since it might still be
      // affected by the side effects in the rhs, e.g.
      //
      //      SomeClass a = foo;
      //      a.result = (a = bar).toString()
      //
      // if this was not decomposed (because 'a' is idempotent), the above snippet would modify
      // the instance 'foo' but return the value of 'bar.result' instead of 'foo.result' (see
      // expandAssignmentExpression).
      // What ensures correctness here without a rewrite is the qualifier being constant - not
      // being idempotent.
      return lhs;
    }

    // The qualifier is not guaranteed to be free of side effects, so evaluate it first and assign
    // to a temporary variable.
    Expression qualifier = lhs.getQualifier();
    Variable qualifierVariable =<commit_msg>Slightly improve the example in decomposeFieldAccess.<commit_after>/*
 * Copyright 2015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.j2cl.transpiler.ast;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;

import java.util.ArrayList;
import java.util.List;

/**
 * Utility functions for expanding assignment, increment and decrement expressions.
 *
 * <p>For example:
 *
 * <ul>
 *   <li>i++ becomes i = i + 1
 *   <li>a = i++ becomes a = (v = i, i = i + 1, v)
 *   <li>a = change().i++ becomes a = (q = change(), v = q.i, q.i = q.i + 1, v)
 * </ul>
 *
 * <p>This kind of rewrite is useful when the numeric type in question is represented by an
 * immutable emulation class or when a conversion operation (such as boxing or unboxing) needs to be
 * inserted between the numeric and assignment stages of an operation.
 */
public class OperationExpansionUtils {
  public static Expression expandCompoundExpression(BinaryExpression binaryExpression) {
    checkArgument(binaryExpression.getOperator().isCompoundAssignment());

    BinaryOperator operator = binaryExpression.getOperator();
    Expression leftOperand = binaryExpression.getLeftOperand();
    Expression rightOperand = binaryExpression.getRightOperand();

    List<VariableDeclarationFragment> temporaryVariableDeclarations = new ArrayList<>();
    Expression lhs =
        leftOperand.isIdempotent()
            ? leftOperand
            : decomposeLhs(leftOperand, temporaryVariableDeclarations);

    return constructReturnedExpression(
        temporaryVariableDeclarations,
        assignToLeftOperand(lhs, operator.getUnderlyingBinaryOperator(), rightOperand));
  }

  /**
   * Decomposes the lhs of a compound assignment (or the operand of a prefix/postfix expressions
   * introducing temporary variables if necessary.
   */
  private static Expression decomposeLhs(
      Expression lhs, List<VariableDeclarationFragment> temporaryVariableDeclarations) {
    if (lhs instanceof VariableReference) {
      // The lhs will be modified but it can be safely evaluated twice in a row without caring to
      // avoid double side-effects if expanded. See the counter example showing an incorrect
      // rewrite:
      //     a[++i] += i => a[++i] = a[++i] + x
      return lhs;
    }

    if (lhs instanceof ArrayAccess) {
      return decomposeArrayAccess((ArrayAccess) lhs, temporaryVariableDeclarations);
    }
    checkState(lhs instanceof FieldAccess);
    return decomposeFieldAccess((FieldAccess) lhs, temporaryVariableDeclarations);
  }

  private static FieldAccess decomposeFieldAccess(
      FieldAccess lhs, List<VariableDeclarationFragment> temporaryVariableDeclarations) {

    if (lhs.getTarget().isStatic()) {
      // The qualifier here should not be extracted since it is a constructor reference.
      //
      // Note that checking idempotence of the qualifier is not correct since it might still be
      // affected by the side effects in the rhs, e.g.
      //
      //      SomeClass a = foo;
      //      String rv = a.result = (a = bar).toString();
      //
      // if this was not decomposed (because 'a' is idempotent), the above snippet would modify
      // the instance 'foo' but return the value of 'bar.result' instead of 'foo.result' (see
      // expandAssignmentExpression).
      // What ensures correctness here without a rewrite is the qualifier being constant - not
      // being idempotent.
      return lhs;
    }

    // The qualifier is not guaranteed to be free of side effects, so evaluate it first and assign
    // to a temporary variable.
    Expression qualifier = lhs.getQualifier();
    Variable qualifierVariable =
{'fraction_non_alphanumeric': 0.08493797991730656, 'fraction_numerical': 0.002835203780271707, 'mean_word_length': 3.409375, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4047202', 'n_tokens_mistral': 2233, 'n_tokens_neox': 2178, 'n_words': 845}