Comments (5)
To locate a part that is compiled to a switch while it shouldn't, I did the following:
I applied a patch:
diff --git a/lib/opal/nodes/if.rb b/lib/opal/nodes/if.rb
index ba8c302c1..3bf0e0ba9 100644
--- a/lib/opal/nodes/if.rb
+++ b/lib/opal/nodes/if.rb
@@ -266,6 +266,10 @@ module Opal
return false unless valid_switch_body?(true_body)
+ disable = (rand(1..2) == 1)
+ p [@sexp.loc.expression, disable]
+ return false if disable
+
could_become_switch_branch?(false_body)
end
This randomly either enabled switch compilation or disabled it.
mkdir _res; cd _res
for i in `seq 1 80`; do echo $i; OPAL_CACHE_DISABLE=1 OPAL_PREFORK_DISABLE=1 ../bin/opal -rcorelib/string/unpack -rparser -rparser/ruby32 -e 'p Parser::Ruby32.parse("#ú\n-1")' > $i.ex; done
This did a lot of sample compilations and checked which one was good while dumping information about which case
was compiled as switch
(false) and which did not (true) and their locations.
I wrote a simple script that gave me info which particular part was badly compiled as a switch
for the test to work:
fixes = Dir["*.ex"].map do |i|
f = File.read(i).split("\n")
last = f.pop
correct = last == "s(:int, -1)"
f.grep(/true/) if correct
end.compact
pp fixes.first.intersection(*fixes)
Then I ran it and got this output:
# ruby ./findpart.rb
["[#<Parser::Source::Range parser/builders/default.rb 8257...8288>, true]"]
Let's try to locate that part:
# ruby -e 'puts File.read("/home/user/Code/.gem/gems/parser-3.2.2.1/lib/parser/builders/default.rb")[8257...8288]'
when '+'
value = +value
Let's try to expand it to provide a bigger view:
def unary_num(unary_t, numeric)
value, = *numeric
operator_loc = loc(unary_t)
case value(unary_t)
when '+'
value = +value
when '-'
value = -value
end
numeric.updated(nil, [ value ],
:location =>
Source::Map::Operator.new(
operator_loc,
operator_loc.join(numeric.loc.expression)))
end
This is correct to be compiled, isn't it? Let's try dig deeper.
Let's try to compile it to JS:
$a = [].concat($to_a(numeric)), (value = ($a[0] == null ? nil : $a[0])), $a;
operator_loc = self.$loc(unary_t);
switch (self.$value(unary_t)) {
case "+":
value = value['$+@']()
break;
case "-":
value = value['$-@']()
break;
default:
nil
};
return numeric.$updated(nil, [value], $hash2(["location"], {"location": $$$($$$($$('Source'), 'Map'), 'Operator').$new(operator_loc, operator_loc.$join(numeric.$loc().$expression()))}));
})
Nothing looks particularly wrong. I have patched that method to add this line of code:
`console.log(#{value(unary_t)})` if RUBY_ENGINE == 'opal'
[String: '-'] {
internal_encoding: klass {
'$$id': 14,
name: 'UTF-8',
names: [ 'UTF-8', 'CP65001' ],
ascii: true,
dummy: false
}
}
s(:int, 1)
Aha! As I suspected, it's an Object-wrapped string (think: new String("123") vs "123"), since we need to store encoding info alongside a string (...a little known about JavaScript feature that we abuse for that matter). Let's try an interactive Node session:
> new String("abc")
[String: 'abc']
> "abc"
'abc'
>
See how to spot an object-wrapped string in Node?
> s = "abc"; switch(s) { case "abc": console.log("equal"); break; default: console.log("inequal"); }
equal
undefined
> s = new String("abc"); switch(s) { case "abc": console.log("equal"); break; default: console.log("inequal"); }
inequal
undefined
>
We can easily fix this issue by extracting a real JS string:
> s = new String("abc"); switch(s.valueOf()) { case "abc": console.log("equal"); break; default: console.log("inequal"); }
equal
undefined
>
Therefore a real fix is this:
diff --git a/lib/opal/nodes/if.rb b/lib/opal/nodes/if.rb
index ba8c302c1..b298964c3 100644
--- a/lib/opal/nodes/if.rb
+++ b/lib/opal/nodes/if.rb
@@ -349,7 +349,7 @@ module Opal
@switch_additional_rules = sexp.meta[:switch_additional_rules]
compile_switch_case(sexp.meta[:switch_test])
else
- line "switch (", expr(@switch_first_test), ") {"
+ line "switch (", expr(@switch_first_test), ".valueOf()) {"
indent do
compile_switch_case(@switch_test)
end
And... it works:
# OPAL_CACHE_DISABLE=1 OPAL_PREFORK_DISABLE=1 bin/opal -rcorelib/string/unpack -rparser -rparser/ruby32 -e 'p Parser::Ruby32.parse("#ú\n-1")'
s(:int, -1)
from opal.
Interesting case - my gut feeling is that parser gem when compiled with Opal loses the minus sign if UTF-8 characters are encountered...
This is not the case if Opal is ran on Linux.
from opal.
This is a minimal viable test case:
# opal -rcorelib/string/unpack -rparser -rparser/ruby32 -e 'p Parser::Ruby32.parse("#ú\n-1")'
s(:int, 1)
The correct output should be:
# ruby -rparser -rparser/ruby32 -e 'p Parser::Ruby32.parse("#ú\n-1")'
s(:int, -1)
This is a regression since opal 1.5.0:
# opal _1.4.0_ -rcorelib/string/unpack -rparser -rparser/ruby32 -e 'p Parser::Ruby32.parse("#ú\n-1")'
Object freezing is not supported by Opal
s(:int, -1)
# opal _1.5.0_ -rcorelib/string/unpack -rparser -rparser/ruby32 -e 'p Parser::Ruby32.parse("#ú\n-1")'
Object freezing is not supported by Opal
s(:int, 1)
#
This is good news, since we can bisect and find an offending commit.
from opal.
The commit that broke it is: 6d1b83d
[user@localhost opal]# git bisect good
6d1b83d1cc45b1fc062222029cca92e413da9ae9 is the first bad commit
commit 6d1b83d1cc45b1fc062222029cca92e413da9ae9
Author: hmdne <[email protected]>
Date: Thu Mar 24 14:08:21 2022 +0100
Try to compile case statements as switch
This improves deeply nested `case` structures, like in... lexer.
Compared to a tree of else ifs, `switch` is more like a goto, so more
likely to be more performant.
This makes our `ifs` (or `&&`, `||`) to be compiled to either `if`,
ternary, `&&`, `||` or `switch`, making use of all the JS control flow
features.
lib/opal/ast/matcher.rb | 77 +++++++++++++++++
lib/opal/nodes/if.rb | 222 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 299 insertions(+)
create mode 100644 lib/opal/ast/matcher.rb
[user@localhost opal]#
This is bad news, since it was a big change :(
from opal.
diff --git a/lib/opal/nodes/if.rb b/lib/opal/nodes/if.rb
index ba8c302c1..109a026bc 100644
--- a/lib/opal/nodes/if.rb
+++ b/lib/opal/nodes/if.rb
@@ -252,6 +252,8 @@ module Opal
end
def could_become_switch?
+ return false
+
return false if expects_expression?
return true if sexp.meta[:switch_child]
This fixes the issue, but obviously this is not a solution. Let's try to find something better.
from opal.
Related Issues (20)
- Bug: opal -c with stdin stopped working
- Bug: const_defined? doesn't work properly with submodules
- Check zlib potential infinite loop HOT 1
- Bug: opal-repl with Chrome runner stopped working HOT 2
- Reminder: update top level 'source-map' used by linter
- Code duplication between stdlib/{nodejs,deno}/file.rb HOT 1
- Code duplication between chrome and firefox CDP runners
- Bug: file paths not included in ARGV when using `-e` HOT 1
- Bug: Wrong order of (JS) requires with prefork
- Bug: kwrestarg (**kwargs) doesn't support non-String (Symbol) keys HOT 3
- Feature: CLI tool --no-headless option
- Bug: Cannot rescue regexp syntax error
- don't use javascript block.length to determine number of arguments in block definition
- Bug: No backtrace on exceptions under Firefox HOT 6
- Feature: Opalfile
- Bug: Methods defined by `Module#define_method` with UnboundMethod do not accept a block
- Bug: jsid_cache breaks for `"toString"`, and I suspect other methods of JS Object HOT 6
- is it possible to require 'openssl' in opalrb? HOT 1
- is it possible to require 'openssl' in opalrb? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from opal.