{% skip_file if flag?(:without_interpreter) %}
require "./spec_helper"

describe Crystal::Repl::Interpreter do
  context "pointers" do
    it "interprets pointer set and get (int)" do
      interpret(<<-CRYSTAL).should eq(10)
        ptr = Pointer(Int32).malloc(1_u64)
        ptr.value = 10
        ptr.value
      CRYSTAL
    end

    it "interprets pointer set and get (bool)" do
      interpret(<<-CRYSTAL).should be_true
        ptr = Pointer(Bool).malloc(1_u64)
        ptr.value = true
        ptr.value
      CRYSTAL
    end

    it "interprets pointer set and get (clear stack)" do
      interpret(<<-CRYSTAL).should eq(50.unsafe_chr)
        ptr = Pointer(UInt8).malloc(1_u64)
        ptr.value = 50_u8
        ptr.value.unsafe_chr
      CRYSTAL
    end

    it "interprets pointerof, mutates pointer, read var" do
      interpret(<<-CRYSTAL).should eq(2)
        a = 1
        ptr = pointerof(a)
        ptr.value = 2
        a
      CRYSTAL
    end

    it "interprets pointerof, mutates var, read pointer" do
      interpret(<<-CRYSTAL).should eq(2)
        a = 1
        ptr = pointerof(a)
        a = 2
        ptr.value
      CRYSTAL
    end

    it "interprets pointerof and mutates memory (there are more variables)" do
      interpret(<<-CRYSTAL).should eq(2)
        x = 42
        a = 1
        ptr = pointerof(a)
        ptr.value = 2
        a
      CRYSTAL
    end

    it "pointerof instance var" do
      interpret(<<-CRYSTAL).should eq(2)
        class Foo
          def initialize(@x : Int32)
          end

          def x
            @x
          end

          def x_ptr
            pointerof(@x)
          end
        end

        foo = Foo.new(1)
        ptr = foo.x_ptr
        ptr.value = 2
        foo.x
      CRYSTAL
    end

    it "pointerof class var" do
      interpret(<<-CRYSTAL).should eq(2)
        class Foo
          @@x : Int32?

          def self.x_ptr
            pointerof(@@x)
          end

          def self.x
            @@x
          end
        end

        ptr = Foo.x_ptr
        v = ptr.value
        ptr.value = 2
        x = Foo.x
        x || 0
      CRYSTAL
    end

    it "pointerof read instance var" do
      interpret(<<-CRYSTAL).should eq(2)
        class Foo
          def initialize(@x : Int32)
          end

          def x
            @x
          end

          def x_ptr
            pointerof(@x)
          end
        end

        foo = Foo.new(1)
        ptr = pointerof(foo.@x)
        ptr.value = 2
        foo.x
      CRYSTAL
    end

    it "pointerof read `StaticArray#@buffer` (1)" do
      interpret(<<-CRYSTAL).should eq(2)
        struct StaticArray(T, N)
          def to_unsafe
            pointerof(@buffer)
          end

          def x
            @buffer
          end
        end

        foo = uninitialized Int32[4]
        foo.to_unsafe.value = 2
        foo.x
        CRYSTAL
    end

    it "pointerof read `StaticArray#@buffer` (2)" do
      interpret(<<-CRYSTAL).should eq(2)
        foo = uninitialized Int32[4]
        pointerof(foo.@buffer).value = 2
        foo.@buffer
        CRYSTAL
    end

    it "interprets pointer set and get (union type)" do
      interpret(<<-CRYSTAL).should eq(10)
        ptr = Pointer(Int32 | Bool).malloc(1_u64)
        ptr.value = 10
        value = ptr.value
        if value.is_a?(Int32)
          value
        else
          20
        end
      CRYSTAL
    end

    it "interprets pointer set and get (union type, setter value)" do
      interpret(<<-CRYSTAL).should eq(10)
        ptr = Pointer(Int32 | Bool).malloc(1_u64)
        ptr.value = 10
      CRYSTAL
    end

    it "interprets pointer new and pointer address" do
      interpret(<<-CRYSTAL).should eq(123_u64)
        ptr = Pointer(Int32 | Bool).new(123_u64)
        ptr.address
      CRYSTAL
    end

    it "interprets pointer diff" do
      interpret(<<-CRYSTAL).should eq(8_i64)
        ptr1 = Pointer(Int32).new(133_u64)
        ptr2 = Pointer(Int32).new(100_u64)
        ptr1 - ptr2
      CRYSTAL
    end

    it "interprets pointer diff, negative" do
      interpret(<<-CRYSTAL).should eq(-8_i64)
        ptr1 = Pointer(Int32).new(133_u64)
        ptr2 = Pointer(Int32).new(100_u64)
        ptr2 - ptr1
      CRYSTAL
    end

    it "discards pointer malloc" do
      interpret(<<-CRYSTAL).should eq(1)
        Pointer(Int32).malloc(1_u64)
        1
      CRYSTAL
    end

    it "discards pointer get" do
      interpret(<<-CRYSTAL).should eq(1)
        ptr = Pointer(Int32).malloc(1_u64)
        ptr.value
        1
      CRYSTAL
    end

    it "discards pointer set" do
      interpret(<<-CRYSTAL).should eq(1)
        ptr = Pointer(Int32).malloc(1_u64)
        ptr.value = 1
      CRYSTAL
    end

    it "discards pointer new" do
      interpret(<<-CRYSTAL).should eq(1)
        Pointer(Int32).new(1_u64)
        1
      CRYSTAL
    end

    it "discards pointer diff" do
      interpret(<<-CRYSTAL).should eq(1)
        ptr1 = Pointer(Int32).new(133_u64)
        ptr2 = Pointer(Int32).new(100_u64)
        ptr1 - ptr2
        1
      CRYSTAL
    end

    it "discards pointerof" do
      interpret(<<-CRYSTAL).should eq(3)
        a = 1
        pointerof(a)
        3
      CRYSTAL
    end

    it "interprets pointer add" do
      interpret(<<-CRYSTAL).should eq(9)
        ptr = Pointer(Int32).new(1_u64)
        ptr2 = ptr + 2_i64
        ptr2.address
      CRYSTAL
    end

    it "discards pointer add" do
      interpret(<<-CRYSTAL).should eq(3)
        ptr = Pointer(Int32).new(1_u64)
        ptr + 2_i64
        3
      CRYSTAL
    end

    it "interprets pointer realloc" do
      interpret(<<-CRYSTAL).should eq(3)
        ptr = Pointer(Int32).malloc(1_u64)
        ptr2 = ptr.realloc(2_u64)
        3
      CRYSTAL
    end

    it "discards pointer realloc" do
      interpret(<<-CRYSTAL).should eq(3)
        ptr = Pointer(Int32).malloc(1_u64)
        ptr.realloc(2_u64)
        3
      CRYSTAL
    end

    it "interprets pointer realloc wrapper" do
      interpret(<<-CRYSTAL).should eq(3)
        struct Pointer(T)
          def realloc(n)
            realloc(n.to_u64)
          end
        end

        ptr = Pointer(Int32).malloc(1_u64)
        ptr2 = ptr.realloc(2)
        3
      CRYSTAL
    end

    it "interprets nilable pointer truthiness" do
      interpret(<<-CRYSTAL).should eq(1)
        ptr = 1 == 1 ? Pointer(UInt8).malloc(1) : nil
        if ptr
          1
        else
          2
        end
      CRYSTAL
    end

    it "autocasts a typed pointer to `Pointer(Void)` in a lib struct (#16649)" do
      interpret(<<-CRYSTAL).should eq(0xDEAD_u64)
        lib LibFoo
          struct S
            data : Pointer(Void)
          end
        end

        s = LibFoo::S.new
        s.data = Pointer(UInt8).new(0xDEAD_u64)
        s.data.address
      CRYSTAL
    end
  end
end
