/**
  * 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)
 {
     $routine = \cast($node, 'xp.compiler.ast.RoutineNode');
     $qname = $scope->declarations[0]->name->compoundName() . '::' . $routine->getName();
     $empty = $routine->body === null;
     if ($scope->declarations[0] instanceof \xp\compiler\ast\InterfaceNode) {
         if (!$empty) {
             return ['R403', 'Interface methods may not have a body ' . $qname];
         } else {
             if ($routine->modifiers !== MODIFIER_PUBLIC && $routine->modifiers !== 0) {
                 return ['R401', 'Interface methods may only be public ' . $qname];
             }
         }
     } else {
         if (Modifiers::isAbstract($routine->modifiers) && !$empty) {
             return ['R403', 'Abstract methods may not have a body ' . $qname];
         } else {
             if (!Modifiers::isAbstract($routine->modifiers) && $empty) {
                 return ['R401', 'Non-abstract methods must have a body ' . $qname];
             }
         }
         if ($routine->extension && !Modifiers::isStatic($routine->modifiers)) {
             return ['E403', 'Extension methods must be static ' . $qname];
         }
     }
 }
Exemplo n.º 3
0
 /**
  * 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]);
         }
     }
 }
Exemplo n.º 4
0
 /**
  * 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));
 }
Exemplo n.º 5
0
 /**
  * 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));
 }
Exemplo n.º 6
0
 /**
  * Provide tests from a given package to the test suite. Handles recursion.
  *
  * @param  lang.reflect.Package $package
  * @param  unittest.TestSuite $suite
  * @param  var[] $arguments
  * @return void
  */
 private function provideFrom($package, $suite, $arguments)
 {
     foreach ($package->getClasses() as $class) {
         if ($class->isSubclassOf(TestCase::class) && !Modifiers::isAbstract($class->getModifiers())) {
             $suite->addTestClass($class, $arguments);
         }
     }
     if ($this->recursive) {
         foreach ($package->getPackages() as $package) {
             $this->provideFrom($package, $suite, $arguments);
         }
     }
 }
 /**
  * Returns a list of all classes inside a given package
  *
  * @param   lang.reflect.Package 
  * @param   bool recursive whether to include subpackages
  * @return  lang.XPClass[]
  */
 protected static function testClassesIn(Package $package, $recursive)
 {
     $r = array();
     foreach ($package->getClasses() as $class) {
         if (!$class->isSubclassOf('unittest.TestCase') || Modifiers::isAbstract($class->getModifiers())) {
             continue;
         }
         $r[] = $class;
     }
     if ($recursive) {
         foreach ($package->getPackages() as $package) {
             $r = array_merge($r, self::testClassesIn($package, $recursive));
         }
     }
     return $r;
 }
 /**
  * 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())];
         }
     }
 }
Exemplo n.º 9
0
 /**
  * Provide tests to test suite
  *
  * @param  unittest.TestSuite $suite
  * @param  var[] $arguments
  * @return void
  * @throws lang.IllegalArgumentException if no tests are found
  */
 public function provideTo($suite, $arguments)
 {
     $it = new FilteredIOCollectionIterator(new FileCollection($this->loader->path), new ExtensionEqualsFilter(\xp::CLASS_FILE_EXT), true);
     $l = strlen($this->loader->path);
     $e = -strlen(\xp::CLASS_FILE_EXT);
     $empty = true;
     foreach ($it as $element) {
         $class = $this->loader->loadClass(strtr(substr($element->getUri(), $l, $e), DIRECTORY_SEPARATOR, '.'));
         if ($class->isSubclassOf(TestCase::class) && !Modifiers::isAbstract($class->getModifiers())) {
             $suite->addTestClass($class, $arguments);
             $empty = false;
         }
     }
     if ($empty) {
         throw new IllegalArgumentException('Cannot find any test cases in ' . $this->loader->toString());
     }
 }
