Skip to content

Commit

Permalink
Honour pre-processor if/unless statements (fixes #413)
Browse files Browse the repository at this point in the history
Introduce pre-processed if directives to hide code from Opal. Two special
constant checks now take place in the compiler. Either `RUBY_ENGINE` or
`RUBY_PLATFORM` when `== "opal"`. Both if and unless statements can pick
up these logic checks:

    if RUBY_ENGINE == "opal"
      # this code compiles
    else
      # this code never compiles
    end

Unless:

    unless RUBY_ENGINE == "opal"
      # this code never compiles
    end

This is particularly useful for avoiding `require()` statements being
picked up, which are included at compile time.
  • Loading branch information
adambeynon committed Dec 8, 2013
1 parent 8f646bc commit ef570e3
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 9 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Expand Up @@ -14,6 +14,26 @@
next release, the exernal `opal-sprockets` gem is no longer needed. This
commit adds `Opal::Processor`, `Opal::Server` and `Opal::Environment`.

* Introduce pre-processed if directives to hide code from Opal. Two special
constant checks now take place in the compiler. Either `RUBY_ENGINE` or
`RUBY_PLATFORM` when `== "opal"`. Both if and unless statements can pick
up these logic checks:

if RUBY_ENGINE == "opal"
# this code compiles
else
# this code never compiles
end

Unless:

unless RUBY_ENGINE == "opal"
# this code never compiles
end

This is particularly useful for avoiding `require()` statements being
picked up, which are included at compile time.

* Add special `debugger` method to compiler. Compiles down to javascript
`debugger` keyword to start in-browser debug console.

Expand Down
25 changes: 16 additions & 9 deletions lib/opal/nodes/if.rb
Expand Up @@ -7,21 +7,20 @@ class IfNode < Base

children :test, :true_body, :false_body

RUBY_ENGINE_CHECK = [:call, [:const, :RUBY_ENGINE],
:==, [:arglist, [:str, "opal"]]]

RUBY_PLATFORM_CHECK = [:call, [:const, :RUBY_PLATFORM],
:==, [:arglist, [:str, "opal"]]]

def compile
truthy, falsy = self.truthy, self.falsy

push "if ("

# optimize unless (we don't want a else() unless we need to)
if falsy and !truthy
truthy = falsy
if skip_check_present?
falsy = nil
push js_falsy(test)
else
push js_truthy(test)
end

push ") {"
push "if (", js_truthy(test), ") {"

# skip if-body if no truthy sexp
indent { line stmt(truthy) } if truthy
Expand All @@ -44,6 +43,14 @@ def compile
wrap "(function() {", "; return nil; })()" if needs_wrapper?
end

# pre-processing only effects falsy blocks. If engine is
# opal, then falsy block gets generated as normal. Unless
# engine is opal then that code gets generated as the
# falsy block
def skip_check_present?
test == RUBY_ENGINE_CHECK or test == RUBY_PLATFORM_CHECK
end

def truthy
needs_wrapper? ? compiler.returns(true_body || s(:nil)) : true_body
end
Expand Down
66 changes: 66 additions & 0 deletions spec/cli/compiler_spec.rb
Expand Up @@ -64,6 +64,72 @@
end
end

describe "pre-processed if conditions" do
it "compiles if blocks using RUBY_ENGINE/RUBY_PLATFORM == opal" do
expect_compiled(<<-RUBY).to include("should_compile_fine")
if RUBY_ENGINE == 'opal'
:should_compile_fine
end
RUBY

expect_compiled(<<-RUBY).to include("so_should_this")
if RUBY_PLATFORM == 'opal'
:so_should_this
end
RUBY
end

it "skips elsif/else parts" do
expect_compiled(<<-RUBY).to_not include("should_be_skipped")
if RUBY_PLATFORM == "opal"
:ok
else
:should_be_skipped
end
RUBY

result = expect_compiled(<<-RUBY)
if RUBY_ENGINE == 'opal'
:this_compiles
elsif false
:this_does_not_compile
else
:this_neither
end
RUBY

result.to_not include("this_does_not_compile", "this_neither")
end

it "generates if-code as normal without check" do
expect_compiled(<<-RUBY).to include("should_compile", "and_this")
if some_conditional
:should_compile
else
:and_this
end
RUBY
end
end

describe "pre-processed unless conditionals" do
it "skips over if using RUBY_ENGINE/RUBY_PLATFORM == 'opal'" do
expect_compiled(<<-RUBY).to_not include("should_not_compile")
unless RUBY_ENGINE == 'opal'
:should_not_compile
end
RUBY
end

it "generates unless code as normal if no check" do
expect_compiled(<<-RUBY).to include("this_should_compile")
unless this_is_true
:this_should_compile
end
RUBY
end
end

def expect_compiled(source)
expect(Opal::Compiler.new.compile source)
end
Expand Down

0 comments on commit ef570e3

Please sign in to comment.