<commit_before># Copyright (c) 2012 The Mirah project authors. All Rights Reserved.
# All contributing project authors may be found in the NOTICE file.
#
# 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 org.mirah.jvm.compiler

import java.util.Arrays
import java.util.List
import java.util.logging.Logger
import mirah.lang.ast.*
import org.mirah.jvm.types.JVMType
import org.mirah.jvm.types.JVMMethod
import org.mirah.jvm.types.MemberVisitor
import org.mirah.util.Context

import org.jruby.org.objectweb.asm.ClassWriter
import org.jruby.org.objectweb.asm.Opcodes
import org.jruby.org.objectweb.asm.Type
import org.jruby.org.objectweb.asm.commons.GeneratorAdapter
import org.jruby.org.objectweb.asm.commons.Method


class CallCompiler < BaseCompiler implements MemberVisitor
  def self.initialize:void
    @@log = Logger.getLogger(ClassCompiler.class.getName)
  end
  def initialize(compiler:BaseCompiler, bytecode:Bytecode, position:Position, target:Node, name:String, args:NodeList, returnType:JVMType)
    initialize(compiler, bytecode, position, target, name, returnType)
    @args = Node[args.size]
    args.size.times {|i| @args[i] = args.get(i)}
  end
  def initialize(compiler:BaseCompiler, bytecode:Bytecode, position:Position, target:Node, name:String, args:List, returnType:JVMType)
    initialize(compiler, bytecode, position, target, name, returnType)
    @args = Node[args.size]
    args.toArray(@args)
  end
  
  # TODO: private
  def initialize(compiler:BaseCompiler, bytecode:Bytecode, position:Position, target:Node, name:String, returnType:JVMType)
    super(compiler.context)
    @compiler = compiler
    @method = bytecode
    @position = position
    @target = target
    @name = name
    @returnType = returnType
  end
  
  def compile(expression:boolean):void
    getMethod.accept(self, expression)
  end
  
  def getMethod
    @member ||= begin
      argTypes = JVMType[@args.length]
      @args.length.times do |i|
        argTypes[i] = getInferredType(@args[i])
      end
      getInferredType(@target).getMethod(@name, Arrays.asList(argTypes))
    end
  end
  
  def recordPosition:void
    @method.recordPosition(@position)
  end
  
  # private
  def compile(node:Node):void
    @compiler.visit(node, Boolean.TRUE)
  end
  
  def convertArgs(argumentTypes:List):void
    argumentTypes.size.times do |i|
      arg = @args[i]
      compile(arg)
      @method.convertValue(getInferredType(arg), JVMType(argumentTypes[i]))
    end
  end

  def convertResult(returnedType:JVMType, expression:boolean):void
    if expression
      @method.convertValue(returnedType, @returnType)
      # TODO casts for generic method calls
    else
      @method.pop(returnedType)
    end
  end

  def computeMathOp(name:String):int
    name = name.intern
    if name == '+'
      GeneratorAdapter.ADD
    elsif name == '&'
      GeneratorAdapter.AND
    elsif name == '/'
      GeneratorAdapter.DIV
    elsif name == '*'
      GeneratorAdapter.MUL
    elsif name == '-@'
      GeneratorAdapter.NEG
    elsif name == '|'
      GeneratorAdapter.OR
    elsif name == '%'
      GeneratorAdapter.REM
    elsif name == '<<'
      GeneratorAdapter.SHL
    elsif name =='>>'
      GeneratorAdapter.SHR
    elsif name == '-'
      GeneratorAdapter.SUB
    elsif name == '>>>'
      GeneratorAdapter.USHR
    elsif name == '^'
      GeneratorAdapter.XOR
    else
      raise IllegalArgumentException, "Unsupported operator #{name}"
    end
  end

  def visitMath(method:JVMMethod, expression:boolean)
    op = computeMathOp(method.name)
    type = method.returnType
    asm_type = type.getAsmType
    compile(@target)
    @method.convertValue(getInferredType(@target), type)
    convertArgs([type])
    recordPosition
    @method.math(op, asm_type)
    convertResult(type, expression)
  end
  
  def self.computeComparisonOp(name:String):int
    name = name.intern
    if name == '=='
      GeneratorAdapter.EQ
    elsif name == '>='
      GeneratorAdapter.GE
    elsif name == '>'
      GeneratorAdapter.GT
    elsif name == '<='
      GeneratorAdapter.LE
    elsif name == '<'
      GeneratorAdapter.LT
    elsif name == '!='
      GeneratorAdapter.NE
    else
      raise IllegalArgumentException, "Unsupported comparison #{name}"
    end
  end
  
  def compileComparisonValues(method:JVMMethod)
    type = method.declaringClass
    compile(@target)
    @method.convertValue(getInferredType(@target), type)
    convertArgs([type])
  end
  
  def visitComparison(method:JVMMethod, expression:boolean)
    compileComparisonValues(method)

    op = CallCompiler.computeComparisonOp(method.name)
    type = method.declaringClass
    ifTrue = @method.newLabel
    done = @method.newLabel
    @method.ifCmp(type.getAsmType, op, ifTrue)
    @method.push(0)
    @method.goTo(done)
    @method.mark(ifTrue)
    @method.push(1)
    @method.mark(done)
    @method.pop unless expression
  end
  
  def visitMethodCall(method:JVMMethod, expression:boolean)
    isVoid = method.returnType.getAsmType.getDescriptor.equals('V')
    compile(@target)
    if expression && isVoid
      @method.dup
    end
    convertArgs(method.argumentTypes)
    recordPosition
    if method.declaringClass.isInterface
      @method.invokeInterface(method.declaringClass.getAsmType, methodDescriptor(method))
    else
      @method.invokeVirtual(method.declaringClass.getAsmType, methodDescriptor(method))
    end
    convertResult(method.returnType, expression)
  end
  def visitStaticMethodCall(method:JVMMethod, expression:boolean)
    convertArgs(method.argumentTypes)
    recordPosition
    @method.invokeStatic(method.declaringClass.getAsmType, methodDescriptor(method))<commit_msg>Fix void chaining.<commit_after># Copyright (c) 2012 The Mirah project authors. All Rights Reserved.
