Skip to content

Commit 66dedb3

Browse files
nhancodesnonbotsnekevss
authored
Add Url Searchparams to ConformanceView (#197)
* feat-search-params * prettier and lint * conformance/index move updateInitialConformanceState above useEffect * Update src/components/conformance/utils.ts guard clause for when `testPath` only contains version Co-authored-by: Kevin Ness <[email protected]> * search param fix * fix/refactor: default to version main when version is undefine --------- Co-authored-by: nonbots <non@bot> Co-authored-by: Kevin Ness <[email protected]>
1 parent efa9762 commit 66dedb3

File tree

10 files changed

+198
-119
lines changed

10 files changed

+198
-119
lines changed

blog/2025-03-05-local-variables.md

Lines changed: 82 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ title: "How ECMAScript Engines Optimize Your Variables"
55
authors: boa-dev
66
---
77

8-
In this post, we will dive into how ECMAScript engines store variables,
8+
In this post, we will dive into how ECMAScript engines store variables,
99
go over storage optimizations, and learn about scope analysis.
1010
If you are an ECMAScript developer, you will get some practical tips to improve the performance of your code.
1111
If you write your own ECMAScript engine or any interpreter/compiler, you might get some implementation ideas.
@@ -33,9 +33,10 @@ Let's look at an example to visualize scopes:
3333
const a = 1;
3434
console.log(a); // 1
3535

36-
{ // <- start of a block scope
37-
const a = 2;
38-
console.log(a); // 2
36+
{
37+
// <- start of a block scope
38+
const a = 2;
39+
console.log(a); // 2
3940
} // <- end of a block scope
4041
```
4142

@@ -53,9 +54,9 @@ Let's modify our example to see what happens in that case:
5354
const a = 1;
5455

5556
{
56-
const b = 2;
57-
console.log(a); // 1
58-
console.log(b); // 2
57+
const b = 2;
58+
console.log(a); // 1
59+
console.log(b); // 2
5960
}
6061
```
6162

@@ -71,16 +72,18 @@ Let's look at a more complex example:
7172
const a = 1;
7273
console.log(a); // 1
7374

74-
function f() { // <- start of a function scope
75-
var a = 2;
76-
console.log(a); // 2
75+
function f() {
76+
// <- start of a function scope
77+
var a = 2;
78+
console.log(a); // 2
7779

78-
{ // <- start of a block scope
79-
let a = 3;
80-
console.log(a); // 3
81-
} // <- end of a block scope
80+
{
81+
// <- start of a block scope
82+
let a = 3;
83+
console.log(a); // 3
84+
} // <- end of a block scope
8285

83-
console.log(a); // 2
86+
console.log(a); // 2
8487
} // <- end of a function scope
8588

8689
f();
@@ -104,9 +107,9 @@ For our proposes we will only work with `let` and `const` and skip those details
104107
When developing an ECMAScript engine we have to think about how we store and access scopes and variables.
105108
Take a look at the requirements we have for that storage data structure:
106109

107-
* A variable maps an identifier to a value.
108-
* A scope can have multiple variables with unique identifiers.
109-
* A scope may have an outer scope.
110+
- A variable maps an identifier to a value.
111+
- A scope can have multiple variables with unique identifiers.
112+
- A scope may have an outer scope.
110113

111114
The variables in a scope fit a typical key-value store, like a hashmap.
112115
The hashmap stores our variable identifiers as keys and the variable values as corresponding values:
@@ -159,8 +162,8 @@ Let's visualize this in an example:
159162
```js
160163
const a = 1; // scope index: 0; variable index: 0
161164
{
162-
const b = 2; // scope index: 1; variable index: 0
163-
const c = 3; // scope index: 1; variable index: 1
165+
const b = 2; // scope index: 1; variable index: 0
166+
const c = 3; // scope index: 1; variable index: 1
164167
}
165168
```
166169

@@ -172,10 +175,10 @@ Let's explore how unique these indices have to be:
172175

173176
```js
174177
{
175-
const a = 1; // scope index: 1; variable index: 0
178+
const a = 1; // scope index: 1; variable index: 0
176179
}
177180
{
178-
const b = 2; // scope index: 1; variable index: 0
181+
const b = 2; // scope index: 1; variable index: 0
179182
}
180183
```
181184

@@ -227,8 +230,8 @@ Let's take a look at this example:
227230

228231
```js
229232
function addOne(a) {
230-
const one = 1;
231-
return one + a;
233+
const one = 1;
234+
return one + a;
232235
}
233236
addOne(2);
234237
```
@@ -260,10 +263,10 @@ Let's look at this example:
260263

261264
```js
262265
function addOneBuilder() {
263-
const one = 1;
264-
return (a) => {
265-
return one + a;
266-
};
266+
const one = 1;
267+
return (a) => {
268+
return one + a;
269+
};
267270
}
268271
const addOne = addOneBuilder();
269272
addOne(2);
@@ -327,10 +330,10 @@ Let's visualize the scope analysis by writing out the scopes for this example:
327330

328331
```js
329332
function addOneBuilder() {
330-
const one = 1;
331-
return (a) => {
332-
return one + a;
333-
};
333+
const one = 1;
334+
return (a) => {
335+
return one + a;
336+
};
334337
}
335338
```
336339

@@ -396,69 +399,69 @@ Without going into detail on each of these cases, we can find all of them via sc
396399
Here is a quick overview:
397400

398401
- Non `strict` functions create a mapped `arguments` object.
399-
The mapped `arguments` object can be used to read and write function arguments without using their identifiers.
400-
The reads and writes are kept in sync with the values of the argument variables.
401-
This means that we cannot determine if the argument variables are accessed from outside the function.
402-
403-
An example of such a situation would be this code:
404-
405-
```js
406-
function f(a) {
407-
console.log(a); // initial
408-
(() => {
409-
arguments[0] = "modified";
410-
})()
411-
console.log(a); // modified
412-
}
413-
f("initial");
414-
```
402+
The mapped `arguments` object can be used to read and write function arguments without using their identifiers.
403+
The reads and writes are kept in sync with the values of the argument variables.
404+
This means that we cannot determine if the argument variables are accessed from outside the function.
415405

416-
The solution here is to mark every argument variable that might be accessed through a mapped `arguments` object as non-local.
406+
An example of such a situation would be this code:
417407

418-
- Direct calls to `eval` allow potential variable access.
419-
Direct calls to `eval` have access to the current variables.
420-
Since any code could be executed in `eval` we cannot do proper scope analysis on any variables in such cases.
408+
```js
409+
function f(a) {
410+
console.log(a); // initial
411+
(() => {
412+
arguments[0] = "modified";
413+
})();
414+
console.log(a); // modified
415+
}
416+
f("initial");
417+
```
421418

422-
An example of direct `eval` usage would be this:
419+
The solution here is to mark every argument variable that might be accessed through a mapped `arguments` object as non-local.
423420

424-
```js
425-
function f() {
426-
const a = 1;
427-
eval("function nested() {console.log(a)}; nested();");
428-
}
429-
f();
430-
```
421+
- Direct calls to `eval` allow potential variable access.
422+
Direct calls to `eval` have access to the current variables.
423+
Since any code could be executed in `eval` we cannot do proper scope analysis on any variables in such cases.
431424

432-
Our solution is this case is to mark every variable in the scopes where the direct `eval` call is as non-local.
425+
An example of direct `eval` usage would be this:
433426

434-
- Usage of the `with` statement.
435-
Variable identifiers inside a `with` statement are not static.
436-
A variable identifier could be the access to a variable, but it also could be the access to an object property.
427+
```js
428+
function f() {
429+
const a = 1;
430+
eval("function nested() {console.log(a)}; nested();");
431+
}
432+
f();
433+
```
437434

438-
See this example:
435+
Our solution is this case is to mark every variable in the scopes where the direct `eval` call is as non-local.
439436

440-
```js
441-
function f() {
442-
const a1 = 1;
443-
for (let i = 0; i < 2; i++) {
444-
with ({ [`a${i}`]: 2 }) {
445-
console.log(a1);
446-
}
447-
}
437+
- Usage of the `with` statement.
438+
Variable identifiers inside a `with` statement are not static.
439+
A variable identifier could be the access to a variable, but it also could be the access to an object property.
440+
441+
See this example:
442+
443+
```js
444+
function f() {
445+
const a1 = 1;
446+
for (let i = 0; i < 2; i++) {
447+
with ({ [`a${i}`]: 2 }) {
448+
console.log(a1);
449+
}
448450
}
449-
f();
450-
```
451+
}
452+
f();
453+
```
451454

452-
In the first loop execution `a1` is the variable.
453-
In the second loop execution `a1` is the object property.
454-
As a result of this behavior, every variable accessed inside a `with` statement cannot be local.
455+
In the first loop execution `a1` is the variable.
456+
In the second loop execution `a1` is the object property.
457+
As a result of this behavior, every variable accessed inside a `with` statement cannot be local.
455458

456459
## Conclusion
457460

458461
After implementing local variables in Boa, we saw significant performance improvements in our benchmarks.
459462
Our overall benchmark scope improved by more than 25%.
460463
In one specific benchmark the scope increased by over 70%.
461-
Notice that Boa is not the most performant engine yet.
464+
Notice that Boa is not the most performant engine yet.
462465
There are probably other optimizations relating to variable storage that we have not implemented yet.
463466

464467
Hopefully, you might have already picked up some practical tips to potentially improve to performance of your ECMAScript code.

blog/2025-06-15-temporal-impl-1.md

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
---
22
layout: post
33
tags: [post]
4-
title:
5-
Implementing Temporal, the new date/time API for JavaScript (and
4+
title: Implementing Temporal, the new date/time API for JavaScript (and
65
Rust!)
76
metadata: ["temporal", "temporal_rs", "boa", "date/time"]
8-
description:
9-
A blog post about the temporal_rs Rust crate that implements
7+
description: A blog post about the temporal_rs Rust crate that implements
108
JavaScript's Temporal API and how temporal_rs supports implementing
119
Temporal in JavaScript engines.
1210
authors: boa-dev
@@ -472,12 +470,9 @@ A FFI version of temporal is also available for C and C++ via
472470

473471
The above issues are considered blocking for a 0.1.0 release.
474472

475-
[mdn-temporal]:
476-
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal
473+
[mdn-temporal]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal
477474
[temporal-rs-repo]: https://github.com/boa-dev/temporal
478-
[construct-link]:
479-
https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-ecmascript-function-objects-construct-argumentslist-newtarget
480-
[call-link]:
481-
https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-ecmascript-function-objects-call-thisargument-argumentslist
475+
[construct-link]: https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-ecmascript-function-objects-construct-argumentslist-newtarget
476+
[call-link]: https://tc39.es/ecma262/multipage/ordinary-and-exotic-objects-behaviours.html#sec-ecmascript-function-objects-call-thisargument-argumentslist
482477
[boa-test262]: https://test262.fyi/#|boa
483478
[temporal-capi]: https://crates.io/crates/temporal_capi

src/components/benchmarks/index.tsx

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const ChartOptions = {
4343

4444
export const BenchmarkGraphs: React.FC<BenchmarkGraphsProps> = ({
4545
selectedEngines,
46-
range
46+
range,
4747
}) => {
4848
// Control the state of which engines are displayed using a Set
4949

@@ -52,20 +52,20 @@ export const BenchmarkGraphs: React.FC<BenchmarkGraphsProps> = ({
5252
const colorMode = useColorMode();
5353
ChartJS.defaults.color = colorMode.colorMode === "light" ? "#666" : "white";
5454

55-
5655
useEffect(() => {
57-
fetch(`https://boa-api.jason-williams.co.uk/benchmarks?months=${range}&engines=${selectedEngines.join(',')}`).then(
58-
(res) => res.json())
59-
.then(respData => {
60-
setData(respData)
61-
});
56+
fetch(
57+
`https://boa-api.jason-williams.co.uk/benchmarks?months=${range}&engines=${selectedEngines.join(",")}`,
58+
)
59+
.then((res) => res.json())
60+
.then((respData) => {
61+
setData(respData);
62+
});
6263
}, [selectedEngines, range]);
6364

