Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: c94112ff69a9
Choose a base ref
...
head repository: crystal-lang/crystal
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 55c75841f32f
Choose a head ref
  • 3 commits
  • 7 files changed
  • 1 contributor

Commits on Dec 17, 2016

  1. Added compare_versions macro methods to correctly compare semver ve…

    …rsions at compile-time
    Ary Borenszweig committed Dec 17, 2016
    Copy the full SHA
    43c222f View commit details
  2. Compiler: small refactor in macro methods

    Ary Borenszweig committed Dec 17, 2016
    Copy the full SHA
    de4d506 View commit details
  3. Compiler: workaround for missing LLVMWriteBitcodeToMemoryBuffer in LL…

    …VM 3.5.0
    Ary Borenszweig committed Dec 17, 2016
    Copy the full SHA
    55c7584 View commit details
4 changes: 4 additions & 0 deletions spec/compiler/macro/macro_methods_spec.cr
Original file line number Diff line number Diff line change
@@ -1524,4 +1524,8 @@ describe "macro methods" do
assert_macro "", %({{flag?(:foo)}}), [] of ASTNode, %(false)
end
end

it "compares versions" do
assert_macro "", %({{compare_versions("1.10.3", "1.2.3")}}), [] of ASTNode, %(1)
end
end
2 changes: 1 addition & 1 deletion src/compiler/crystal/compiler.cr
Original file line number Diff line number Diff line change
@@ -406,7 +406,7 @@ module Crystal
bc_name = self.bc_name
object_name = self.object_name

memory_buffer = llvm_mod.write_bitcode
memory_buffer = llvm_mod.write_bitcode_to_memory_buffer

# To compile a file we first generate a `.bc` file and then
# create an object file from it. These `.bc` files are stored
9 changes: 9 additions & 0 deletions src/compiler/crystal/macros.cr
Original file line number Diff line number Diff line change
@@ -5,6 +5,15 @@
# are documented on the classes in this module. Additionally, methods of the
# `Macros` module are top-level methods that you can invoke, like `puts` and `run`.
module Crystal::Macros
# Compares two [semantic versions](http://semver.org/).
# Returns -1 if v1 < v2, 0 if v1 == v2, - if v1 > v2.
#
# ```
# {{ compare_versions("1.10.0", "1.2.0") }} # => 1
# ```
def compare_versions(v1 : StringLiteral, v2 : StringLiteral) : NumberLiteral
end

# Outputs the current macro's buffer to the standard output. Useful for debugging
# a macro to see what's being generated. Use it like `{{debug()}}`, the parenthesis
# are mandatory.
82 changes: 45 additions & 37 deletions src/compiler/crystal/macros/methods.cr
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
require "../semantic/ast"
require "./macros"
require "semantic_version"

module Crystal
class MacroInterpreter
def interpret_top_level_call(node)
case node.name
when "compare_versions"
interpret_compare_versions(node)
when "debug"
interpret_debug(node)
when "env"
@@ -26,6 +29,34 @@ module Crystal
end
end

def interpret_compare_versions(node)
unless node.args.size == 2
node.wrong_number_of_arguments "macro call 'compare_versions'", node.args.size, 2
end

first_arg = node.args[0]
first = accept first_arg
first_string = first.to_string("first argument to 'compare_versions'")

second_arg = node.args[1]
second = accept second_arg
second_string = second.to_string("second argument to 'compare_versions'")

first_version = begin
SemanticVersion.parse(first_string)
rescue ex
first_arg.raise ex.message
end

second_version = begin
SemanticVersion.parse(second_string)
rescue ex
second_arg.raise ex.message
end

@last = NumberLiteral.new(first_version <=> second_version)
end

def interpret_debug(node)
if node.args.size >= 1
node.args.first.accept self
@@ -187,6 +218,16 @@ module Crystal
to_s
end

def to_string(context)
case self
when StringLiteral then return self.value
when SymbolLiteral then return self.value
when MacroId then return self.value
else
raise "expected #{context} to be a StringLiteral, SymbolLiteral or MacroId, not #{class_desc}"
end
end

def truthy?
case self
when NilLiteral, Nop
@@ -1274,42 +1315,19 @@ module Crystal
interpret_argless_method(method, args) { TypeNode.constants(type) }
when "constant"
interpret_one_arg_method(method, args) do |arg|
case arg
when StringLiteral
value = arg.value
when SymbolLiteral
value = arg.value.to_s
when MacroId
value = arg.value.to_s
else
raise "argument to constant must be a StringLiteral, SymbolLiteral or MacroId, not #{arg.class_desc}"
end
value = arg.to_string("argument to 'TypeNode#constant'")
TypeNode.constant(type, value)
end
when "has_constant?"
interpret_one_arg_method(method, args) do |arg|
case arg
when StringLiteral
value = arg.value
when SymbolLiteral
value = arg.value.to_s
else
raise "argument to has_constant? must be a StringLiteral or SymbolLiteral, not #{arg.class_desc}"
end
value = arg.to_string("argument to 'TypeNode#has_constant?'")
TypeNode.has_constant?(type, value)
end
when "methods"
interpret_argless_method(method, args) { TypeNode.methods(type) }
when "has_attribute?"
interpret_one_arg_method(method, args) do |arg|
case arg
when StringLiteral
value = arg.value
when SymbolLiteral
value = arg.value
else
raise "argument to has_attribtue? must be a StringLiteral or SymbolLiteral, not #{arg.class_desc}"
end
value = arg.to_string("argument to 'TypeNode#has_attribute?'")
BoolLiteral.new(!!type.has_attribute?(value))
end
when "size"
@@ -1397,17 +1415,7 @@ module Crystal
raise "TypeNode##{method} expects TypeNode as a first argument, not #{arg1.class_desc}"
end