Exemplo n.º 10
0
 /**
  * Uses the constructor represented by this Constructor object to create 
  * and initialize a new instance of the constructor's declaring class, 
  * with the specified initialization parameters.
  *
  * Example:
  * <code>
  *   $constructor= XPClass::forName('util.Binford')->getConstructor();
  *
  *   $instance= $constructor->newInstance();
  *   $instance= $constructor->newInstance([6100]);
  * </code>
  *
  * @param   var[] args
  * @return  lang.Generic
  * @throws  lang.IllegalAccessException in case the constructor is not public or if it is abstract
  * @throws  lang.reflect.TargetInvocationException in case the constructor throws an exception
  */
 public function newInstance(array $args = [])
 {
     $class = new \ReflectionClass($this->_class);
     if ($class->isAbstract()) {
         throw new IllegalAccessException('Cannot instantiate abstract class ' . XPClass::nameOf($this->_class));
     }
     // Check modifiers. If caller is an instance of this class, allow private and
     // protected constructor invocation (which the PHP reflection API does not).
     $m = $this->_reflect->getModifiers();
     $public = $m & MODIFIER_PUBLIC;
     if (!$public && !$this->accessible) {
         $t = debug_backtrace(0, 2);
         $decl = $this->_reflect->getDeclaringClass()->getName();
         if ($m & MODIFIER_PROTECTED) {
             $allow = $t[1]['class'] === $decl || is_subclass_of($t[1]['class'], $decl);
         } else {
             $allow = $t[1]['class'] === $decl;
         }
         if (!$allow) {
             throw new IllegalAccessException(sprintf('Cannot invoke %s constructor of class %s from scope %s', Modifiers::stringOf($this->getModifiers()), XPClass::nameOf($this->_class), $t[1]['class']));
         }
     }
     // For non-public constructors: Use setAccessible() / invokeArgs() combination
     try {
         if ($public) {
             return $class->newInstanceArgs($args);
         }
         $instance = unserialize('O:' . strlen($this->_class) . ':"' . $this->_class . '":0:{}');
         $this->_reflect->setAccessible(true);
         $this->_reflect->invokeArgs($instance, $args);
         return $instance;
     } catch (\lang\SystemExit $e) {
         throw $e;
     } catch (\lang\Throwable $e) {
         throw new TargetInvocationException(XPClass::nameOf($this->_class) . '::<init>', $e);
     } catch (\Exception $e) {
         throw new TargetInvocationException(XPClass::nameOf($this->_class) . '::<init>', new \lang\XPException($e->getMessage()));
     } catch (\BaseException $e) {
         throw new TargetInvocationException(XPClass::nameOf($this->_class) . '::' . $this->_reflect->getName(), new \lang\Error($e->getMessage()));
     }
 }
Exemplo n.º 11
0
 /**
  * Invokes the underlying method represented by this Method object, 
  * on the specified object with the specified parameters.
  *
  * Example:
  * ```php
  * $method= XPClass::forName('lang.Object')->getMethod('toString');
  * $str= $method->invoke(new Object());
  * ```
  *
  * Example (passing arguments)
  * ```php
  * $method= XPClass::forName('lang.types.String')->getMethod('concat');
  * $str= $method->invoke(new String('Hello'), ['World']);
  * ```
  *
  * Example (static invokation):
  * ```php
  * $method= XPClass::forName('util.log.Logger')->getMethod('getInstance');
  * $log= $method->invoke(null);
  * ```
  *
  * @param   lang.Object $obj
  * @param   var[] $args default []
  * @return  var
  * @throws  lang.IllegalArgumentException in case the passed object is not an instance of the declaring class
  * @throws  lang.IllegalAccessException in case the method is not public or if it is abstract
  * @throws  lang.reflect.TargetInvocationException for any exception raised from the invoked method
  */
 public function invoke($obj, $args = [])
 {
     if (null !== $obj && !$obj instanceof $this->_class) {
         throw new IllegalArgumentException(sprintf('Passed argument is not a %s class (%s)', XPClass::nameOf($this->_class), \xp::typeOf($obj)));
     }
     // Check modifiers. If caller is an instance of this class, allow
     // protected method invocation (which the PHP reflection API does
     // not).
     $m = $this->_reflect->getModifiers();
     if ($m & MODIFIER_ABSTRACT) {
         throw new IllegalAccessException(sprintf('Cannot invoke abstract %s::%s', XPClass::nameOf($this->_class), $this->_reflect->getName()));
     }
     $public = $m & MODIFIER_PUBLIC;
     if (!$public && !$this->accessible) {
         $t = debug_backtrace(0, 2);
         $decl = $this->_reflect->getDeclaringClass()->getName();
         if ($m & MODIFIER_PROTECTED) {
             $allow = $t[1]['class'] === $decl || is_subclass_of($t[1]['class'], $decl);
         } else {
             $allow = $t[1]['class'] === $decl;
         }
         if (!$allow) {
             throw new IllegalAccessException(sprintf('Cannot invoke %s %s::%s from scope %s', Modifiers::stringOf($this->getModifiers()), XPClass::nameOf($this->_class), $this->_reflect->getName(), $t[1]['class']));
         }
     }
     try {
         if (!$public) {
             $this->_reflect->setAccessible(true);
         }
         return $this->_reflect->invokeArgs($obj, (array) $args);
     } catch (\lang\SystemExit $e) {
         throw $e;
     } catch (\lang\Throwable $e) {
         throw new TargetInvocationException(XPClass::nameOf($this->_class) . '::' . $this->_reflect->getName(), $e);
     } catch (\Exception $e) {
         throw new TargetInvocationException(XPClass::nameOf($this->_class) . '::' . $this->_reflect->getName(), new \lang\XPException($e->getMessage()));
     } catch (\Throwable $e) {
         throw new TargetInvocationException(XPClass::nameOf($this->_class) . '::' . $this->_reflect->getName(), new \lang\Error($e->getMessage()));
     }
 }
