Skip to content

Commit 998bbed

Browse files
authored
Allow macro state to resolve to the initial template context (#542)
1 parent 4eda9d2 commit 998bbed

File tree

5 files changed

+50
-2
lines changed

5 files changed

+50
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ All notable changes to MiniJinja are documented here.
1010
- Bumped the minimum version of `self_cell` to 1.0.4. #540
1111
- MiniJinja will now warn if the `serde` feature is disabled. This is in
1212
anticipation of removing the serde dependency in the future. #541
13+
- Improved an edge case with `State::resolve`. It now can resolve the
14+
initial template context in macro calls even if no closure has been
15+
created. #542
1316

1417
## 2.0.3
1518

minijinja/src/vm/context.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,15 @@ impl<'env> Context<'env> {
256256
self.stack.last_mut().unwrap().closure = closure;
257257
}
258258

259+
/// Return the base context value
260+
#[cfg(feature = "macros")]
261+
pub fn clone_base(&self) -> Value {
262+
self.stack
263+
.first()
264+
.map(|x| x.ctx.clone())
265+
.unwrap_or_default()
266+
}
267+
259268
/// Looks up a variable in the context.
260269
pub fn load(&self, env: &Environment, key: &str) -> Option<Value> {
261270
for frame in self.stack.iter().rev() {

minijinja/src/vm/macro_object.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ impl Object for Macro {
137137
instructions,
138138
*offset,
139139
closure,
140+
state.ctx.clone_base(),
140141
caller,
141142
&mut out,
142143
state,

minijinja/src/vm/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const INCLUDE_RECURSION_COST: usize = 10;
3838

3939
// the cost of a single macro call against the stack limit.
4040
#[cfg(feature = "macros")]
41-
const MACRO_RECURSION_COST: usize = 5;
41+
const MACRO_RECURSION_COST: usize = 4;
4242

4343
/// Helps to evaluate something.
4444
#[cfg_attr(feature = "internal_debug", derive(Debug))]
@@ -108,12 +108,14 @@ impl<'env> Vm<'env> {
108108
instructions: &Instructions<'env>,
109109
pc: usize,
110110
closure: Value,
111+
context_base: Value,
111112
caller: Option<Value>,
112113
out: &mut Output,
113114
state: &State,
114115
args: Vec<Value>,
115116
) -> Result<Option<Value>, Error> {
116-
let mut ctx = Context::new_with_frame(Frame::new(closure), self.env.recursion_limit());
117+
let mut ctx = Context::new_with_frame(Frame::new(context_base), self.env.recursion_limit());
118+
ok!(ctx.push_frame(Frame::new(closure)));
117119
if let Some(caller) = caller {
118120
ctx.store("caller", caller);
119121
}

minijinja/tests/test_macros.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,36 @@ fn test_caller_bug() {
214214
);
215215
assert_snapshot!(rv.trim(), @"42|23");
216216
}
217+
218+
/// https://github.com/mitsuhiko/minijinja/issues/535
219+
#[test]
220+
fn test_unenclosed_resolve() {
221+
// the current intended logic here is that a the state can
222+
// observe real globals and the initial template context, but
223+
// no other modifications. Normally the call block can only
224+
// see what it encloses explicitly, but since it does not
225+
// refer to anything here it in fact has an empty closure.
226+
227+
fn resolve(state: &minijinja::State, var: &str) -> Value {
228+
state.lookup(var).unwrap_or_default()
229+
}
230+
231+
let mut env = Environment::new();
232+
env.add_global("ctx_global", "ctx global");
233+
env.add_function("resolve", resolve);
234+
let rv = env
235+
.render_str(
236+
r#"
237+
{%- set template_global = 'template global' %}
238+
{%- macro wrapper() %}{{ caller() }}{% endmacro %}
239+
{%- call wrapper() %}
240+
{{- resolve('render_global') }}|
241+
{{- resolve('ctx_global') }}|
242+
{{- resolve('template_global') }}
243+
{%- endcall -%}
244+
"#,
245+
context! { render_global => "render global" },
246+
)
247+
.unwrap();
248+
assert_snapshot!(rv, @"render global|ctx global|");
249+
}

0 commit comments

Comments
 (0)