/** * Executes this check * * @param xp.compiler.ast.Node node * @param xp.compiler.types.Scope scope * @return bool */ public function verify(\xp\compiler\ast\Node $node, \xp\compiler\types\Scope $scope) { $access = \cast($node, 'xp.compiler.ast.MemberAccessNode'); // Verify type // * var: Might have method // * primitive, array, map, int: Definitely don't have fields $type = $scope->typeOf($access->target); if ($type->isVariable()) { return ['T203', 'Member access (var).' . $access->name . '() verification deferred until runtime']; } else { if (!$type->isClass()) { return ['T305', 'Using member access on unsupported type ' . $type->compoundName()]; } } // Verify target method exists $target = new \xp\compiler\types\TypeInstance($scope->resolveType($type)); if ($target->hasField($access->name)) { $member = $target->getField($access->name); } else { if ($target->hasProperty($access->name)) { $member = $target->getProperty($access->name); } else { return ['T404', 'No such field $' . $access->name . ' in ' . $target->name()]; } } // Verify visibility if (!($member->modifiers & MODIFIER_PUBLIC)) { $enclosing = $scope->resolveType($scope->declarations[0]->name); if ($member->modifiers & MODIFIER_PRIVATE && !$enclosing->equals($target) || $member->modifiers & MODIFIER_PROTECTED && !($enclosing->equals($target) || $enclosing->isSubclassOf($target))) { return ['T403', sprintf('Accessing %s %s::$%s from %s', implode(' ', \lang\reflect\Modifiers::namesOf($member->modifiers)), $target->name(), $member->name, $enclosing->name())]; } } }
/** * Executes this check * * @param xp.compiler.ast.Node node * @param xp.compiler.types.Scope scope * @return bool */ public function verify(\xp\compiler\ast\Node $node, \xp\compiler\types\Scope $scope) { $access = \cast($node, 'xp.compiler.ast.ArrayAccessNode'); $type = $scope->typeOf($access->target); $result = TypeName::$VAR; $message = null; if ($type->isArray()) { $result = $type->arrayComponentType(); } else { if ($type->isMap()) { $result = $type->mapComponentType(); } else { if ($type->isClass()) { $ptr = new TypeInstance($scope->resolveType($type)); if ($ptr->hasIndexer()) { $result = $ptr->getIndexer()->type; } else { $message = ['T305', 'Type ' . $ptr->name() . ' does not support offset access']; } } else { if ($type->isVariable()) { $message = ['T203', 'Array access (var)' . $access->hashCode() . ' verification deferred until runtime']; } else { if ('string' === $type->name) { $result = $type; } else { $message = ['T305', 'Using array-access on unsupported type ' . $type->toString()]; } } } } } $scope->setType($access, $result); return $message; }
/** * Executes this check * * @param xp.compiler.ast.Node node * @param xp.compiler.types.Scope scope * @return bool */ public function verify(\xp\compiler\ast\Node $node, \xp\compiler\types\Scope $scope) { $v = \cast($node, 'xp.compiler.ast.VariableNode'); if (!$scope->getType($v)) { return ['V404', 'Uninitialized variable ' . $v->name]; } }
/** * Executes this check * * @param xp.compiler.ast.Node node * @param xp.compiler.types.Scope scope * @return bool */ public function verify(\xp\compiler\ast\Node $node, \xp\compiler\types\Scope $scope) { $arm = cast($node, 'xp.compiler.ast.ArmNode'); foreach ($arm->initializations as $i => $init) { $type = $scope->resolveType($scope->typeOf($init), false); if (!$type->isSubclassOf(self::$closeable)) { return ['A403', 'Type ' . $type->name() . ' for assignment #' . ($i + 1) . ' in ARM block is not closeable']; } } }
/** * Executes this check * * @param xp.compiler.ast.Node node * @param xp.compiler.types.Scope scope * @return bool */ public function verify(\xp\compiler\ast\Node $node, \xp\compiler\types\Scope $scope) { $call = \cast($node, 'xp.compiler.ast.MethodCallNode'); // Verify type // * var: Might have method // * primitive, array, map, int: Definitely don't have methods $type = $scope->typeOf($call->target); if ($type->isVariable()) { return ['T203', 'Member call (var).' . $call->name . '() verification deferred until runtime']; } else { if (!$type->isClass()) { return ['T305', 'Using member calls on unsupported type ' . $type->compoundName()]; } } return $this->verifyMethod($type, $call->name, $scope); }
/** * Entry point * * @param xp.compiler.ast.ParseTree tree * @param xp.compiler.types.Scope scope * @return xp.compiler.Result */ public function emit(ParseTree $tree, Scope $scope) { $bytes = new Buffer('', 1); array_unshift($this->local, []); array_unshift($this->temp, 0); array_unshift($this->scope, $scope->enter(new CompilationUnitScope())); $this->scope[0]->importer = $this->nativeImporter; $this->scope[0]->declarations = [$tree->declaration]; $this->scope[0]->package = $tree->package; // Functions from lang.base.php $this->scope[0]->statics = [0 => [], 'newinstance' => true, 'with' => true, 'create' => true, 'raise' => true, 'delete' => true, 'cast' => true, 'is' => true, 'this' => true, 'isset' => true, 'unset' => true, 'empty' => true, 'eval' => true, 'typeof' => true, 'nameof' => true, 'include' => true, 'require' => true, 'include_once' => true, 'require_once' => true]; $this->cat && $this->cat->infof('== Enter %s ==', basename($tree->origin)); // Import and declarations $t = null; $this->scope[0]->addResolved('self', new TypeDeclaration($tree)); // FIXME: for import self $this->emitAll($bytes, (array) $tree->imports); while ($this->scope[0]->declarations) { array_unshift($this->types, new CompiledType()); $decl = current($this->scope[0]->declarations); $this->local[0][$decl->name->name] = true; $this->emitOne($bytes, $decl); array_shift($this->scope[0]->declarations); $t || ($t = $this->types[0]); } // Load used classes $this->emitUses($bytes, $this->scope[0]->used); // Leave scope array_shift($this->local); $this->leave(); // Check on errors $this->cat && $this->cat->infof('== %s: %d error(s), %d warning(s) ==', basename($tree->origin), sizeof($this->messages['errors']), sizeof($this->messages['warnings'])); if ($this->messages['errors']) { throw new FormatException('Errors emitting ' . $tree->origin . ': ' . \xp::stringOf($this->messages)); } // Finalize return new Result($t, $bytes); }