6465
useEffect(() => {
6566
setCharts(buildChartFromBenchmark(data));
6667
}, [data]);
6768

68-
6969
return charts && charts.map((chart) => chart);
7070
};
7171

@@ -74,19 +74,18 @@ const normalizeBenchmarkData = (benchmarkData: any[]) => {
7474
new Date(entry.date).toLocaleDateString(),
7575
);
7676

77-
const engines = Object.keys(benchmarkData[0]).filter(key => key != "date");
77+
const engines = Object.keys(benchmarkData[0]).filter((key) => key != "date");
7878

7979
return {
8080
labels,
81-
datasets: engines.map(engine => ({
81+
datasets: engines.map((engine) => ({
8282
label: engine,
83-
data: benchmarkData.map(entry => entry[engine]),
84-
fill: false
83+
data: benchmarkData.map((entry) => entry[engine]),
84+
fill: false,
8585
})),
8686
};
8787
};
8888

89-
9089
const getBarChartData = (data) => {
9190
// We only want the last value from each dataset
9291
return {
@@ -101,12 +100,11 @@ const getBarChartData = (data) => {
101100
};
102101

103102
const buildChartFromBenchmark = (data: any): any[] => {
104-
105103
let charts = [];
106104
for (const benchmark in data) {
107-
const normalizedData = normalizeBenchmarkData(data[benchmark])
105+
const normalizedData = normalizeBenchmarkData(data[benchmark]);
108106
const barData = getBarChartData(normalizedData);
109-
charts.push((
107+
charts.push(
110108
<div key={benchmark}>
111109
<div className={`card__header ${styles["benchmark-card-header"]}`}>
112110
<Heading as="h2">{benchmark}</Heading>
@@ -119,13 +117,9 @@ const buildChartFromBenchmark = (data: any): any[] => {
119117
<Bar data={barData} options={ChartOptions}></Bar>
120118
</div>
121119
</div>
122-
</div>
123-
));
124-
};
120+
</div>,
121+
);
122+
}
125123

126124
return charts;
127125
};
128-
129-
130-
131-

src/components/conformance/HeroBanner/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
import {
88
createState,
99
mapToTestStats,
10+
createSearchParams,
1011
} from "@site/src/components/conformance/utils";
1112
import { useHistory } from "@docusaurus/router";
1213
import Heading from "@theme/Heading";
@@ -86,6 +87,7 @@ function BannerCard(props) {
8687
className="button button--block button--primary"
8788
onClick={() =>
8889
history.push({
90+
search: createSearchParams(props.item),
8991
pathname: "/conformance",
9092
state: createState(props.item),
9193
})

0 commit comments

Comments
 (0)