Exemplo n.º 12
0
 /**
  * Get all test cases
  *
  * @param   var[] arguments
  * @return  unittest.TestCase[]
  */
 public function testCasesWith($arguments)
 {
     if (null === ($cl = $this->findLoaderFor($this->folder->getURI()))) {
         throw new IllegalArgumentException($this->folder->toString() . ' is not in class path');
     }
     $l = strlen($cl->path);
     $e = -strlen(\xp::CLASS_FILE_EXT);
     $it = new FilteredIOCollectionIterator(new FileCollection($this->folder), new ExtensionEqualsFilter(\xp::CLASS_FILE_EXT), true);
     $cases = [];
     foreach ($it as $element) {
         $name = strtr(substr($element->getUri(), $l, $e), DIRECTORY_SEPARATOR, '.');
         $class = XPClass::forName($name);
         if (!$class->isSubclassOf('unittest.TestCase') || Modifiers::isAbstract($class->getModifiers())) {
             continue;
         }
         $cases = array_merge($cases, $this->testCasesInClass($class, $arguments));
     }
     if (empty($cases)) {
         throw new IllegalArgumentException('Cannot find any test cases in ' . $this->folder->toString());
     }
     return $cases;
 }
Exemplo n.º 13
0
 /**
  * Retrieve string representation. Examples:
  *
  * <pre>
  *   public lang.XPClass getClass()
  *   public static util.Date now()
  *   public open(string $mode) throws io.FileNotFoundException, io.IOException
  * </pre>
  *
  * @return  string
  */
 public function toString()
 {
     $signature = '';
     foreach ($this->getParameters() as $param) {
         if ($param->isOptional()) {
             $signature .= ', [' . $param->getTypeName() . ' $' . $param->getName() . '= ' . str_replace("\n", ' ', \xp::stringOf($param->getDefaultValue())) . ']';
         } else {
             $signature .= ', ' . $param->getTypeName() . ' $' . $param->getName();
         }
     }
     if ($exceptions = $this->getExceptionNames()) {
         $throws = ' throws ' . implode(', ', $exceptions);
     } else {
         $throws = '';
     }
     return sprintf('%s %s %s(%s)%s', Modifiers::stringOf($this->getModifiers()), $this->getReturnTypeName(), $this->getName(), substr($signature, 2), $throws);
 }
Exemplo n.º 14
0
 /**
  * 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));
 }
Exemplo n.º 16
0
 /**
  * Resolve a static call. Return true if the target is a function
  * (e.g. key()), a xp.compiler.types.Method instance if it's a static 
  * method (Map::key()).
  *
  * @param   string name
  * @return  var
  */
 public function resolveStatic($name)
 {
     foreach ($this->statics[0] as $lookup => $type) {
         if (true === $type && $this->importer->hasFunction($lookup, $name)) {
             return true;
         } else {
             if ($type instanceof Types && $type->hasMethod($name)) {
                 $m = $type->getMethod($name);
                 if (\lang\reflect\Modifiers::isStatic($m->modifiers)) {
                     return $m;
                 }
             }
         }
     }
     return null;
 }