# All contributing project authors may be found in the NOTICE file.
#
# 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 org.mirah.jvm.compiler

import java.util.Arrays
import java.util.List
import java.util.logging.Logger
import mirah.lang.ast.*
import org.mirah.jvm.types.JVMType
import org.mirah.jvm.types.JVMMethod
import org.mirah.jvm.types.MemberVisitor
import org.mirah.util.Context

import org.jruby.org.objectweb.asm.ClassWriter
import org.jruby.org.objectweb.asm.Opcodes
import org.jruby.org.objectweb.asm.Type
import org.jruby.org.objectweb.asm.commons.GeneratorAdapter
import org.jruby.org.objectweb.asm.commons.Method


class CallCompiler < BaseCompiler implements MemberVisitor
  def self.initialize:void
    @@log = Logger.getLogger(ClassCompiler.class.getName)
  end
  def initialize(compiler:BaseCompiler, bytecode:Bytecode, position:Position, target:Node, name:String, args:NodeList, returnType:JVMType)
    initialize(compiler, bytecode, position, target, name, returnType)
    @args = Node[args.size]
    args.size.times {|i| @args[i] = args.get(i)}
  end
  def initialize(compiler:BaseCompiler, bytecode:Bytecode, position:Position, target:Node, name:String, args:List, returnType:JVMType)
    initialize(compiler, bytecode, position, target, name, returnType)
    @args = Node[args.size]
    args.toArray(@args)
  end
  
  # TODO: private
  def initialize(compiler:BaseCompiler, bytecode:Bytecode, position:Position, target:Node, name:String, returnType:JVMType)
    super(compiler.context)
    @compiler = compiler
    @method = bytecode
    @position = position
    @target = target
    @name = name
    @returnType = returnType
  end
  
  def compile(expression:boolean):void
    getMethod.accept(self, expression)
  end
  
  def getMethod
    @member ||= begin
      argTypes = JVMType[@args.length]
      @args.length.times do |i|
        argTypes[i] = getInferredType(@args[i])
      end
      getInferredType(@target).getMethod(@name, Arrays.asList(argTypes))
    end
  end
  
  def recordPosition:void
    @method.recordPosition(@position)
  end
  
  # private
  def compile(node:Node):void
    @compiler.visit(node, Boolean.TRUE)
  end
  
  def convertArgs(argumentTypes:List):void
    argumentTypes.size.times do |i|
      arg = @args[i]
      compile(arg)
      @method.convertValue(getInferredType(arg), JVMType(argumentTypes[i]))
    end
  end

  def convertResult(returnedType:JVMType, expression:boolean):void
    if expression
      @method.convertValue(returnedType, @returnType)
      # TODO casts for generic method calls
    else
      @method.pop(returnedType)
    end
  end

  def computeMathOp(name:String):int
    name = name.intern
    if name == '+'
      GeneratorAdapter.ADD
    elsif name == '&'
      GeneratorAdapter.AND
    elsif name == '/'
      GeneratorAdapter.DIV
    elsif name == '*'
      GeneratorAdapter.MUL
    elsif name == '-@'
      GeneratorAdapter.NEG
    elsif name == '|'
      GeneratorAdapter.OR
    elsif name == '%'
      GeneratorAdapter.REM
    elsif name == '<<'
      GeneratorAdapter.SHL
    elsif name =='>>'
      GeneratorAdapter.SHR
    elsif name == '-'
      GeneratorAdapter.SUB
    elsif name == '>>>'
      GeneratorAdapter.USHR
    elsif name == '^'
      GeneratorAdapter.XOR
    else
      raise IllegalArgumentException, "Unsupported operator #{name}"
    end
  end

  def visitMath(method:JVMMethod, expression:boolean)
    op = computeMathOp(method.name)
    type = method.returnType
    asm_type = type.getAsmType
    compile(@target)
    @method.convertValue(getInferredType(@target), type)
    convertArgs([type])
    recordPosition
    @method.math(op, asm_type)
    convertResult(type, expression)
  end
  
  def self.computeComparisonOp(name:String):int
    name = name.intern
    if name == '=='
      GeneratorAdapter.EQ
    elsif name == '>='
      GeneratorAdapter.GE
    elsif name == '>'
      GeneratorAdapter.GT
    elsif name == '<='
      GeneratorAdapter.LE
    elsif name == '<'
      GeneratorAdapter.LT
    elsif name == '!='
      GeneratorAdapter.NE
    else
      raise IllegalArgumentException, "Unsupported comparison #{name}"
    end
  end
  
  def compileComparisonValues(method:JVMMethod)
    type = method.declaringClass
    compile(@target)
    @method.convertValue(getInferredType(@target), type)
    convertArgs([type])
  end
  
  def visitComparison(method:JVMMethod, expression:boolean)
    compileComparisonValues(method)

    op = CallCompiler.computeComparisonOp(method.name)
    type = method.declaringClass
    ifTrue = @method.newLabel
    done = @method.newLabel
    @method.ifCmp(type.getAsmType, op, ifTrue)
    @method.push(0)
    @method.goTo(done)
    @method.mark(ifTrue)
    @method.push(1)
    @method.mark(done)
    @method.pop unless expression
  end
  
  def visitMethodCall(method:JVMMethod, expression:boolean)
    isVoid = method.returnType.getAsmType.getDescriptor.equals('V')
    compile(@target)
    returnType = method.returnType
    if expression && isVoid
      @method.dup
      returnType = getInferredType(@target)
    end
    convertArgs(method.argumentTypes)
    recordPosition
    if method.declaringClass.isInterface
      @method.invokeInterface(method.declaringClass.getAsmType, methodDescriptor(method))
    else
      @method.invokeVirtual(method.declaringClass.getAsmType, methodDescriptor(method))
    end
    convertResult(returnType, expression)
  end
  def visitStaticMethodCall(method:JVMMethod, expression:boolean)
    convertArgs(method.argumentTypes)
    recordPosition
    @method.invokeStatic(method.declaringClass.getAsmType, methodDescriptor(method))
{'fraction_non_alphanumeric': 0.10150891632373114, 'fraction_numerical': 0.0016138142499798273, 'mean_word_length': 3.6593984962406014, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 17, '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': '2550796', 'n_tokens_mistral': 3787, 'n_tokens_neox': 3729, 'n_words': 847}