Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Maximum stylus call stack size exceeded when using display: flex #2771

Open
dlants opened this issue Mar 6, 2023 · 2 comments
Open

Maximum stylus call stack size exceeded when using display: flex #2771

dlants opened this issue Mar 6, 2023 · 2 comments

Comments

@dlants
Copy link

dlants commented Mar 6, 2023

in a fresh directory:

$ npm --version
8.15.0
$ node --version
v16.17.0
$ npm init
$ npm install stylus nib
stylus: 0.59.0
nib: 1.2.0

Then create two files - an empty a.styl

touch a.styl

And put the following into b.styl:

.b
  display: flex

Then try and run

npx stylus --include "./node_modules/nib/lib" --import nib a.styl b.styl

Some runs succeed, but every 2-3 times I run that command I get a RangeError Maximum stylus call stack size exceeded:

/test/node_modules/stylus/bin/stylus:684
              throw err;
              ^

RangeError: /test/node_modules/stylus/lib/functions/index.styl:266:5
   262| add-property(name, expr)
   263|   if mixin
   264|     {name} expr
   265|   else
   266|     add-property-function(name, expr)
------------^
   267|
   268| prefix-classes(prefix)
   269|   -prefix-classes(prefix, block)

Maximum stylus call stack size exceeded
    at display() (node_modules/nib/lib/nib/flex.styl:12:1)
    at ".dcg-shared-modal-transition-container" (b.styl:2:1)

    at Evaluator.visitCall (/test/node_modules/stylus/lib/visitor/evaluator.js:498:11)
    at Evaluator.Visitor.visit (/test/node_modules/stylus/lib/visitor/index.js:28:40)
    at Evaluator.visit (/test/node_modules/stylus/lib/visitor/evaluator.js:160:18)
    at Evaluator.visitExpression (/test/node_modules/stylus/lib/visitor/evaluator.js:644:26)
    at Evaluator.Visitor.visit (/test/node_modules/stylus/lib/visitor/index.js:28:40)
    at Evaluator.visit (/test/node_modules/stylus/lib/visitor/evaluator.js:160:18)
    at Evaluator.visitBlock (/test/node_modules/stylus/lib/visitor/evaluator.js:720:39)
    at Evaluator.Visitor.visit (/test/node_modules/stylus/lib/visitor/index.js:28:40)
    at Evaluator.visit (/src/test/node_modules/stylus/lib/visitor/evaluator.js:160:18)
    at Evaluator.visitIf (/test/node_modules/stylus/lib/visitor/evaluator.js:823:22) {
  lineno: 266,
  column: 5,
  filename: '/test/node_modules/stylus/lib/functions/index.styl',
  stylusStack: '    at display() (node_modules/nib/lib/nib/flex.styl:12:1)\n' +
    '    at ".dcg-shared-modal-transition-container" (b.styl:2:1)',
  input: 'called-from = ()\n' +
    '\n' +
    'vendors = moz webkit o ms official\n' +
    '\n' +
    '// stringify the given arg\n' +
    '\n' +
    '-string(arg)\n' +
    "  type(arg) + ' ' + arg\n" +
    '\n' +
    '// require a color\n' +
    '\n' +
    'require-color(color)\n' +
    "  unless color is a 'color'\n" +
    "    error('RGB or HSL value expected, got a ' + -string(color))\n" +
    '\n' +
    '// require a unit\n' +
    '\n' +
    'require-unit(n)\n' +
    "  unless n is a 'unit'\n" +
    "    error('unit expected, got a ' + -string(n))\n" +
    '\n' +
    '// require a string\n' +
    '\n' +
    'require-string(str)\n' +
    "  unless str is a 'string' or str is a 'ident'\n" +
    "    error('string expected, got a ' + -string(str))\n" +
    '\n' +
    '// Math functions\n' +
    '\n' +
    "abs(n) { math(n, 'abs') }\n" +
    'min(a, b) { a < b ? a : b }\n' +
    'max(a, b) { a > b ? a : b }\n' +
    '\n' +
    '// Trigonometrics\n' +
    "PI = -math-prop('PI')\n" +
    '\n' +
    'radians-to-degrees(angle)\n' +
    '  angle * (180 / PI)\n' +
    '\n' +
    'degrees-to-radians(angle)\n' +
    '  angle * (PI / 180)\n' +
    '\n' +
    'sin(n)\n' +
    "  n = unit(n) == 'deg' ? degrees-to-radians(unit(n, '')) : unit(n, '')\n" +
    "  round(math(n, 'sin'), 9)\n" +
    '\n' +
    'cos(n)\n' +
    "  n = unit(n) == 'deg' ? degrees-to-radians(unit(n, '')) : unit(n, '')\n" +
    "  round(math(n, 'cos'), 9)\n" +
    '\n' +
    '// Rounding Math functions\n' +
    '\n' +
    'ceil(n, precision = 0)\n' +
    '  multiplier = 10 ** precision\n' +
    "  math(n * multiplier, 'ceil') / multiplier\n" +
    '\n' +
    'floor(n, precision = 0)\n' +
    '  multiplier = 10 ** precision\n' +
    "  math(n * multiplier, 'floor') / multiplier\n" +
    '\n' +
    'round(n, precision = 0)\n' +
    '  multiplier = 10 ** precision\n' +
    "  math(n * multiplier, 'round') / multiplier\n" +
    '\n' +
    '// return the sum of the given numbers\n' +
    '\n' +
    'sum(nums)\n' +
    '  sum = 0\n' +
    '  sum += n for n in nums\n' +
    '\n' +
    '// return the average of the given numbers\n' +
    '\n' +
    'avg(nums)\n' +
    '  sum(nums) / length(nums)\n' +
    '\n' +
    '// return a unitless number, or pass through\n' +
    '\n' +
    'remove-unit(n)\n' +
    '  if typeof(n) is "unit"\n' +
    '    unit(n, "")\n' +
    '  else\n' +
    '    n\n' +
    '\n' +
    '// convert a percent to a decimal, or pass through\n' +
    '\n' +
    'percent-to-decimal(n)\n' +
    '  if unit(n) is "%"\n' +
    '    remove-unit(n) / 100\n' +
    '  else\n' +
    '    n\n' +
    '\n' +
    '// check if n is an odd number\n' +
    '\n' +
    'odd(n)\n' +
    '  1 == n % 2\n' +
    '\n' +
    '// check if n is an even number\n' +
    '\n' +
    'even(n)\n' +
    '  0 == n % 2\n' +
    '\n' +
    '// check if color is light\n' +
    '\n' +
    'light(color)\n' +
    '  lightness(color) >= 50%\n' +
    '\n' +
    '// check if color is dark\n' +
    '\n' +
    'dark(color)\n' +
    '  lightness(color) < 50%\n' +
    '\n' +
    '// desaturate color by amount\n' +
    '\n' +
    'desaturate(color, amount)\n' +
    "  adjust(color, 'saturation', - amount)\n" +
    '\n' +
    '// saturate color by amount\n' +
    '\n' +
    "saturate(color = '', amount = 100%)\n" +
    "  if color is a 'color'\n" +
    "    adjust(color, 'saturation', amount)\n" +
    '  else\n' +
    '    unquote( "saturate(" + color + ")" )\n' +
    '\n' +
    '// darken by the given amount\n' +
    '\n' +
    'darken(color, amount)\n' +
    "  adjust(color, 'lightness', - amount)\n" +
    '\n' +
    '// lighten by the given amount\n' +
    '\n' +
    'lighten(color, amount)\n' +
    "  adjust(color, 'lightness', amount)\n" +
    '\n' +
    '// decrease opacity by amount\n' +
    '\n' +
    'fade-out(color, amount)\n' +
    '  color - rgba(black, percent-to-decimal(amount))\n' +
    '\n' +
    '// increase opacity by amount\n' +
    '\n' +
    'fade-in(color, amount)\n' +
    '  color + rgba(black, percent-to-decimal(amount))\n' +
    '\n' +
    '// spin hue by a given amount\n' +
    '\n' +
    'spin(color, amount)\n' +
    '  color + unit(amount, deg)\n' +
    '\n' +
    '// mix two colors by a given amount\n' +
    '\n' +
    'mix(color1, color2, weight = 50%)\n' +
    '  unless weight in 0..100\n' +
    '    error("Weight must be between 0% and 100%")\n' +
    '\n' +
    '  if length(color1) == 2\n' +
    '    weight = color1[0]\n' +
    '    color1 = color1[1]\n' +
    '\n' +
    '  else if length(color2) == 2\n' +
    '    weight = 100 - color2[0]\n' +
    '    color2 = color2[1]\n' +
    '\n' +
    '  require-color(color1)\n' +
    '  require-color(color2)\n' +
    '\n' +
    "  p = unit(weight / 100, '')\n" +
    '  w = p * 2 - 1\n' +
    '\n' +
    '  a = alpha(color1) - alpha(color2)\n' +
    '\n' +
    '  w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2\n' +
    '  w2 = 1 - w1\n' +
    '\n' +
    '  channels = (red(color1) red(color2)) (green(color1) green(color2)) (blue(color1) blue(color2))\n' +
    '  rgb = ()\n' +
    '\n' +
    '  for pair in channels\n' +
    '    push(rgb, floor(pair[0] * w1 + pair[1] * w2))\n' +
    '\n' +
    '  a1 = alpha(color1) * p\n' +
    '  a2 = alpha(color2) * (1 - p)\n' +
    '  alpha = a1 + a2\n' +
    '\n' +
    '  rgba(rgb[0], rgb[1], rgb[2], alpha)\n' +
    '\n' +
    '// invert colors, leave alpha intact\n' +
    '\n' +
    "invert(color = '')\n" +
    "  if color is a 'color'\n" +
    '    rgba(#fff - color, alpha(color))\n' +
    '  else\n' +
    '    unquote( "invert(" + color + ")" )\n' +
    '\n' +
    '// give complement of the given color\n' +
    '\n' +
    'complement( color )\n' +
    '  spin( color, 180 )\n' +
    '\n' +
    '// give grayscale of the given color\n' +
    '\n' +
    "grayscale( color = '' )\n" +
    "  if color is a 'color'\n" +
    '    desaturate( color, 100% )\n' +
    '  else\n' +
    '    unquote( "grayscale(" + color + ")" )\n' +
    '\n' +
    '// mix the given color with white\n' +
    '\n' +
    'tint( color, percent )\n' +
    '  mix( white, color, percent )\n' +
    '\n' +
    '// mix the given color with black\n' +
    '\n' +
    'shade( color, percent )\n' +
    '  mix( black, color, percent )\n' +
    '\n' +
    '// return the last value in the given expr\n' +
    '\n' +
    'last(expr)\n' +
    '  expr[length(expr) - 1]\n' +
    '\n' +
    '// return keys in the given pairs or object\n' +
    '\n' +
    'keys(pairs)\n' +
    '  ret = ()\n' +
    "  if type(pairs) == 'object'\n" +
    '    for key in pairs\n' +
    '      push(ret, key)\n' +
    '  else\n' +
    '    for pair in pairs\n' +
    '      push(ret, pair[0]);\n' +
    '  ret\n' +
    '\n' +
    '// return values in the given pairs or object\n' +
    '\n' +
    'values(pairs)\n' +
    '  ret = ()\n' +
    "  if type(pairs) == 'object'\n" +
    '    for key, val in pairs\n' +
    '      push(ret, val)\n' +
    '  else\n' +
    '    for pair in pairs\n' +
    '      push(ret, pair[1]);\n' +
    '  ret\n' +
    '\n' +
    '// join values with the given delimiter\n' +
    '\n' +
    'join(delim, vals...)\n' +
    "  buf = ''\n" +
    '  vals = vals[0] if length(vals) == 1\n' +
    '  for val, i in vals\n' +
    '    buf += i ? delim + val : val\n' +
    '\n' +
    '// add a CSS rule to the containing block\n' +
    '\n' +
    '// - This definition allows add-property to be used as a mixin\n' +
    '// - It has the same effect as interpolation but allows users\n' +
    '//   to opt for a functional style\n' +
    '\n' +
    'add-property-function = add-property\n' +
    'add-property(name, expr)\n' +
    '  if mixin\n' +
    '    {name} expr\n' +
    '  else\n' +
    '    add-property-function(name, expr)\n' +
    '\n' +
    'prefix-classes(prefix)\n' +
    '  -prefix-classes(prefix, block)\n' +
    '\n' +
    '// Caching mixin, use inside your functions to enable caching by extending.\n' +
    '\n' +
    '$stylus_mixin_cache = {}\n' +
    'cache()\n' +
    "  $key = (current-media() or 'no-media') + '__' + called-from[0] + '__' + arguments\n" +
    '  if $key in $stylus_mixin_cache\n' +
    '    @extend {"$cache_placeholder_for_" + $stylus_mixin_cache[$key]}\n' +
    "  else if 'cache' in called-from\n" +
    '    {block}\n' +
    '  else\n' +
    '    $id = length($stylus_mixin_cache)\n' +
    '\n' +
    '    &,\n' +
    '    /$cache_placeholder_for_{$id}\n' +
    '      $stylus_mixin_cache[$key] = $id\n' +
    '      {block}\n' +
    '\n' +
    '// Percentage function to convert a number, e.g. ".45", into a percentage, e.g. "45%"\n' +
    '\n' +
    'percentage(num)\n' +
    "  return unit(num * 100, '%')\n" +
    '\n' +
    '// Returns the position of a `value` within a `list`\n' +
    '\n' +
    'index(list, value)\n' +
    '  for val, i in list\n' +
    '    return i if val == value\n'
}
@dlants
Copy link
Author

dlants commented Mar 6, 2023

Stepping through it seems it gets stuck in an infinite loop...

it's hitting the "else" step of this:
https://github.com/stylus/stylus/blob/dev/lib/functions/index.styl#L266

stylus/lib/functions/index.styl:262
add-property-function = add-property
add-property(name, expr)
  if mixin
    {name} expr
  else
    add-property-function(name, expr)

Seems like if mixin is missing, it just calls itself over and over again. Maybe a race condition in how nib initializes the mixin?

@dlants
Copy link
Author

dlants commented Mar 6, 2023

Looking at how we get into this situation, I see it first going into display(flex) here:
https://github.com/stylus/nib/blob/fb3c70cfaac29f4d6efa8d568c29e20ba2dcd42d/lib/nib/flex.styl#L16

We then encounter add-property here:
https://github.com/stylus/nib/blob/main/lib/nib/vendor.styl#L53

And then get into the infinite loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant