Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement toString #381

Merged
merged 4 commits into from
May 10, 2020
Merged

Implement toString #381

merged 4 commits into from
May 10, 2020

Conversation

dvtkrlbs
Copy link

@dvtkrlbs dvtkrlbs commented May 7, 2020

No description provided.

@dvtkrlbs
Copy link
Author

dvtkrlbs commented May 8, 2020

I implemented the initial version but the problem is i made some mistake while translating c++ to c (to further use c2rust) and in some cases produces wrong values for example Number(90).toString(16) should return '5a' but instead it returns '50'. I will try to debug it tomorrow. I think it is a problem in the integer part of the conversion.

Copy link
Member

@Razican Razican left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find where the issue was, but I added some suggestions, feel free to ignore those that seem unreasonable :)

boa/src/builtins/number/mod.rs Outdated Show resolved Hide resolved
boa/src/builtins/number/mod.rs Show resolved Hide resolved
// Insert decimal point.
let fresh0 = fraction_cursor;
fraction_cursor += 1;
buffer[fresh0] = b'.';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Rust, this indexing is very slow, since it will do bounds checking every time it accesses the buffer. I think we can somehow iterate through it using iterators instead of direct access, but then, the carry-overs can be problematic. Maybe @HalidOdat has some ideas on how to improve this performance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something that can be done here to at least increase the iteration for the integer part is this:

let (int_buf, frac_buf) = buffer.split_at_mut(BUF_SIZE / 2);

Then, in the integer iteration:

    let mut int_iter = int_buf.into_iter().rev();
    for c in int_iter {
        let remainder = integer % (radix as f64);
        *c = std::char::from_digit(remainder as u32, radix).unwrap() as u8;
        integer = (integer - remainder) / radix as f64;
        if integer <= 0_f64 {
            break;
        }
    }
    // Add sign and terminate string.
    if negative {
        *int_buf.next().expect("buffer ended") = b'-';
    }

This might need some mut-s somewhere around, and we need to save the index somehow, maybe by calling enumerate() in the iterator, but it should work. Maybe we can do something similar with the fractional part.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may also be able to replace some of the while loop using take_while.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fraction part is hard to refactor since there is a second pass to the buffer and implementation sometimes increments and sometimes decrements the cursor. Maybe we can do something with Rev but it will be complicated.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like bidir_iter might work better but problem is they use slice.get internally so we still pay for the bound checks. We might use unsafe get to not pay for the bound check since we check the bounds anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's good enough for now :) we can maybe improve it further in the future.

boa/src/builtins/number/mod.rs Outdated Show resolved Hide resolved
boa/src/builtins/number/mod.rs Outdated Show resolved Hide resolved
@dvtkrlbs
Copy link
Author

dvtkrlbs commented May 8, 2020

Ok using FloatCore::integer_decodeactually fixed the bug. 😄

Tunahan Karlibas added 2 commits May 8, 2020 23:07
…[radix] )

Implement the radix paramet for the `toString`. This implementation is
converted from the V8's c++ implementation.
Initial version for getting rid of direct slice accesses. Currently
converted integer part to iterators. Fraction part is a lot harder since
there are two passes to the fraction part (for carry over) and it is
hard to express that using iterators.
forward(&mut engine, "Number(0.0123).toString()")
);
// assert_eq!(String::from("111111111111111110000"), forward(&mut engine, "Number(111111111111111111111).toString()"));
// assert_eq!(String::from("1.1111111111111111e+21"), forward(&mut engine, "Number(1111111111111111111111).toString()"));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tests fails i think because rusts default formatting does not match the js spec.

Copy link
Member

@HalidOdat HalidOdat May 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tests fails i think because rusts default formatting does not match the js spec.

Yes. The rust formatter is not JavaScript spec compliant. Even using format!("{:e}", num) isn't sufficient.
We probably need to write our own f64 parser, like we did the one with bases. Here is V8 DoubleToCString

@dvtkrlbs dvtkrlbs marked this pull request as ready for review May 9, 2020 14:51
@dvtkrlbs
Copy link
Author

dvtkrlbs commented May 9, 2020

I was trying to resolve the conflicts and now it seems every number is interpreted as NaN. I dont it is a issue from the to_string method i just debug the value after calling to_number on it and it just prints NaN.

@dvtkrlbs
Copy link
Author

dvtkrlbs commented May 9, 2020

Ok my bad i apparently called to_number on this without calling to_num

Copy link
Member

@Razican Razican left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could improve how the tests are being written, to make them more clear.

Comment on lines 183 to 188
assert_eq!(default_string, String::from("0"));
assert_eq!(int_string, String::from("123"));
assert_eq!(float_string, String::from("1.234"));
assert_eq!(exp_string, String::from("12000"));
assert_eq!(neg_string, String::from("-1.2"));
assert_eq!(nan_string, String::from("NaN"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should make this easier to understand like this:

Suggested change
assert_eq!(default_string, String::from("0"));
assert_eq!(int_string, String::from("123"));
assert_eq!(float_string, String::from("1.234"));
assert_eq!(exp_string, String::from("12000"));
assert_eq!(neg_string, String::from("-1.2"));
assert_eq!(nan_string, String::from("NaN"));
assert_eq!(&default_string, "0");
assert_eq!(&int_string, "123");
assert_eq!(&float_string, "1.234");
assert_eq!(&exp_string, "12000");
assert_eq!(&neg_string, "-1.2");
assert_eq!(&nan_string, "NaN");

I think we should do this in all tests.

Comment on lines 266 to 271
// assert_eq!(String::from("1e-7"), forward(&mut engine, "Number(0.0000001).toString()"));
// assert_eq!(String::from("1.2e-7"), forward(&mut engine, "Number(0.00000012).toString()"));
// assert_eq!(String::from("1.23e-7"), forward(&mut engine, "Number(0.000000123).toString()"));
// assert_eq!(String::from("1e-8"), forward(&mut engine, "Number(0.00000001).toString()"));
// assert_eq!(String::from("1.2e-8"), forward(&mut engine, "Number(0.000000012).toString()"));
// assert_eq!(String::from("1.23e-8"), forward(&mut engine, "Number(0.0000000123).toString()"));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing these tests don't pass wither, right? If that's the case, I would isolate them in a different test and add the #[ignore] attribute to it, to know that we must fix them in the future.

Comment on lines 274 to 275
String::from("NaN"),
forward(&mut engine, "Number(NaN).toString(16)")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change all these to have this syntax:

Suggested change
String::from("NaN"),
forward(&mut engine, "Number(NaN).toString(16)")
"NaN",
&forward(&mut engine, "Number(NaN).toString(16)")

@Razican Razican added this to the v0.8.0 milestone May 9, 2020
Copy link
Member

@Razican Razican left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is good to go!

Copy link
Member

@HalidOdat HalidOdat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

@Razican Razican merged commit bdad99c into boa-dev:master May 10, 2020
@dvtkrlbs dvtkrlbs deleted the number-toString branch May 10, 2020 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement optional parameter radix for Number.prototype.toString( [radix] )
3 participants