/** * Executes this check * * @param xp.compiler.types.TypeName * @param string name method name * @param xp.compiler.types.Scope scope * @return string[] error or null */ protected function verifyMethod($type, $name, $scope) { // Verify target method exists $target = new \xp\compiler\types\TypeInstance($scope->resolveType($type)); if (!$target->hasMethod($name)) { return ['T404', 'No such method ' . $name . '() in ' . $target->name()]; } // Verify visibility $method = $target->getMethod($name); if (!($method->modifiers & MODIFIER_PUBLIC)) { $enclosing = $scope->resolveType($scope->declarations[0]->name); if ($method->modifiers & MODIFIER_PRIVATE && !$enclosing->equals($target) || $method->modifiers & MODIFIER_PROTECTED && !($enclosing->equals($target) || $enclosing->isSubclassOf($target))) { return ['T403', sprintf('Invoking %s %s::%s() from %s', implode(' ', \lang\reflect\Modifiers::namesOf($method->modifiers)), $target->name(), $method->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.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())]; } } }