require "../../spec_helper" describe "Code gen: pointer" do it "get pointer and value of it" do run("a = 1; b = pointerof(a); b.value").to_i.should eq(1) end it "get pointer of instance var" do run(<<-CRYSTAL).to_i.should eq(10) class Foo def initialize(value : Int32) @value = value end def value_ptr pointerof(@value) end end foo = Foo.new(10) value_ptr = foo.value_ptr value_ptr.value CRYSTAL end it "set pointer value" do run("a = 1; b = pointerof(a); b.value = 2; a").to_i.should eq(2) end it "get value of pointer to union" do run("a = 1.1; a = 1; b = pointerof(a); b.value.to_i!").to_i.should eq(1) end it "sets value of pointer to union" do run("p = Pointer(Int32|Float64).malloc(1_u64); a = 1; a = 2.5; p.value = a; p.value.to_i!").to_i.should eq(2) end it "increments pointer" do run(<<-CRYSTAL).to_i.should eq(2) class Foo def initialize @a = 1 @b = 2 end def value p = pointerof(@a) p += 1_i64 p.value end end Foo.new.value CRYSTAL end it "codegens malloc" do run("p = Pointer(Int32).malloc(10_u64); p.value = 1; p.value &+ 1_i64").to_i.should eq(2) end it "codegens realloc" do run("p = Pointer(Int32).malloc(10_u64); p.value = 1; x = p.realloc(20_u64); x.value &+ 1_i64").to_i.should eq(2) end it "codegens pointer cast" do run("a = 1_i64; pointerof(a).as(Int32*).value").to_i.should eq(1) end it "codegens pointer cast to Nil (#8015)" do run("a = 1_i64; pointerof(a).as(Nil).nil? ? 3 : 7").to_i.should eq(3) end it "codegens pointer as if condition" do run("a = 0; pointerof(a) ? 1 : 2").to_i.should eq(1) end it "codegens null pointer as if condition" do run("Pointer(Int32).new(0_u64) ? 1 : 2").to_i.should eq(2) end it "gets pointer of instance variable in virtual type" do run(<<-CRYSTAL).to_i.should eq(1) class Foo def initialize @a = 1 end def foo pointerof(@a) end end class Bar < Foo end foo = Foo.new || Bar.new x = foo.foo x.value CRYSTAL end it "sets value of pointer to struct" do run(<<-CRYSTAL).to_i.should eq(20) lib LibC struct Color r, g, b, a : UInt8 end end color = Pointer(LibC::Color).malloc(1_u64) color.value.r = 10_u8 color2 = Pointer(LibC::Color).malloc(1_u64) color2.value.r = 20_u8 color.value = color2.value color.value.r CRYSTAL end it "changes through var and reads from pointer" do run(<<-CRYSTAL).to_i.should eq(2) x = 1 px = pointerof(x) x = 2 px.value CRYSTAL end it "creates pointer by address" do run(<<-CRYSTAL).to_i.should eq(123) x = Pointer(Int32).new(123_u64) x.address CRYSTAL end it "calculates pointer diff" do run(<<-CRYSTAL).to_i.should eq(1) x = 1 (pointerof(x) + 1_i64) - pointerof(x) CRYSTAL end it "can dereference pointer to func" do run(<<-CRYSTAL).to_i.should eq(1) def foo; 1; end x = ->foo y = pointerof(x) y.value.call CRYSTAL end it "gets pointer of argument that is never assigned to" do run(<<-CRYSTAL).to_i.should eq(1) def foo(x) pointerof(x) end foo(1) 1 CRYSTAL end it "codegens nilable pointer type (1)" do run(<<-CRYSTAL).to_i.should eq(3) p = Pointer(Int32).malloc(1_u64) p.value = 3 a = 1 == 2 ? nil : p if a a.value else 4 end CRYSTAL end it "codegens nilable pointer type (2)" do run(<<-CRYSTAL).to_i.should eq(4) p = Pointer(Int32).malloc(1_u64) p.value = 3 a = 1 == 1 ? nil : p if a a.value else 4 end CRYSTAL end it "codegens nilable pointer type dispatch (1)" do run(<<-CRYSTAL).to_i.should eq(3) def foo(x : Pointer) x.value end def foo(x : Nil) 0 end p = Pointer(Int32).malloc(1_u64) p.value = 3 a = 1 == 1 ? p : nil foo(a) CRYSTAL end it "codegens nilable pointer type dispatch (2)" do run(<<-CRYSTAL).to_i.should eq(0) def foo(x : Pointer) x.value end def foo(x : Nil) 0 end p = Pointer(Int32).malloc(1_u64) p.value = 3 a = 1 == 1 ? nil : p foo(a) CRYSTAL end it "assigns nil and pointer to nilable pointer type" do run(<<-CRYSTAL).to_i.should eq(3) class Foo def initialize end def x=(@x : Int32*?) end def x @x end end p = Pointer(Int32).malloc(1_u64) p.value = 3 foo = Foo.new foo.x = nil foo.x = p z = foo.x if z p.value else 2 end CRYSTAL end it "gets pointer to constant" do run(<<-CRYSTAL).to_i.should eq(1) require "prelude" FOO = 1 pointerof(FOO).value CRYSTAL end it "passes pointer of pointer to method" do run(<<-CRYSTAL).to_i.should eq(1) def foo(x) x.value.value end p = Pointer(Pointer(Int32)).malloc(1_u64) p.value = Pointer(Int32).malloc(1_u64) p.value.value = 1 foo p CRYSTAL end it "codegens pointer as if condition inside union (1)" do run(<<-CRYSTAL).to_i.should eq(2) ptr = Pointer(Int32).new(0_u64) || Pointer(Float64).new(0_u64) if ptr 1 else 2 end CRYSTAL end it "codegens pointer as if condition inside union (2)" do run(<<-CRYSTAL).to_i.should eq(30) if 1 == 1 ptr = Pointer(Int32).new(0_u64) else ptr = 10 end ptr ? 20 : 30 CRYSTAL end it "can use typedef pointer value get and set (#630)" do codegen(<<-CRYSTAL) lib LibFoo type MyObj = Int32* fun foo : MyObj end LibFoo.foo.value LibFoo.foo.value = 1 CRYSTAL end it "does pointerof class variable" do run(<<-CRYSTAL).to_i.should eq(2) class Foo @@a = 1 def self.a_ptr pointerof(@@a) end def self.a @@a end end Foo.a_ptr.value = 2 Foo.a CRYSTAL end it "does pointerof class variable with class" do run(<<-CRYSTAL).to_i.should eq(2) class Bar def initialize(@x : Int32) end def x @x end end class Foo @@a = Bar.new(1) def self.a_ptr pointerof(@@a) end def self.a @@a end end Foo.a_ptr.value = Bar.new(2) Foo.a.x CRYSTAL end it "does pointerof read variable" do run(<<-CRYSTAL).to_i.should eq(123) class Foo def initialize @x = 1 end def x @x end end foo = Foo.new pointerof(foo.@x).value = 123 foo.x CRYSTAL end it "can assign nil to void pointer" do codegen(<<-CRYSTAL) ptr = Pointer(Void).malloc(1_u64) ptr.value = ptr.value CRYSTAL end it "can pass any pointer to something expecting void* in lib call" do codegen(<<-CRYSTAL) lib LibFoo fun foo(x : Void*) : Float64 end LibFoo.foo(Pointer(Int32).malloc(1_u64)) CRYSTAL end it "can pass any pointer to something expecting void* in lib call, with to_unsafe" do codegen(<<-CRYSTAL) lib LibFoo fun foo(x : Void*) : Float64 end class Foo def to_unsafe Pointer(Int32).malloc(1_u64) end end LibFoo.foo(Foo.new) CRYSTAL end it "uses correct llvm module for typedef metaclass (#2877)" do run(<<-CRYSTAL) lib LibFoo type Foo = Void* type Bar = Void* end class Class def foo foo(1) end def foo(x) end end struct Pointer def foo T.foo end end foo = uninitialized LibFoo::Foo* bar = uninitialized LibFoo::Bar* foo.foo bar.foo 1 CRYSTAL end it "passes arguments correctly for typedef metaclass (#8544)" do run <<-CRYSTAL lib LibFoo type Foo = Void* end class Class def foo(x) x end end x = 1 LibFoo::Foo.foo(x) Pointer(Void).foo(x) CRYSTAL end it "generates correct code for Pointer.malloc(0) (#2905)" do run(<<-CRYSTAL).to_i.should eq(3) class Foo def initialize(@value : Int32) end def value @value end end foo = Foo.new(3) Pointer(Int32 | UInt8[9]).malloc(0_u64) foo.value CRYSTAL end it "compares pointers through typedef" do run(<<-CRYSTAL).to_b.should be_true module Comparable(T) def ==(other : T) (self <=> other) == 0 end end struct Pointer(T) include Comparable(Pointer) def <=>(other : Pointer) 0 end end lib LibFoo type Ptr = Void* end ptr = Pointer(Void).malloc(1_u64).as(LibFoo::Ptr) ptr == ptr CRYSTAL end # FIXME: `$external_var` implies __declspec(dllimport), but we only have an # object file, so MinGW-w64 fails linking (actually MSVC also emits an # LNK4217 linker warning) {% unless flag?(:win32) && flag?(:gnu) %} it "takes pointerof lib external var" do test_c(<<-C, <<-CRYSTAL, &.to_i.should eq(111)) int external_var = 0; C lib LibFoo $external_var : Int32 end LibFoo.external_var = 1 ptr = pointerof(LibFoo.external_var) x = ptr.value ptr.value = 10 y = ptr.value ptr.value = 100 z = LibFoo.external_var x + y + z CRYSTAL end {% end %} end