diff --git a/src/Compiler.php b/src/Compiler.php index b69cf43c..62ef74d1 100644 --- a/src/Compiler.php +++ b/src/Compiler.php @@ -1450,6 +1450,13 @@ protected function pushCallStack($name = '') Parser::SOURCE_LINE => $this->sourceLine, Parser::SOURCE_COLUMN => $this->sourceColumn ]; + // infinite calling loop + if (count($this->callStack) > 25000) { + // not displayed but you can var_dump it to deep debug + $msg = $this->callStackMessage(true, 100); + $msg = "Infinite calling loop"; + $this->throwError($msg); + } } protected function popCallStack() @@ -1462,11 +1469,13 @@ protected function popCallStack() * * @param array $stms * @param \Leafo\ScssPhp\Formatter\OutputBlock $out + * @param string $traceName * * @return array|null */ - protected function compileChildren($stms, OutputBlock $out) + protected function compileChildren($stms, OutputBlock $out, $traceName = '') { + $this->pushCallStack($traceName); foreach ($stms as $stm) { $ret = $this->compileChild($stm, $out); @@ -1474,6 +1483,7 @@ protected function compileChildren($stms, OutputBlock $out) return $ret; } } + $this->popCallStack(); return null; } @@ -1484,11 +1494,13 @@ protected function compileChildren($stms, OutputBlock $out) * @param array $stms * @param \Leafo\ScssPhp\Formatter\OutputBlock $out * @param \Leafo\ScssPhp\Block $selfParent + * @param string $traceName * * @throws \Exception */ - protected function compileChildrenNoReturn($stms, OutputBlock $out, $selfParent = null) + protected function compileChildrenNoReturn($stms, OutputBlock $out, $selfParent = null, $traceName = '') { + $this->pushCallStack($traceName); foreach ($stms as $stm) { if ($selfParent && isset($stm[1]) && is_object($stm[1]) && $stm[1] instanceof Block) { $stm[1]->selfParent = $selfParent; @@ -1508,6 +1520,7 @@ protected function compileChildrenNoReturn($stms, OutputBlock $out, $selfParent return; } } + $this->popCallStack(); } @@ -2160,9 +2173,7 @@ protected function compileChild($child, OutputBlock $out) $this->env->marker = 'mixin'; - $this->pushCallStack($this->env->marker . " " . $name); - $this->compileChildrenNoReturn($mixin->children, $out, $selfParent); - $this->popCallStack(); + $this->compileChildrenNoReturn($mixin->children, $out, $selfParent, $this->env->marker . " " . $name); $this->storeEnv = $storeEnv; @@ -2170,8 +2181,7 @@ protected function compileChild($child, OutputBlock $out) break; case Type::T_MIXIN_CONTENT: - $content = $this->get(static::$namespaces['special'] . 'content', false, $this->getStoreEnv()) - ?: $this->get(static::$namespaces['special'] . 'content', false, $this->env); + $content = $this->get(static::$namespaces['special'] . 'content', false, isset($this->storeEnv) ? $this->storeEnv : $this->env); if (! $content) { $content = new \stdClass(); @@ -3904,20 +3914,41 @@ public function throwError($msg) : "line: $line"; $msg = "$msg: $loc"; - if ($this->callStack) { - $msg .= "\nCall Stack:\n"; - $ncall = 0; + $callStackMsg = $this->callStackMessage(); + if ($callStackMsg) { + $msg .= "\nCall Stack:\n" . $callStackMsg; + } + + throw new CompilerException($msg); + } + + /** + * @param bool $all + * @param null $limit + * @return string + */ + protected function callStackMessage($all = false, $limit = null) + { + $callStackMsg = []; + $ncall = 0; + if ($this->callStack) { foreach (array_reverse($this->callStack) as $call) { - $msg .= "#" . $ncall++ . " " . $call['n'] . " "; - $msg .= (isset($this->sourceNames[$call[Parser::SOURCE_INDEX]]) - ? $this->sourceNames[$call[Parser::SOURCE_INDEX]] - : '(unknown file)'); - $msg .= " on line " . $call[Parser::SOURCE_LINE] . "\n"; + if ($all || (isset($call['n']) && $call['n'])) { + $msg = "#" . $ncall++ . " " . $call['n'] . " "; + $msg .= (isset($this->sourceNames[$call[Parser::SOURCE_INDEX]]) + ? $this->sourceNames[$call[Parser::SOURCE_INDEX]] + : '(unknown file)'); + $msg .= " on line " . $call[Parser::SOURCE_LINE]; + $callStackMsg[] = $msg; + if (!is_null($limit) && $ncall>$limit) { + break; + } + } } } - throw new CompilerException($msg); + return implode("\n", $callStackMsg); } /** @@ -3984,11 +4015,8 @@ protected function callScssFunction($name, $argValues, &$returnValue) $tmp->children = []; $this->env->marker = 'function'; - $this->pushCallStack($this->env->marker . " " . $name); - $ret = $this->compileChildren($func->children, $tmp); - - $this->popCallStack(); + $ret = $this->compileChildren($func->children, $tmp, $this->env->marker . " " . $name); $this->storeEnv = $storeEnv;