Exemplo n.º 17
0
 /**
  * Lists commands
  *
  * @return void
  */
 protected function listCommands()
 {
     $commandsIn = function ($package) {
         $text = '';
         foreach ($package->getClasses() as $class) {
             if ($class->isSubclassOf('util.cmd.Command') && !Modifiers::isAbstract($class->getModifiers())) {
                 $text .= '  $ xpcli ' . $class->getSimpleName() . "\n";
             }
         }
         return $text ?: '  (no commands)';
     };
     self::$err->writeLine('Named commands');
     self::$err->writeLine();
     if ($packages = Commands::allPackages()) {
         foreach (Commands::allPackages() as $package) {
             self::$err->writeLine('* ', $package);
             self::$err->writeLine($commandsIn($package));
         }
         self::$err->writeLine();
     }
     self::$err->writeLine('* Global package');
     self::$err->writeLine($commandsIn(Package::forName(null)));
 }
Exemplo n.º 18
0
 /**
  * Generates code for (re)implementation of the (abstract) class methods
  * of the base class.
  *
  * @param lang.XPClass baseClass
  */
 private function generateBaseClassMethods($baseClass)
 {
     $bytes = '';
     $reservedMethods = \lang\XPClass::forName('lang.Generic')->getMethods();
     $reservedMethodNames = array_map(function ($i) {
         return $i->getName();
     }, $reservedMethods);
     foreach ($baseClass->getMethods() as $m) {
         // do not overwrite reserved methods, omit static methods
         if (in_array($m->getName(), $reservedMethodNames) || Modifiers::isStatic($m->getModifiers())) {
             continue;
         }
         // Check for already declared methods, do not redeclare them
         // implement abstract methods
         if ($this->overwriteExisting || ($m->getModifiers() & 2) == 2) {
             if (isset($this->added[$m->getName()])) {
                 continue;
             }
             $this->added[$m->getName()] = true;
             $bytes .= $this->generateMethod($m);
         }
     }
     return $bytes;
 }
Exemplo n.º 19
0
 public function staticModifierNames()
 {
     $this->assertEquals(['public', 'static'], Modifiers::namesOf(MODIFIER_STATIC));
 }
Exemplo n.º 20
0
 /**
  * Lists commands
  *
  * @return void
  */
 protected function listCommands()
 {
     $commandsIn = function ($package) {
         $markdown = '';
         foreach ($package->getClasses() as $class) {
             if ($class->isSubclassOf('util.cmd.Command') && !Modifiers::isAbstract($class->getModifiers())) {
                 $markdown .= '  $ xp cmd ' . $class->getSimpleName() . "\n";
             }
         }
         return $markdown ?: '  *(no commands)*';
     };
     $markdown = "# Named commands\n\n";
     if ($packages = Commands::allPackages()) {
         foreach ($packages as $package) {
             $markdown .= '* In package **' . $package->getName() . "**\n\n" . $commandsIn($package);
         }
         $markdown .= "\n";
     }
     $markdown .= "* In global package\n\n" . $commandsIn(Package::forName(null));
     Help::render(self::$err, $markdown, []);
 }
Exemplo n.º 21
0
 /**
  * Creates a string representation of this field
  *
  * @return  string
  */
 public function toString()
 {
     return sprintf('%s %s %s::$%s', Modifiers::stringOf($this->getModifiers()), $this->getTypeName(), $this->getDeclaringClass()->getName(), $this->getName());
 }
