/** * 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())]; } } }
/** * Resolves a class member, which is either a field, a class constant * or the `ClassName::class` syntax, which returns the class' literal. * * @param lang.XPClass $class * @param var[] $token A token as returned by `token_get_all()` * @param string $context * @return var */ protected function memberOf($class, $token, $context) { if (T_VARIABLE === $token[0]) { $field = $class->getField(substr($token[1], 1)); $m = $field->getModifiers(); if ($m & MODIFIER_PUBLIC) { return $field->get(null); } else { if ($m & MODIFIER_PROTECTED && $class->isAssignableFrom($context)) { return $field->setAccessible(true)->get(null); } else { if ($m & MODIFIER_PRIVATE && $class->getName() === $context) { return $field->setAccessible(true)->get(null); } else { throw new IllegalAccessException(sprintf('Cannot access %s field %s::$%s', implode(' ', Modifiers::namesOf($m)), $class->getName(), $field->getName())); } } } } else { if (T_CLASS === $token[0]) { return $class->literal(); } else { return $class->getConstant($token[1]); } } }
/** * Creates a string representation of this method * * @return string */ public function toString() { $signature = ''; foreach ($this->parameters as $parameter) { $signature .= ', ' . $parameter->toString(); } return sprintf('%s<%s __construct(%s)>', nameof($this), implode(' ', \lang\reflect\Modifiers::namesOf($this->modifiers)), substr($signature, 2)); }
/** * Creates a string representation of this Operator * * @return string */ public function toString() { $signature = ''; foreach ($this->parameters as $parameter) { $signature .= ', ' . $parameter->compoundName(); } return sprintf('%s<%s %s %s(%s)>', nameof($this), implode(' ', \lang\reflect\Modifiers::namesOf($this->modifiers)), $this->returns->compoundName(), $this->symbol, substr($signature, 2)); }
/** * 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())]; } } }
/** * Prints package * * @param lang.reflect.Package package */ protected static function printPackage($package) { Console::writeLine('package ', $package->getName(), ' {'); // Child packages foreach ($package->getPackages() as $child) { Console::writeLine(' package ', $child->getName()); } // Classes $order = array('interface' => array(), 'enum' => array(), 'class' => array()); foreach ($package->getClasses() as $class) { $mod = $class->getModifiers(); if ($class->isInterface()) { $type = 'interface'; $mod = $mod ^ MODIFIER_ABSTRACT; } else { if ($class->isEnum()) { $type = 'enum'; } else { $type = 'class'; } } $name = self::displayNameOf($class); $order[$type][] = implode(' ', Modifiers::namesOf($mod)) . ' ' . $type . ' ' . self::displayNameOf($class); } foreach ($order as $type => $classes) { if (empty($classes)) { continue; } Console::writeLine(); sort($classes); foreach ($classes as $name) { Console::writeLine(' ', $name); } } Console::writeLine('}'); }
/** * Creates a string representation of this field * * @return string */ public function toString() { return sprintf('%s<%s %s $%s>', nameof($this), implode(' ', \lang\reflect\Modifiers::namesOf($this->modifiers)), $this->type->compoundName(), $this->name); }
public function staticModifierNames() { $this->assertEquals(array('public', 'static'), \lang\reflect\Modifiers::namesOf(MODIFIER_STATIC)); }
/** * Asserts given modifiers do not contain abstract * * @param int $modifiers * @return void * @throws unittest.AssertionFailedError */ protected function assertNotAbstract($modifiers) { $this->assertFalse(Modifiers::isAbstract($modifiers), implode(' | ', Modifiers::namesOf($modifiers))); }
protected function assertProperty($modifiers, $name, $type, $actual) { $this->assertEquals(['modifiers' => Modifiers::namesOf($modifiers), 'name' => $name, 'type' => $type], ['modifiers' => Modifiers::namesOf($actual->modifiers), 'name' => $actual->name, 'type' => $actual->type]); }
public function staticModifierNames() { $this->assertEquals(['public', 'static'], Modifiers::namesOf(MODIFIER_STATIC)); }
/** * Emit a constructor * * @param xp.compiler.emit.Buffer b * @param xp.compiler.ast.ConstructorNode constructor */ protected function emitConstructor($b, $constructor) { $b->append(implode(' ', Modifiers::namesOf($constructor->modifiers))); $b->append(' function __construct'); // Begin $this->enter(new MethodScope($constructor)); $this->scope[0]->setType(new VariableNode('this'), $this->scope[0]->declarations[0]->name); array_unshift($this->method, '__construct'); // Meta data $this->metadata[0][1]['__construct'] = $this->meta($constructor->comment, $constructor->annotations, [DETAIL_ARGUMENTS => [], DETAIL_RETURNS => null, DETAIL_THROWS => []]); // Arguments, initializations, body if (null !== $constructor->body) { $signature = $this->emitParameters($b, (array) $constructor->parameters, '{'); if ($this->inits[0][false]) { foreach ($this->inits[0][false] as $init) { $b->append($init); } $this->inits[0][false] = []; } $this->emitAll($b, $constructor->body); $b->append('}'); } else { $signature = $this->emitParameters($b, (array) $constructor->parameters, ';'); } // Finalize array_shift($this->method); $this->leave(); // Register type information $c = new Constructor(); $c->parameters = $signature; $c->modifiers = $constructor->modifiers; $this->types[0]->constructor = $c; }