Skip to content

Commit 43e37c0

Browse files
committed
Clean up the scope after resolving a term reference
1 parent d5e06c1 commit 43e37c0

File tree

2 files changed

+29
-29
lines changed

2 files changed

+29
-29
lines changed

fluent-bundle/src/resolver.js

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ function getArguments(scope, args) {
8888
}
8989
}
9090

91-
return [positional, named];
91+
return {positional, named};
9292
}
9393

9494
// Resolve an expression to a Fluent type.
@@ -117,15 +117,23 @@ function resolveExpression(scope, expr) {
117117

118118
// Resolve a reference to a variable.
119119
function VariableReference(scope, {name}) {
120-
if (!scope.args || !scope.args.hasOwnProperty(name)) {
121-
if (scope.insideTermReference === false) {
122-
scope.reportError(new ReferenceError(`Unknown variable: $${name}`));
120+
let arg;
121+
if (scope.params) {
122+
// We're inside a TermReference. It's OK to reference undefined parameters.
123+
if (scope.params.hasOwnProperty(name)) {
124+
arg = scope.params[name];
125+
} else {
126+
return new FluentNone(`$${name}`);
123127
}
128+
} else if (scope.args && scope.args.hasOwnProperty(name)) {
129+
// We're in the top-level Pattern or inside a MessageReference. Missing
130+
// variables references produce ReferenceErrors.
131+
arg = scope.args[name];
132+
} else {
133+
scope.reportError(new ReferenceError(`Unknown variable: $${name}`));
124134
return new FluentNone(`$${name}`);
125135
}
126136

127-
const arg = scope.args[name];
128-
129137
// Return early if the argument already is an instance of FluentType.
130138
if (arg instanceof FluentType) {
131139
return arg;
@@ -183,20 +191,23 @@ function TermReference(scope, {name, attr, args}) {
183191
return new FluentNone(id);
184192
}
185193

186-
// Every TermReference has its own variables.
187-
const [, params] = getArguments(scope, args);
188-
const local = scope.cloneForTermReference(params);
189-
190194
if (attr) {
191195
const attribute = term.attributes[attr];
192196
if (attribute) {
193-
return resolvePattern(local, attribute);
197+
// Every TermReference has its own variables.
198+
scope.params = getArguments(scope, args).named;
199+
const resolved = resolvePattern(scope, attribute);
200+
scope.params = null;
201+
return resolved;
194202
}
195203
scope.reportError(new ReferenceError(`Unknown attribute: ${attr}`));
196204
return new FluentNone(`${id}.${attr}`);
197205
}
198206

199-
return resolvePattern(local, term.value);
207+
scope.params = getArguments(scope, args).named;
208+
const resolved = resolvePattern(scope, term.value);
209+
scope.params = null;
210+
return resolved;
200211
}
201212

202213
// Resolve a call to a Function with positional and key-value arguments.
@@ -215,7 +226,8 @@ function FunctionReference(scope, {name, args}) {
215226
}
216227

217228
try {
218-
return func(...getArguments(scope, args));
229+
let resolved = getArguments(scope, args);
230+
return func(resolved.positional, resolved.named);
219231
} catch (err) {
220232
scope.reportError(err);
221233
return new FluentNone(`${name}()`);

fluent-bundle/src/scope.js

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
export default class Scope {
2-
constructor(
3-
bundle,
4-
errors,
5-
args,
6-
dirty = new WeakSet()
7-
) {
2+
constructor(bundle, errors, args) {
83
/** The bundle for which the given resolution is happening. */
94
this.bundle = bundle;
105
/** The list of errors collected while resolving. */
@@ -14,21 +9,14 @@ export default class Scope {
149

1510
/** The Set of patterns already encountered during this resolution.
1611
* Used to detect and prevent cyclic resolutions. */
17-
this.dirty = dirty;
18-
/** Term references require different variable lookup logic. */
19-
this.insideTermReference = false;
12+
this.dirty = new WeakSet();
13+
/** A dict of parameters passed to a TermReference. */
14+
this.params = null;
2015
/** The running count of placeables resolved so far. Used to detect the
2116
* Billion Laughs and Quadratic Blowup attacks. */
2217
this.placeables = 0;
2318
}
2419

25-
cloneForTermReference(args) {
26-
let scope = new Scope(this.bundle, this.errors, args, this.dirty);
27-
scope.insideTermReference = true;
28-
scope.placeables = this.placeables;
29-
return scope;
30-
}
31-
3220
reportError(error) {
3321
if (!this.errors) {
3422
throw error;

0 commit comments

Comments
 (0)