public function genFcallCode()
{
$codePrinter = new CodePrinter();
$codePrinter->output('#ifndef ZEPHIR_KERNEL_FCALL_INTERNAL_H');
$codePrinter->output('#define ZEPHIR_KERNEL_FCALL_INTERNAL_H');
$codePrinter->increaseLevel();
ksort($this->requiredMacros);
foreach ($this->requiredMacros as $name => $info) {
list($scope, $mode, $paramCount) = $info;
$paramsStr = '';
$retParam = '';
$retValueUsed = '0';
$params = array();
$zvals = array();
$initStatements = array();
$postStatements = array();
for ($i = 0; $i < $paramCount; ++$i) {
$params[] = 'p' . $i;
}
if ($paramCount) {
$paramsStr = ', ' . implode(', ', $params);
}
if ($mode == 'CALL_INTERNAL_METHOD_P') {
$retValueUsed = '1';
$retParam = 'return_value_ptr';
$initStatements[] = 'ZEPHIR_INIT_NVAR((return_value_ptr)); \\';
}
$objParam = $scope ? 'scope_ce, ' : 'object, ';
$macroName = $name . '(' . ($retParam ? $retParam . ', ' : '') . $objParam . 'method' . $paramsStr . ')';
$codePrinter->output('#define ' . $macroName . ' \\');
if (!$retParam) {
$retParam = 'return_value';
}
$codePrinter->increaseLevel();
$codePrinter->output('do { \\');
$codePrinter->increaseLevel();
if ($mode == 'CALL_INTERNAL_METHOD_NORETURN_P') {
$codePrinter->output('zval rv; \\');
$codePrinter->output('zval *rvp = &rv; \\');
$codePrinter->output('ZVAL_UNDEF(&rv); \\');
$retParam = 'rvp';
}
$codePrinter->output('ZEPHIR_BACKUP_SCOPE(); \\');
$codePrinter->output('ZEPHIR_BACKUP_THIS_PTR(); \\');
if (!$scope) {
$codePrinter->output('ZEPHIR_SET_THIS(object); \\');
$codePrinter->output('ZEPHIR_SET_SCOPE((Z_OBJ_P(object) ? Z_OBJCE_P(object) : NULL), (Z_OBJ_P(object) ? Z_OBJCE_P(object) : NULL)); \\');
} else {
$codePrinter->output('ZEPHIR_SET_THIS_EXPLICIT_NULL(); \\');
$codePrinter->output('ZEPHIR_SET_SCOPE(scope_ce, scope_ce); \\');
}
/* Create new zval's for parameters */
for ($i = 0; $i < $paramCount; ++$i) {
$zv = '_' . $params[$i];
$zvals[] = $zv;
$initStatements[] = 'ZVAL_COPY(&' . $zv . ', ' . $params[$i] . '); \\';
$postStatements[] = 'Z_TRY_DELREF_P(' . $params[$i] . '); \\';
//$postStatements[] = 'zval_ptr_dtor(' . $params[$i] . '); \\';
}
if ($i) {
$codePrinter->output('zval ' . implode(', ', $zvals) . '; \\');
}
foreach ($initStatements as $statement) {
$codePrinter->output($statement);
}
$zvalStr = $i ? ', &' . implode(', &', $zvals) : '';
$codePrinter->output('method(0, ' . $retParam . ', ' . ($scope ? 'NULL, ' : $objParam) . $retValueUsed . $zvalStr . '); \\');
if ($mode == 'CALL_INTERNAL_METHOD_NORETURN_P') {
$postStatements[] = 'zval_ptr_dtor(rvp); \\';
}
foreach ($postStatements as $statement) {
$codePrinter->output($statement);
}
$codePrinter->output('ZEPHIR_LAST_CALL_STATUS = EG(exception) ? FAILURE : SUCCESS; \\');
$codePrinter->output('ZEPHIR_RESTORE_THIS_PTR(); \\');
$codePrinter->output('ZEPHIR_RESTORE_SCOPE(); \\');
$codePrinter->decreaseLevel();
$codePrinter->output('} while (0)');
$codePrinter->decreaseLevel();
$codePrinter->output('');
}
$codePrinter->decreaseLevel();
$codePrinter->output("#endif");
Utils::checkAndWriteIfNeeded($codePrinter->getOutput(), 'ext/kernel/fcall_internal.h');
}
/** * Generates the C sources from Zephir without compiling them * * @param CommandInterface $command * @return bool * @throws Exception */ public function generate(CommandInterface $command) { /** * Get global namespace */ $namespace = $this->checkDirectory(); /** * Check whether there are external dependencies */ $externalDependencies = $this->config->get('external-dependencies'); if (is_array($externalDependencies)) { foreach ($externalDependencies as $dependencyNs => $location) { if (!file_exists($location)) { throw new CompilerException('Location of dependency "' . $dependencyNs . '" does not exist. Check the config.json for more information'); } $this->addExternalDependency($dependencyNs, $location); } } /** * Check if there are module/request/global destructors */ $destructors = $this->config->get('destructors'); if (is_array($destructors)) { $invokeDestructors = $this->processCodeInjection($destructors); $includes = $invokeDestructors[0]; $destructors = $invokeDestructors[1]; } /** * Check if there are module/request/global initializers */ $initializers = $this->config->get('initializers'); if (is_array($initializers)) { $invokeInitializers = $this->processCodeInjection($initializers); $includes = $invokeInitializers[0]; $initializers = $invokeInitializers[1]; } /** * Round 1. pre-compile all files in memory */ $this->recursivePreCompile(str_replace('\\', DIRECTORY_SEPARATOR, $namespace)); if (!count($this->files)) { throw new Exception("Zephir files to compile couldn't be found. Did you add a first class to the extension?"); } /** * Round 2. Check 'extends' and 'implements' dependencies */ foreach ($this->files as $compileFile) { $compileFile->checkDependencies($this); } /** * Sort the files by dependency ranking */ $files = array(); $rankedFiles = array(); $this->calculateDependencies($this->files); foreach ($this->files as $rankFile) { $rank = $rankFile->getClassDefinition()->getDependencyRank(); $rankedFiles[$rank][] = $rankFile; } krsort($rankedFiles); foreach ($rankedFiles as $rank => $rankFiles) { $files = array_merge($files, $rankFiles); } $this->files = $files; /** * Convert C-constants into PHP constants */ $constantsSources = $this->config->get('constants-sources'); if (is_array($constantsSources)) { $this->loadConstantsSources($constantsSources); } /** * Set extension globals */ $globals = $this->config->get('globals'); if (is_array($globals)) { $this->setExtensionGlobals($globals); } /** * Load function optimizers */ if (self::$loadedPrototypes === false) { FunctionCall::addOptimizerDir(ZEPHIRPATH . 'Library/Optimizers/FunctionCall'); $optimizerDirs = $this->config->get('optimizer-dirs'); if (is_array($optimizerDirs)) { foreach ($optimizerDirs as $directory) { FunctionCall::addOptimizerDir(realpath($directory)); } } if (is_dir(ZEPHIRPATH . 'prototypes') && is_readable(ZEPHIRPATH . 'prototypes')) { /** * Load additional extension prototypes * @var $file \DirectoryIterator */ foreach (new \DirectoryIterator(ZEPHIRPATH . 'prototypes') as $file) { if (!$file->isDir()) { $extension = str_replace('.php', '', $file); if (!extension_loaded($extension)) { require $file->getRealPath(); } } } } /** * Load customer additional extension prototypes */ $prototypeDirs = $this->config->get('prototype-dir'); if (is_array($prototypeDirs)) { foreach ($prototypeDirs as $prototype => $prototypeDir) { /** * Check if the extension is installed */ if (!extension_loaded($prototype)) { $prototypeRealpath = realpath($prototypeDir); if ($prototypeRealpath) { foreach (new \DirectoryIterator($prototypeRealpath) as $file) { if (!$file->isDir()) { require $file->getRealPath(); } } } } } } self::$loadedPrototypes = true; } /** * Round 3. Compile all files to C sources */ $files = array(); $hash = ""; foreach ($this->files as $compileFile) { /** * Only compile classes in the local extension, ignore external classes */ if (!$compileFile->isExternal()) { $compileFile->compile($this, $this->stringManager); $compiledFile = $compileFile->getCompiledFile(); $methods = array(); $classDefinition = $compileFile->getClassDefinition(); foreach ($classDefinition->getMethods() as $method) { $methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']'; if ($method->isInitializer() && $method->isStatic()) { $this->internalInitializers[] = "\t" . $method->getName() . '(TSRMLS_C);'; } } $files[] = $compiledFile; $hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']'; } } /** * Round 3.2. Compile anonymous classes */ foreach ($this->anonymousFiles as $compileFile) { $compileFile->compile($this, $this->stringManager); $compiledFile = $compileFile->getCompiledFile(); $methods = array(); $classDefinition = $compileFile->getClassDefinition(); foreach ($classDefinition->getMethods() as $method) { $methods[] = '[' . $method->getName() . ':' . join('-', $method->getVisibility()) . ']'; } $files[] = $compiledFile; $hash .= '|' . $compiledFile . ':' . $classDefinition->getClassEntry() . '[' . join('|', $methods) . ']'; } $hash = md5($hash); $this->compiledFiles = $files; /** * Round 3.3. Load extra C-sources */ $extraSources = $this->config->get('extra-sources'); if (is_array($extraSources)) { $this->extraFiles = $extraSources; } else { $this->extraFiles = array(); } /** * Round 3.4. Load extra classes sources */ $extraClasses = $this->config->get('extra-classes'); if (is_array($extraClasses)) { foreach ($extraClasses as $value) { if (isset($value['source'])) { $this->extraFiles[] = $value['source']; } } } /** * Round 4. Create config.m4 and config.w32 files / Create project.c and project.h files */ $namespace = str_replace('\\', '_', $namespace); $extensionName = $this->config->get('extension-name'); if (empty($extensionName) || !is_string($extensionName)) { $extensionName = $namespace; } $needConfigure = $this->createConfigFiles($extensionName); $needConfigure |= $this->createProjectFiles($extensionName); $needConfigure |= $this->checkIfPhpized(); $version = self::getCurrentVersion(); /** * When a new file is added or removed we need to run configure again */ if (!$command instanceof CommandGenerate) { if (!$this->fileSystem->exists($version . '/compiled-files-sum')) { $needConfigure = true; $this->fileSystem->write($version . '/compiled-files-sum', $hash); } else { if ($this->fileSystem->read($version . '/compiled-files-sum') != $hash) { $needConfigure = true; $this->fileSystem->write($version . '/compiled-files-sum', $hash); } } } /** * Round 5. Generate concatenation functions */ $this->stringManager->genConcatCode(); $this->fcallManager->genFcallCode(); if ($this->config->get('stubs-run-after-generate', 'stubs')) { $this->stubs($command, true); } return $needConfigure; }