Repository navigation
Add integer support to Rust JSG - #8072
Conversation
|
💬 Approved with comments — No defects found in the integer handles, the ToInt32/ToUint32/ToInteger helpers, the integer ToJS impls or the fast API integer paths. One README wording suggestion, plus a note that the jsg-bench fixture was only checked for type consistency. Findings: 1 info · 1 suggestion The decision is advisory: this comment never approves, blocks or requests changes on the pull request. 📋 Code Quality: 💬 1 infoNo defects found. One INFO note: the jsg-bench fixture was only checked for consistency. Recommendation: 💬 Optional Nits
View full review
This review checked 📝 Documentation: 💬 1 suggestionOne optional wording improvement in the JSG README Recommendation: 💬 Optional Nits
View full reviewThe README sections on integer conversions, the integer handles and the
✅ All other checks passed🔒 Security: ✅ LGTMThe coercion helpers go through 📖 AGENTS.md: ✅ LGTMNo AGENTS.md updates needed. ⚡ Performance: ✅ LGTM
Reviewed commit: |
|
Since last review: 0 resolved, 0 still open, 0 new. Not re-run: kj-style (no author changes in their files since the last review) Reviewed commit: 239740bc · github run |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #8072 +/- ##
==========================================
- Coverage 69.49% 69.38% -0.11%
==========================================
Files 695 696 +1
Lines 181564 181864 +300
Branches 29204 29213 +9
==========================================
+ Hits 126170 126182 +12
- Misses 38328 38615 +287
- Partials 17066 17067 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
668779c to
8219dd2
Compare
The Dict and function-length tests, and the Dict docs, refer to `jsg::Number`, a type that no longer exists, so `//src/rust/jsg-test` does not build. The values a test coerces are `f64`; the ones it adds, counts or makes are `u32`.
* `jsg::v8::Integer`, `v8::Int32` and `v8::Uint32` mirror the V8 classes: `Integer::new`/`new_from_unsigned`, `value()` on each, casts to and from `Number` and `Value` through `impl_local_cast!`, and a strict `FromJS` for the handles that accepts only a number in range, as `v8::Local<v8::Number>` accepts only a number * `Local<Value>::is_int32`/`is_uint32` (`IsInt32`/`IsUint32`); a `v8::Integer` is a number that is one or the other, as in rusty_v8, since V8 has no `IsInteger` * `Local<Value>::int32_value`, `uint32_value` and `integer_value` run `ToInt32`, `ToUint32` and `ToInteger` through `call_into_js`, so what `valueOf` throws stays pending and a termination is kept
* `ToJS` for `u8`..`u32` and `i8`..`i32` makes the number with `v8::Integer::New` or `NewFromUnsigned`, as C++ JSG does, instead of going through `f64` and `v8::Number::New` * `FastApiReturn` for the same six types: V8's `int32_t` and `uint32_t` (`FastApiType::Int32`/`Uint32`, `kInt32`/`kUint32`), the narrower types widened to one of the two, so a `#[jsg_method]` returning an integer gets a fast callback * `FastApiArg` for `i8`, `i16`, `u8` and `u16` as for `i32` and `u32`: V8 passes a double and `from_number` converts it, so the range checks and truncation are the slow path's. V8's own integer argument types convert differently (`ToInt32` wrapping, or truncation before the range check with `kEnforceRangeBit`), so they are not used * `jsg-macros` puts these types on its fast API lists
Counts, lengths, sums of bytes, identifiers and other integral values in the jsg-test suite, the jsg-bench fixture, the compile tests and the README examples are `u32`, `i32` or a narrower integer type, as parameters and as return values, so that `f64` is left for values that are numbers in general. * `f64` stays where a test is about `ToNumber` coercion, `NaN`, `-0` or fractional values, and for `Math`-like functions and `Float64Array`. * The jsg-bench fixture returns `u32` from Rust and `uint32_t` from C++ for the cases that count or sum, so both sides convert the same way; the cases whose result is a number in general keep `f64` and `double`. * The Dict tests and docs refer to `jsg::Number`, a type that no longer exists; they use `f64` where the value is coerced and `u32` where it is counted.
8219dd2 to
239740b
Compare
Follows #8054, which made the Rust JSG value types mirror their V8
classes and left numbers as
f64plus av8::Numberhandle. This addsthe integer side: the V8 integer classes as handle types, the
conversions V8 offers for them, integers made with
Integer::New, andinteger return values on the fast API. With that in place, the bindings
and tests use integers wherever the value is one, so that
f64is theexception rather than the rule.
Handle types and conversions:
jsg::v8::Integer,v8::Int32andv8::Uint32mirror the V8classes:
Integer::new/new_from_unsigned,value()on each, caststo and from
NumberandValue, and a strictFromJSfor thehandles that accepts only a number in range (so a method parameter
v8::Local<v8::Uint32>rejects-1and1.5), asv8::Local<v8::Number>accepts only a number.Local<Value>::is_int32/is_uint32. V8 has noIsInteger, so av8::Integeris a number that is one or the other, as in rusty_v8;Integer::Newcannot make anything else.Local<Value>::int32_value,uint32_valueandinteger_valuerunToInt32,ToUint32andToInteger. They go throughcall_into_js, so whatvalueOfthrows stays pending, a method thatpasses the error on rethrows it unchanged, and a termination is kept.
Integers to JavaScript and the fast API:
ToJSforu8..u32andi8..i32usesv8::Integer::NeworNewFromUnsigned, as C++ JSG does, instead of going throughf64and
v8::Number::New.FastApiReturnfor the same six types, as V8'sint32_tanduint32_t(kInt32/kUint32), with the narrower types widened toone of the two, so a
#[jsg_method]returning an integer gets a fastcallback.
i64/u64stay slow-pathBigInts; a fast API return forthem is not added.
FastApiArgfori8,i16,u8andu16as fori32andu32.Integer arguments keep coming from V8 as doubles, converted by
from_number, so the range checks and truncation are the slow path's.V8's integer argument types were considered and rejected: by default
they convert with
ToInt32's wrapping, and withkEnforceRangeBitthey truncate before the range check, so
2 ** 31 - 0.5would be anint32_tand-0.5auint32_ton the fast path while C++ JSG andthe slow path throw a
TypeErrorfor both.Audit of number types in the bindings and tests:
src/rust/apialready used integers throughout, so no binding thata Worker can reach changes type.
src/rust/jsg-test, counts, lengths, sums of bytes, ids, boundsand the struct fields, constants and properties that hold them are
u32,i32,u8oru16, as parameters and return values and inOption,NullableandVec, and the tests read them back asintegers.
f64stays where a test is aboutToNumbercoercion(
'2',null,valueOf,new Number(1)),NaN,-0, fractionalvalues,
Math-like functions,Float64Arrays or theDatemillisecond value.
u32from Rust anduint32_tfromC++ for the lengths, counts and sums, and keeps
f64anddoublefor the number, union, array, struct and callback cases, so both
sides of each comparison convert the same way.
u32for counters andlengths.
The first commit is separate from the rest: the Dict tests and docs
from #7998 and the function-length tests from #8045 refer to
jsg::Number, which #8054 removed, sojsg-testdoes not build onmainas it is. They usef64where the value is coerced andu32where it is added, counted or made.