case arg2
when StringLiteral
value = arg2.value
when SymbolLiteral
value = arg2.value
when MacroId
value = arg2.value
else
raise "TypeNode##{method} expects StringLiteral, SymbolLiteral or MacroId as a second argument, not #{arg2.class_desc}"
end

value = arg2.to_string("second argument to 'TypeNode#overrides?")
TypeNode.overrides?(type, arg1.type, value)
end
else
8 changes: 7 additions & 1 deletion src/llvm/lib_llvm.cr
Original file line number Diff line number Diff line change
@@ -295,11 +295,17 @@ lib LibLLVM
fun dispose_pass_manager_builder = LLVMPassManagerBuilderDispose(PassManagerBuilderRef)
fun set_volatile = LLVMSetVolatile(value : ValueRef, volatile : UInt32)
fun set_alignment = LLVMSetAlignment(value : ValueRef, bytes : UInt32)
fun write_bitcode_to_memory_buffer = LLVMWriteBitcodeToMemoryBuffer(mod : ModuleRef) : MemoryBufferRef

{% unless LibLLVM::IS_35 %}
fun write_bitcode_to_memory_buffer = LLVMWriteBitcodeToMemoryBuffer(mod : ModuleRef) : MemoryBufferRef
{% end %}

fun dispose_memory_buffer = LLVMDisposeMemoryBuffer(buf : MemoryBufferRef) : Void
fun get_buffer_start = LLVMGetBufferStart(buf : MemoryBufferRef) : UInt8*
fun get_buffer_size = LLVMGetBufferSize(buf : MemoryBufferRef) : LibC::SizeT

fun write_bitcode_to_fd = LLVMWriteBitcodeToFD(mod : ModuleRef, fd : LibC::Int, should_close : LibC::Int, unbuffered : LibC::Int) : LibC::Int

{% if LibLLVM::IS_36 || LibLLVM::IS_35 %}
fun add_target_data = LLVMAddTargetData(td : TargetDataRef, pm : PassManagerRef)
{% end %}
19 changes: 13 additions & 6 deletions src/llvm/memory_buffer.cr
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
class LLVM::MemoryBuffer
def initialize(@unwrap : LibLLVM::MemoryBufferRef)
def initialize(@unwrap : LibLLVM::MemoryBufferRef | Bytes)
@finalized = false
end

def to_slice
Slice.new(
LibLLVM.get_buffer_start(self),
LibLLVM.get_buffer_size(self),
)
if (unwrap = @unwrap).is_a?(Bytes)
unwrap
else
Slice.new(
LibLLVM.get_buffer_start(unwrap),
LibLLVM.get_buffer_size(unwrap),
)
end
end

def dispose
@@ -18,7 +22,10 @@ class LLVM::MemoryBuffer

def finalize
return if @finalized
LibLLVM.dispose_memory_buffer(@unwrap)

if (unwrap = @unwrap).is_a?(LibLLVM::MemoryBufferRef)
LibLLVM.dispose_memory_buffer(unwrap)
end
end

def to_unsafe
20 changes: 17 additions & 3 deletions src/llvm/module.cr
Original file line number Diff line number Diff line change
@@ -30,12 +30,26 @@ class LLVM::Module
GlobalCollection.new(self)
end

def write_bitcode(filename : String)
def write_bitcode_to_file(filename : String)
LibLLVM.write_bitcode_to_file self, filename
end

def write_bitcode
MemoryBuffer.new(LibLLVM.write_bitcode_to_memory_buffer self)
def write_bitcode_to_memory_buffer
{% if LibLLVM::IS_35 %}
# LLVMWriteBitcodeToMemoryBuffer doesn't exist in LLVM 3.5.0
slice = IO.pipe do |r, w|
write_bitcode_to_fd(w.fd)
w.close
r.gets_to_end.to_slice
end
MemoryBuffer.new(slice)
{% else %}
MemoryBuffer.new(LibLLVM.write_bitcode_to_memory_buffer self)
{% end %}
end

def write_bitcode_to_fd(fd : Int, should_close = false, buffered = false)
LibLLVM.write_bitcode_to_fd(self, fd, should_close ? 1 : 0, buffered ? 1 : 0)
end

def verify