<commit_before>/*
 * Copyright 2011-2015 Asakusa Framework Team.
 *
 * 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.asakusafw.spark.tools.asm

import java.lang.{
  Boolean => JBoolean,
  Character => JChar,
  Byte => JByte,
  Short => JShort,
  Integer => JInt,
  Long => JLong,
  Float => JFloat,
  Double => JDouble
}
import java.lang.invoke.CallSite
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
import java.util.Arrays

import scala.reflect.ClassTag

import org.objectweb.asm.Handle
import org.objectweb.asm.Label
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes._
import org.objectweb.asm.Type

class MethodBuilder(thisType: Type, private[MethodBuilder] val mv: MethodVisitor) {

  import MethodBuilder._ // scalastyle:ignore

  val thisVar: Var = {
    new Var(this, thisType, 0)
  }

  def `var`(`type`: Type, local: Int): Var = { // scalastyle:ignore
    new Var(this, `type`, local)
  }

  def `return`(stack: Stack): Unit = { // scalastyle:ignore
    mv.visitInsn(stack.`type`.getOpcode(IRETURN))
  }

  def `return`(): Unit = { // scalastyle:ignore
    mv.visitInsn(RETURN)
  }

  def `throw`(stack: Stack): Unit = { // scalastyle:ignore
    mv.visitInsn(ATHROW)
  }

  def pushNew(`type`: Type): Stack = {
    mv.visitTypeInsn(NEW, `type`.getInternalName())
    new Stack(this, `type`)
  }

  def pushNew0(`type`: Type): Stack = {
    val stack = pushNew(`type`)
    stack.dup().invokeInit()
    stack
  }

  def pushNewArray(`type`: Type, size: Int): Stack = {
    pushNewArray(`type`, ldc(size))
  }

  def pushNewArray(`type`: Type, size: Stack): Stack = {
    `type`.getSort() match {
      case Type.BOOLEAN =>
        mv.visitIntInsn(NEWARRAY, T_BOOLEAN)
      case Type.CHAR =>
        mv.visitIntInsn(NEWARRAY, T_CHAR)
      case Type.BYTE =>
        mv.visitIntInsn(NEWARRAY, T_BYTE)
      case Type.SHORT =>
        mv.visitIntInsn(NEWARRAY, T_SHORT)
      case Type.INT =>
        mv.visitIntInsn(NEWARRAY, T_INT)
      case Type.LONG =>
        mv.visitIntInsn(NEWARRAY, T_LONG)
      case Type.FLOAT =>
        mv.visitIntInsn(NEWARRAY, T_FLOAT)
      case Type.DOUBLE =>
        mv.visitIntInsn(NEWARRAY, T_DOUBLE)
      case _ =>
        mv.visitTypeInsn(ANEWARRAY, `type`.getInternalName())
    }
    new Stack(this, Type.getType("[" + `type`.getDescriptor()))
  }

  def pushNewMultiArray(`type`: Type, d1size: Int, sizes: Int*): Stack = {
    pushNewMultiArray(`type`, ldc(d1size), sizes.map(ldc(_)): _*)
  }

  def pushNewMultiArray(`type`: Type, d1size: Stack, sizes: Stack*): Stack = {
    mv.visitMultiANewArrayInsn(`type`.getInternalName(), sizes.size + 1)
    new Stack(this, Type.getType(("[" * (sizes.size + 1)) + `type`.getDescriptor()))
  }

  def pushNull(`type`: Type): Stack = {
    mv.visitInsn(ACONST_NULL)
    new Stack(this, `type`.boxed)
  }

  def ldc[A <: Any: ClassTag](ldc: A): Stack = {
    val `type` = Type.getType(implicitly[ClassTag[A]].runtimeClass)
    `type`.getSort() match {
      case Type.BOOLEAN =>
        if (ldc.asInstanceOf[Boolean]) {
          mv.visitInsn(ICONST_1)
        } else {
          mv.visitInsn(ICONST_0)
        }
      case Type.CHAR =>
        ldc.asInstanceOf[Char].toInt match {
          case i if i <= 5 => mv.visitInsn(ICONST_0 + i)
          case i if i <= Byte.MaxValue => mv.visitIntInsn(BIPUSH, i)
          case i if i <= Short.MaxValue => mv.visitIntInsn(SIPUSH, i)
          case i => mv.visitLdcInsn(i)
        }
      case Type.BYTE =>
        ldc.asInstanceOf[Byte] match {
          case i if i >= -1 && i <= 5 => mv.visitInsn(ICONST_0 + i)
          case i => mv.visitIntInsn(BIPUSH, i)
        }
      case Type.SHORT =>
        ldc.asInstanceOf[Short] match {
          case i if i >= -1 && i <= 5 => mv.visitInsn(ICONST_0 + i)
          case i if i >= Byte.MinValue && i <= Byte.MaxValue => mv.visitIntInsn(BIPUSH, i)
          case i => mv.visitIntInsn(SIPUSH, i)
        }
      case Type.INT =>
        ldc.asInstanceOf[Int] match {
          case i if i >= -1 && i <= 5 => mv.visitInsn(ICONST_0 + i)
          case i if i >= Byte.MinValue && i <= Byte.MaxValue => mv.visitIntInsn(BIPUSH, i)
          case i if i >= Short.MinValue && i <= Short.MaxValue => mv.visitIntInsn(SIPUSH, i)
          case i => mv.visitLdcInsn(i)
        }
      case Type.LONG =>
        ldc.asInstanceOf[Long] match {
          case 0L => mv.visitInsn(LCONST_0)
          case 1L => mv.visitInsn(LCONST_1)
          case l => mv.visitLdcInsn(l)
        }
      case Type.FLOAT =>
        ldc.asInstanceOf[Float] match {
          case 0.0f => mv.visitInsn(FCONST_0)
          case 1.0f => mv.visitInsn(FCONST_1)
          case 2.0f => mv.visitInsn(FCONST_2)
          case f => mv.visitLdcInsn(f)
        }
      case Type.DOUBLE =>
        ldc.asInstanceOf[Double] match {
          case 0.0d => mv.visitInsn(DCONST_0)
          case 1.0d => mv.visitInsn(DCONST_1)
          case d => mv.visitLdcInsn(d)
        }
      case _ =>
        mv.visitLdcInsn(ldc)
    }
    new Stack(this, `type`)
  }

  def invokeDynamic(
    name: String,
    bootstrapType: Type,
    bootstrapMethod: String,
    bootstrapArguments: Seq[(Class[_], AnyRef)],
    retType: Type,
    arguments: Stack*): Stack = {

    val mt = MethodType.methodType(
      classOf[CallSite],
      Array(classOf[MethodHandles.Lookup], classOf[String], classOf[MethodType]) ++ bootstrapArguments.map(_._1))
    val bootstrap = new Handle(H_INVOKESTATIC, bootstrapType.getInternalName(), bootstrapMethod, mt.toMethodDescriptorString())
    mv.visitInvokeDynamicInsn(
      name,
      Type.getMethodDescriptor(
        retType,<commit_msg>Refactor MethodBuilder to simplify ldc method.<commit_after>/*
 * Copyright 2011-2015 Asakusa Framework Team.
 *
 * 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.asakusafw.spark.tools.asm

import java.lang.{
  Boolean => JBoolean,
  Character => JChar,
  Byte => JByte,
  Short => JShort,
  Integer => JInt,
  Long => JLong,
  Float => JFloat,
  Double => JDouble
}
import java.lang.invoke.CallSite
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
import java.util.Arrays

import scala.reflect.ClassTag

import org.objectweb.asm.Handle
import org.objectweb.asm.Label
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes._
import org.objectweb.asm.Type

class MethodBuilder(thisType: Type, private[MethodBuilder] val mv: MethodVisitor) {

  import MethodBuilder._ // scalastyle:ignore

  val thisVar: Var = {
    new Var(this, thisType, 0)
  }

  def `var`(`type`: Type, local: Int): Var = { // scalastyle:ignore
    new Var(this, `type`, local)
  }

  def `return`(stack: Stack): Unit = { // scalastyle:ignore
    mv.visitInsn(stack.`type`.getOpcode(IRETURN))
  }

  def `return`(): Unit = { // scalastyle:ignore
    mv.visitInsn(RETURN)
  }

  def `throw`(stack: Stack): Unit = { // scalastyle:ignore
    mv.visitInsn(ATHROW)
  }

  def pushNew(`type`: Type): Stack = {
    mv.visitTypeInsn(NEW, `type`.getInternalName())
    new Stack(this, `type`)
  }

  def pushNew0(`type`: Type): Stack = {
    val stack = pushNew(`type`)
    stack.dup().invokeInit()
    stack
  }

  def pushNewArray(`type`: Type, size: Int): Stack = {
    pushNewArray(`type`, ldc(size))
  }

  def pushNewArray(`type`: Type, size: Stack): Stack = {
    `type`.getSort() match {
      case Type.BOOLEAN =>
        mv.visitIntInsn(NEWARRAY, T_BOOLEAN)
      case Type.CHAR =>
        mv.visitIntInsn(NEWARRAY, T_CHAR)
      case Type.BYTE =>
        mv.visitIntInsn(NEWARRAY, T_BYTE)
      case Type.SHORT =>
        mv.visitIntInsn(NEWARRAY, T_SHORT)
      case Type.INT =>
        mv.visitIntInsn(NEWARRAY, T_INT)
      case Type.LONG =>
        mv.visitIntInsn(NEWARRAY, T_LONG)
      case Type.FLOAT =>
        mv.visitIntInsn(NEWARRAY, T_FLOAT)
      case Type.DOUBLE =>
        mv.visitIntInsn(NEWARRAY, T_DOUBLE)
      case _ =>
        mv.visitTypeInsn(ANEWARRAY, `type`.getInternalName())
    }
    new Stack(this, Type.getType("[" + `type`.getDescriptor()))
  }

  def pushNewMultiArray(`type`: Type, d1size: Int, sizes: Int*): Stack = {
    pushNewMultiArray(`type`, ldc(d1size), sizes.map(ldc(_)): _*)
  }

  def pushNewMultiArray(`type`: Type, d1size: Stack, sizes: Stack*): Stack = {
    mv.visitMultiANewArrayInsn(`type`.getInternalName(), sizes.size + 1)
    new Stack(this, Type.getType(("[" * (sizes.size + 1)) + `type`.getDescriptor()))
  }

  def pushNull(`type`: Type): Stack = {
    mv.visitInsn(ACONST_NULL)
    new Stack(this, `type`.boxed)
  }

  def ldc[A <: Any: ClassTag](value: A): Stack = {
    val `type` = Type.getType(implicitly[ClassTag[A]].runtimeClass)
    `type`.getSort() match {
      case Type.BOOLEAN =>
        ldc(value.asInstanceOf[Boolean])
      case Type.CHAR =>
        ldc(value.asInstanceOf[Char])
      case Type.BYTE =>
        ldc(value.asInstanceOf[Byte])
      case Type.SHORT =>
        ldc(value.asInstanceOf[Short])
      case Type.INT =>
        ldc(value.asInstanceOf[Int])
      case Type.LONG =>
        ldc(value.asInstanceOf[Long])
      case Type.FLOAT =>
        ldc(value.asInstanceOf[Float])
      case Type.DOUBLE =>
        ldc(value.asInstanceOf[Double])
      case _ =>
        mv.visitLdcInsn(value)
        new Stack(this, `type`)
    }
  }

  def ldc(z: Boolean): Stack = {
    if (z) {
      mv.visitInsn(ICONST_1)
    } else {
      mv.visitInsn(ICONST_0)
    }
    new Stack(this, Type.BOOLEAN_TYPE)
  }

  def ldc(c: Char): Stack = {
    if (c <= 5) {
      mv.visitInsn(ICONST_0 + c)
    } else if (c <= Byte.MaxValue) {
      mv.visitIntInsn(BIPUSH, c)
    } else if (c <= Short.MaxValue) {
      mv.visitIntInsn(SIPUSH, c)
    } else {
      mv.visitLdcInsn(c)
    }
    new Stack(this, Type.CHAR_TYPE)
  }

  def ldc(b: Byte): Stack = {
    if (b >= -1 && b <= 5) {
      mv.visitInsn(ICONST_0 + b)
    } else {
      mv.visitIntInsn(BIPUSH, b)
    }
    new Stack(this, Type.BYTE_TYPE)
  }

  def ldc(s: Short): Stack = {
    if (s >= -1 && s <= 5) {
      mv.visitInsn(ICONST_0 + s)
    } else if (s >= Byte.MinValue && s <= Byte.MaxValue) {
      mv.visitIntInsn(BIPUSH, s)
    } else {
      mv.visitIntInsn(SIPUSH, s)
    }
    new Stack(this, Type.SHORT_TYPE)
  }

  def ldc(i: Int): Stack = {
    if (i >= -1 && i <= 5) {
      mv.visitInsn(ICONST_0 + i)
    } else if (i >= Byte.MinValue && i <= Byte.MaxValue) {
      mv.visitIntInsn(BIPUSH, i)
    } else if (i >= Short.MinValue && i <= Short.MaxValue) {
      mv.visitIntInsn(SIPUSH, i)
    } else {
      mv.visitLdcInsn(i)
    }
    new Stack(this, Type.INT_TYPE)
  }

  def ldc(j: Long): Stack = {
    if (j == 0L) {
      mv.visitInsn(LCONST_0)
    } else if (j == 1L) {
      mv.visitInsn(LCONST_1)
    } else {
      mv.visitLdcInsn(j)
    }
    new Stack(this, Type.LONG_TYPE)
  }

  def ldc(f: Float): Stack = {
    if (f == 0.0f) {
      mv.visitInsn(FCONST_0)
    } else if (f == 1.0f) {
      mv.visitInsn(FCONST_1)
    } else if (f == 2.0f) {
      mv.visitInsn(FCONST_2)
    } else {
      mv.visitLdcInsn(f)
    }
    new Stack(this, Type.FLOAT_TYPE)
  }

  def ldc(d: Double): Stack = {
    if (d == 0.0d) {
      mv.visitInsn(DCONST_0)
    } else if (d == 1.0d) {
      mv.visitInsn(DCONST_1)
    } else {
      mv.visitLdcInsn(d)
    }
    new Stack(this, Type.DOUBLE_TYPE)
  }

  def invokeDynamic(
    name: String,
    bootstrapType: Type,
    bootstrapMethod: String,
    bootstrapArguments: Seq[(Class[_], AnyRef)],
    retType: Type,
    arguments: Stack*): Stack = {

    val mt = MethodType.methodType(
      classOf[CallSite],
      Array(classOf[MethodHandles.Lookup], classOf[String], classOf[MethodType]) ++ bootstrapArguments.map(_._1))
    val bootstrap = new Handle(H_INVOKESTATIC, bootstrapType.getInternalName(), bootstrapMethod, mt.toMethodDescriptorString())
    mv.visitInvokeDynamicInsn(
      name,
      Type.getMethodDescriptor(
        retType,
{'fraction_non_alphanumeric': 0.1370350320934189, 'fraction_numerical': 0.00804268811383497, 'mean_word_length': 2.7278754684347075, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 90, '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': '1920869', 'n_tokens_mistral': 4723, 'n_tokens_neox': 4539, 'n_words': 1011}