From d0d9add20c52facce49c8c30642e25f4480d57cb Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Wed, 8 Dec 2021 17:47:01 -0800 Subject: [PATCH] Do not propagate user-code across Abstract Closure boundaries (#382) --- src/Spec.ts | 2 +- src/Xref.ts | 20 +++++++++++++--- .../generated-reference/effect-user-code.html | 12 ++++++++++ test/baselines/sources/effect-user-code.html | 24 +++++++++++++++++++ 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/Spec.ts b/src/Spec.ts index 13d04f89..1b93813b 100644 --- a/src/Spec.ts +++ b/src/Spec.ts @@ -474,7 +474,7 @@ export default class Spec { this.autolink(); - this.log('Propagating can-call-user-code annotations...'); + this.log('Propagating effect annotations...'); this.propagateEffects(); this.log('Linking xrefs...'); this._xrefs.forEach(xref => xref.build()); diff --git a/src/Xref.ts b/src/Xref.ts index 21dc6fae..32233742 100644 --- a/src/Xref.ts +++ b/src/Xref.ts @@ -89,8 +89,22 @@ export default class Xref extends Builder { canHaveEffect(effectName: string) { if (!this.isInvocation) return false; - if (this.clause && !this.clause.canHaveEffect(effectName)) { - return false; + if (this.clause) { + // Xrefs nested inside Abstract Closures should not propagate the + // user-code effect, since those are effectively nested functions. + // + // Calls to Abstract Closures that can call user code must be explicitly + // marked as such with .... + for (let node = this.node; node.parentElement; node = node.parentElement) { + const parent = node.parentElement; + // This is super hacky. It's checking the output of ecmarkdown. + if (parent.tagName === 'LI' && parent.textContent?.includes('be a new Abstract Closure')) { + return false; + } + } + if (!this.clause.canHaveEffect(effectName)) { + return false; + } } if (this.suppressEffects !== null) { return !this.suppressEffects.includes(effectName); @@ -223,7 +237,7 @@ export default class Xref extends Builder { } if (this.addEffects !== null) { if (effects !== null) { - effects.push(...this.addEffects); + effects = effects.concat(...this.addEffects); } else { effects = this.addEffects; } diff --git a/test/baselines/generated-reference/effect-user-code.html b/test/baselines/generated-reference/effect-user-code.html index d883b1e5..ced6cc64 100644 --- a/test/baselines/generated-reference/effect-user-code.html +++ b/test/baselines/generated-reference/effect-user-code.html @@ -90,4 +90,16 @@

15 RenderedMeta()

The abstract operation RenderedMeta takes no arguments. emu-meta tags with the effects attribute that aren't surrounding what ecmarkup recognizes as invocations are changed into span tags to be rendered. The effects list is prefixed with e- and changed into class names. It performs the following steps when called:

  1. Perform ? O.[[Call]]().
+ + +

16 MakeAbstractClosure()

+

The abstract operation MakeAbstractClosure takes no arguments. The user-code effect doesn't propagate through Abstract Closure boundaries by recognizing the "be a new Abstract Closure" substring. It performs the following steps when called:

+
  1. Let closure be a new Abstract Closure that captures nothing and performs the following steps when called:
    1. UserCode().
  2. Return closure.
+
+ + +

17 CallMakeAbstractClosure()

+

The abstract operation CallMakeAbstractClosure takes no arguments. The user-code effect doesn't propagate through Abstract Closure boundaries by recognizing the "be a new Abstract Closure" substring. It performs the following steps when called:

+
  1. MakeAbstractClosure().
+
\ No newline at end of file diff --git a/test/baselines/sources/effect-user-code.html b/test/baselines/sources/effect-user-code.html index 647ff438..f1612011 100644 --- a/test/baselines/sources/effect-user-code.html +++ b/test/baselines/sources/effect-user-code.html @@ -166,3 +166,27 @@

RenderedMeta()

1. Perform ? _O_.[[Call]](). + + +

MakeAbstractClosure()

+
+
description
+
The user-code effect doesn't propagate through Abstract Closure boundaries by recognizing the "be a new Abstract Closure" substring.
+
+ + 1. Let _closure_ be a new Abstract Closure that captures nothing and performs the following steps when called: + 1. UserCode(). + 1. Return _closure_. + +
+ + +

CallMakeAbstractClosure()

+
+
description
+
The user-code effect doesn't propagate through Abstract Closure boundaries by recognizing the "be a new Abstract Closure" substring.
+
+ + 1. MakeAbstractClosure(). + +