Exemplo n.º 22
0
 /**
  * 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('}');
 }
Exemplo n.º 23
0
 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 is_abstract()
 {
     $this->assertTrue(\lang\reflect\Modifiers::isAbstract(self::$fixture->getModifiers()));
 }
Exemplo n.º 25
0
 /**
  * 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)));
 }
Exemplo n.º 26
0
 /**
  * Convert value based on type
  *
  * @param   lang.Type type
  * @param   [:var] value
  * @return  var
  */
 public function unmarshal($type, $value)
 {
     if (null === $type || $type->equals(Type::$VAR)) {
         // No conversion
         return $value;
     } else {
         if (null === $value) {
             // Valid for any type
             return null;
         } else {
             if ($type instanceof Primitive) {
                 return $type->cast($this->valueOf($value));
             } else {
                 if ($type->equals(XPClass::forName('util.Date'))) {
                     return $type->newInstance($value);
                 } else {
                     if ($type instanceof XPClass) {
                         if ($type->isInstance($value)) {
                             return $value;
                         }
                         foreach ($this->marshallers->keys() as $t) {
                             if ($t->isAssignableFrom($type)) {
                                 return $this->marshallers[$t]->unmarshal($type, $value, $this);
                             }
                         }
                         // Check if a public static valueOf() method exists
                         if ($type->hasMethod('valueOf')) {
                             $valueOf = $type->getMethod('valueOf');
                             if (Modifiers::isStatic($valueOf->getModifiers()) && Modifiers::isPublic($valueOf->getModifiers())) {
                                 if (1 === $valueOf->numParameters()) {
                                     return $valueOf->invoke(null, [$this->unmarshal($this->paramType($valueOf->getParameter(0)), $value)]);
                                 } else {
                                     $param = 0;
                                     $args = [];
                                     foreach ($this->iterableOf($value) as $value) {
                                         $args[] = $this->unmarshal($this->paramType($valueOf->getParameter($param++)), $value);
                                     }
                                     return $valueOf->invoke(null, $args);
                                 }
                             }
                         }
                         // Generic approach
                         $return = $type->newInstance();
                         foreach ($this->iterableOf($value) as $name => $value) {
                             foreach ($this->variantsOf($name) as $variant) {
                                 if ($type->hasField($variant)) {
                                     $field = $type->getField($variant);
                                     $m = $field->getModifiers();
                                     if ($m & MODIFIER_STATIC) {
                                         continue;
                                     } else {
                                         if ($m & MODIFIER_PUBLIC) {
                                             if (null !== ($fType = $field->getType())) {
                                                 $field->set($return, $this->unmarshal($fType, $value));
                                             } else {
                                                 $field->set($return, $value);
                                             }
                                             continue 2;
                                         }
                                     }
                                 }
                                 if ($type->hasMethod('set' . $variant)) {
                                     $method = $type->getMethod('set' . $variant);
                                     if ($method->getModifiers() & MODIFIER_PUBLIC) {
                                         if (null !== ($param = $method->getParameter(0))) {
                                             $method->invoke($return, [$this->unmarshal($param->getType(), $value)]);
                                         } else {
                                             $method->invoke($return, [$value]);
                                         }
                                         continue 2;
                                     }
                                 }
                             }
                         }
                         return $return;
                     } else {
                         if ($type instanceof \lang\ArrayType) {
                             $return = [];
                             foreach ($this->iterableOf($value) as $element) {
                                 $return[] = $this->unmarshal($type->componentType(), $element);
                             }
                             return $return;
                         } else {
                             if ($type instanceof \lang\MapType) {
                                 $return = [];
                                 foreach ($this->iterableOf($value) as $key => $element) {
                                     $return[$key] = $this->unmarshal($type->componentType(), $element);
                                 }
                                 return $return;
                             } else {
                                 if ($type->equals(Type::$ARRAY)) {
                                     $return = [];
                                     foreach ($this->iterableOf($value) as $key => $element) {
                                         $return[$key] = $element;
                                     }
                                     return $return;
                                 } else {
                                     throw new \lang\FormatException('Cannot convert to ' . \xp::stringOf($type));
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
 }
Exemplo n.º 27
0
 /**
  * Emit an enum declaration
  *
  * Basic form:
  * <code>
  *   public enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
  * </code>
  *
  * With values:
  * <code>
  *   public enum Coin { penny(1), nickel(2), dime(10), quarter(25) }
  * </code>
  *
  * Abstract:
  * <code>
  *   public abstract enum Operation {
  *     plus {
  *       public int evaluate(int $x, int $y) { return $x + $y; }
  *     },
  *     minus {
  *       public int evaluate(int $x, int $y) { return $x - $y; }
  *     };
  *
  *     public abstract int evaluate(int $x, int $y);
  *   }
  * </code>
  *
  * @param   xp.compiler.emit.Buffer b
  * @param   xp.compiler.ast.EnumNode declaration
  */
 protected function emitEnum($b, $declaration)
 {
     $parent = $declaration->parent ?: new TypeName('lang.Enum');
     $parentType = $this->resolveType($parent);
     $thisType = new TypeDeclaration(new ParseTree($this->scope[0]->package, [], $declaration), $parentType);
     $this->scope[0]->addResolved('self', $thisType);
     $this->scope[0]->addResolved('parent', $parentType);
     // FIXME: ???
     $this->scope[0]->addResolved($declaration->name->name, $thisType);
     $this->scope[0]->imports[$declaration->name->name] = $declaration->name->name;
     $this->enter(new TypeDeclarationScope());
     // Ensure parent class and interfaces are loaded
     $this->emitTypeName($b, 'class', $declaration);
     $b->append(' extends ' . $this->literal($parentType, true));
     array_unshift($this->metadata, [[], []]);
     array_unshift($this->properties, ['get' => [], 'set' => []]);
     $abstract = Modifiers::isAbstract($declaration->modifiers);
     // Meta data
     $this->metadata[0]['class'] = $this->meta($declaration->comment, $declaration->annotations, []);
     // Generics
     if ($declaration->name->isGeneric()) {
         $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['self'] = $this->genericComponentAsMetadata($declaration->name);
     }
     if ($parent->isGeneric()) {
         $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['parent'] = $this->genericComponentAsMetadata($parent);
     }
     // Interfaces
     if ($declaration->implements) {
         $b->append(' implements ');
         $s = sizeof($declaration->implements) - 1;
         foreach ($declaration->implements as $i => $type) {
             if ($type->isGeneric()) {
                 $this->metadata[0]['class'][DETAIL_ANNOTATIONS]['generic']['implements'][$i] = $this->genericComponentAsMetadata($type);
             }
             $b->append($this->resolveType($type)->literal(true));
             $i < $s && $b->append(', ');
         }
     }
     // Member declaration
     $b->append(' {');
     // public static self[] values() { return parent::membersOf(__CLASS__) }
     $declaration->body[] = new MethodNode(['modifiers' => MODIFIER_PUBLIC | MODIFIER_STATIC, 'annotations' => null, 'name' => 'values', 'returns' => new TypeName('self[]'), 'parameters' => null, 'throws' => null, 'body' => [new ReturnNode(new StaticMethodCallNode(new TypeName('parent'), 'membersOf', [new StringNode($this->literal($thisType))]))], 'comment' => '(Generated)']);
     // Members
     foreach ((array) $declaration->body as $node) {
         $this->emitOne($b, $node);
     }
     $this->emitProperties($b, $this->properties[0]);
     // Initialization
     $b->append('static function __static() {');
     foreach ($declaration->body as $i => $member) {
         if (!$member instanceof EnumMemberNode) {
             continue;
         }
         $b->append('self::$' . $member->name . '= ');
         if ($member->body) {
             if (!$abstract) {
                 $this->error('E403', 'Only abstract enums can contain members with bodies (' . $member->name . ')');
                 // Continues so declaration is closed
             }
             $unique = new TypeName($declaration->name->name . '··' . $member->name);
             $decl = new ClassNode(0, null, $unique, $declaration->name, [], $member->body);
             $decl->synthetic = true;
             $ptr = new TypeDeclaration(new ParseTree(null, [], $decl), $thisType);
             $this->scope[0]->declarations[] = $decl;
             $b->append('new ' . $unique->name . '(');
         } else {
             $b->append('new self(');
         }
         if ($member->value) {
             $this->emitOne($b, $member->value);
         } else {
             $b->append($i);
         }
         $b->append(', \'' . $member->name . '\');');
     }
     $b->append('}');
     // Finish
     $b->append('}');
     $this->leave();
     $this->registerClass($b, $declaration, $thisType->name());
     array_shift($this->properties);
     array_shift($this->metadata);
     // Register type info
     $this->types[0]->name = $thisType->name();
     $this->types[0]->kind = Types::ENUM_KIND;
     $this->types[0]->literal = $this->declaration($declaration);
     $this->types[0]->parent = $parentType;
     $this->types[0]->modifiers = $declaration->modifiers;
 }