Skip to content

Commit 3ead0e6

Browse files
committed
Implement short-circuiting
1 parent 6bef91d commit 3ead0e6

4 files changed

Lines changed: 110 additions & 16 deletions

File tree

packages/@glimmer-workspace/integration-tests/test/keywords/and-test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,40 @@ import { template } from '@ember/template-compiler';
66
class KeywordAnd extends RenderTest {
77
static suiteName = 'keyword helper: and';
88

9+
@test
10+
'references are lazy'(assert: Assert) {
11+
const obj = {
12+
get a() {
13+
assert.step('a');
14+
return 1;
15+
},
16+
get b() {
17+
assert.step('b');
18+
return 89;
19+
},
20+
get c() {
21+
assert.step('c');
22+
return false;
23+
},
24+
get d() {
25+
assert.step('d');
26+
return 'unexpected!!!';
27+
},
28+
};
29+
30+
const compiled = template('{{and obj.a obj.b obj.c obj.d}}', {
31+
strictMode: true,
32+
scope: () => ({ obj }),
33+
});
34+
35+
this.renderComponent(compiled);
36+
this.assertHTML('false');
37+
assert.verifySteps(
38+
['a', 'b', 'c'],
39+
'd not evaluated because obj.c was the last to be evaluated and short-circuited'
40+
);
41+
}
42+
943
@test
1044
'explicit scope'() {
1145
let a = 'yes';

packages/@glimmer-workspace/integration-tests/test/keywords/or-test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,40 @@ import { template } from '@ember/template-compiler';
66
class KeywordOr extends RenderTest {
77
static suiteName = 'keyword helper: or';
88

9+
@test
10+
'references are lazy'(assert: Assert) {
11+
const obj = {
12+
get a() {
13+
assert.step('a');
14+
return false;
15+
},
16+
get b() {
17+
assert.step('b');
18+
return null;
19+
},
20+
get c() {
21+
assert.step('c');
22+
return 2;
23+
},
24+
get d() {
25+
assert.step('d');
26+
return 'unexpected!!!';
27+
},
28+
};
29+
30+
const compiled = template('{{or obj.a obj.b obj.c obj.d}}', {
31+
strictMode: true,
32+
scope: () => ({ obj }),
33+
});
34+
35+
this.renderComponent(compiled);
36+
this.assertHTML('2');
37+
assert.verifySteps(
38+
['a', 'b', 'c'],
39+
'd not evaluated because obj.c was the last to be evaluated and short-circuited'
40+
);
41+
}
42+
943
@test
1044
'explicit scope'() {
1145
let a = false;
Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
import { DEBUG } from '@glimmer/env';
2+
import type { CapturedArguments } from '@glimmer/interfaces';
23
import { toBool } from '@glimmer/global-context';
4+
import { createComputeRef, valueForRef } from '@glimmer/reference';
35

4-
export const and = (...args: unknown[]) => {
5-
if (DEBUG && args.length < 2) {
6-
throw new Error(`\`and\` expects at least two arguments, but received ${args.length}.`);
7-
}
6+
import { internalHelper } from './internal-helper';
87

9-
for (let i = 0; i < args.length; i++) {
10-
if (!toBool(args[i])) return args[i];
8+
export const and = internalHelper(({ positional }: CapturedArguments) => {
9+
if (DEBUG && positional.length < 2) {
10+
throw new Error(`\`and\` expects at least two arguments, but received ${positional.length}.`);
1111
}
12-
return args[args.length - 1];
13-
};
12+
13+
return createComputeRef(
14+
() => {
15+
let last: unknown;
16+
for (let i = 0; i < positional.length; i++) {
17+
let arg = positional[i];
18+
last = arg ? valueForRef(arg) : arg;
19+
if (!toBool(last)) return last;
20+
}
21+
return last;
22+
},
23+
null,
24+
'and'
25+
);
26+
});
Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
import { DEBUG } from '@glimmer/env';
2+
import type { CapturedArguments } from '@glimmer/interfaces';
23
import { toBool } from '@glimmer/global-context';
4+
import { createComputeRef, valueForRef } from '@glimmer/reference';
35

4-
export const or = (...args: unknown[]) => {
5-
if (DEBUG && args.length < 2) {
6-
throw new Error(`\`or\` expects at least two arguments, but received ${args.length}.`);
7-
}
6+
import { internalHelper } from './internal-helper';
87

9-
for (let i = 0; i < args.length; i++) {
10-
if (toBool(args[i])) return args[i];
8+
export const or = internalHelper(({ positional }: CapturedArguments) => {
9+
if (DEBUG && positional.length < 2) {
10+
throw new Error(`\`or\` expects at least two arguments, but received ${positional.length}.`);
1111
}
12-
return args[args.length - 1];
13-
};
12+
13+
return createComputeRef(
14+
() => {
15+
let last: unknown;
16+
for (let i = 0; i < positional.length; i++) {
17+
let arg = positional[i];
18+
last = arg ? valueForRef(arg) : arg;
19+
if (toBool(last)) return last;
20+
}
21+
return last;
22+
},
23+
null,
24+
'or'
25+
);
26+
});

0 commit comments

Comments
 (0)