コード例 #1
0
 /**
  * @param array $input
  * @param array $expected
  *
  * @dataProvider evaluateDataProvider
  */
 public function testEvaluate(array $input, array $expected)
 {
     $this->_itemInterpreter->expects($this->any())->method('evaluate')->will($this->returnCallback(function ($input) {
         return '-' . $input['value'] . '-';
     }));
     $actual = $this->_model->evaluate($input);
     $this->assertSame($expected, $actual);
 }
コード例 #2
0
ファイル: TypehintHelper.php プロジェクト: phpstan/phpstan
 public static function decideType(Type $type, Type $phpDocType = null) : Type
 {
     if ($phpDocType !== null) {
         if ($type instanceof IterableType && $phpDocType instanceof ArrayType) {
             if ($type instanceof IterableIterableType) {
                 $phpDocType = new IterableIterableType($phpDocType->getItemType(), $type->isNullable() || $phpDocType->isNullable());
             } elseif ($type instanceof ArrayType) {
                 $type = new ArrayType($phpDocType->getItemType(), $type->isNullable() || $phpDocType->isNullable());
             }
         }
         if ($type->accepts($phpDocType)) {
             return $phpDocType;
         }
     }
     return $type;
 }
コード例 #3
0
ファイル: TypehintHelper.php プロジェクト: phpstan/phpstan
 public static function decideType(\ReflectionType $reflectionType = null, Type $phpDocType = null, string $selfClass = null, bool $isVariadic = false) : Type
 {
     if ($reflectionType === null) {
         return $phpDocType !== null ? $phpDocType : new MixedType(true);
     }
     $reflectionTypeString = (string) $reflectionType;
     if ($isVariadic) {
         $reflectionTypeString .= '[]';
     }
     $type = self::getTypeObjectFromTypehint($reflectionTypeString, $reflectionType->allowsNull(), $selfClass);
     if ($phpDocType !== null) {
         if ($type instanceof ArrayType && $phpDocType instanceof ArrayType) {
             $type = new ArrayType($phpDocType->getItemType(), $type->isNullable() || $phpDocType->isNullable());
         }
         if ($type->accepts($phpDocType)) {
             return $phpDocType;
         }
     }
     return $type;
 }
 public static function init()
 {
     self::$CLASS_TO_VALUE_READER = new IdentityHashMap();
     self::$CLASS_TO_VECTOR_READER = new IdentityHashMap();
     self::$CLASS_TO_VECTOR_READER->put(ArrayType::clazz(Boolean::typeClass()), new SSSR_VectorReader_Boolean());
     self::$CLASS_TO_VECTOR_READER->put(ArrayType::clazz(Byte::typeClass()), new SSSR_VectorReader_Byte());
     self::$CLASS_TO_VECTOR_READER->put(ArrayType::clazz(Character::typeClass()), new SSSR_VectorReader_Char());
     self::$CLASS_TO_VECTOR_READER->put(ArrayType::clazz(Double::typeClass()), new SSSR_VectorReader_Double());
     self::$CLASS_TO_VECTOR_READER->put(ArrayType::clazz(Float::typeClass()), new SSSR_VectorReader_Float());
     self::$CLASS_TO_VECTOR_READER->put(ArrayType::clazz(Integer::typeClass()), new SSSR_VectorReader_Int());
     self::$CLASS_TO_VECTOR_READER->put(ArrayType::clazz(Long::typeClass()), new SSSR_VectorReader_Long());
     self::$CLASS_TO_VECTOR_READER->put(ArrayType::clazz(Object::clazz()), new SSSR_VectorReader_Object());
     self::$CLASS_TO_VECTOR_READER->put(ArrayType::clazz(Short::typeClass()), new SSSR_VectorReader_Short());
     self::$CLASS_TO_VECTOR_READER->put(ArrayType::clazz(String::clazz()), new SSSR_VectorReader_String());
     self::$CLASS_TO_VALUE_READER->put(Boolean::typeClass(), new SSSR_ValueReader_Boolean());
     self::$CLASS_TO_VALUE_READER->put(Byte::typeClass(), new SSSR_ValueReader_Byte());
     self::$CLASS_TO_VALUE_READER->put(Character::typeClass(), new SSSR_ValueReader_Char());
     self::$CLASS_TO_VALUE_READER->put(Double::typeClass(), new SSSR_ValueReader_Double());
     self::$CLASS_TO_VALUE_READER->put(Float::typeClass(), new SSSR_ValueReader_Float());
     self::$CLASS_TO_VALUE_READER->put(Integer::typeClass(), new SSSR_ValueReader_Int());
     self::$CLASS_TO_VALUE_READER->put(Long::typeClass(), new SSSR_ValueReader_Long());
     self::$CLASS_TO_VALUE_READER->put(Object::clazz(), new SSSR_ValueReader_Object());
     self::$CLASS_TO_VALUE_READER->put(Short::typeClass(), new SSSR_ValueReader_Short());
     self::$CLASS_TO_VALUE_READER->put(String::clazz(), new SSSR_ValueReader_String());
 }
コード例 #5
0
 public function cast_raises_exceptions_for_non_arrays($value)
 {
     ArrayType::forName('var[]')->cast($value);
 }
コード例 #6
0
 public function stringArrayDeprecatedSyntax()
 {
     $this->assertEquals(ArrayType::forName('string[]'), Type::forName('array<string>'));
 }
コード例 #7
0
 /**
  * @param Node $node
  * A node to check types on
  *
  * @return UnionType
  * The resulting type(s) of the binary operation
  */
 public function visitBinaryAdd(Node $node) : UnionType
 {
     $left = UnionType::fromNode($this->context, $this->code_base, $node->children['left']);
     $right = UnionType::fromNode($this->context, $this->code_base, $node->children['right']);
     // fast-track common cases
     if ($left->isType(IntType::instance()) && $right->isType(IntType::instance())) {
         return IntType::instance()->asUnionType();
     }
     if (($left->isType(IntType::instance()) || $left->isType(FloatType::instance())) && ($right->isType(IntType::instance()) || $right->isType(FloatType::instance()))) {
         return FloatType::instance()->asUnionType();
     }
     $left_is_array = !empty($left->genericArrayElementTypes()) && empty($left->nonGenericArrayTypes());
     $right_is_array = !empty($right->genericArrayElementTypes()) && empty($right->nonGenericArrayTypes());
     if ($left_is_array && !$right->canCastToUnionType(ArrayType::instance()->asUnionType())) {
         Log::err(Log::ETYPE, "invalid operator: left operand is array and right is not", $this->context->getFile(), $node->lineno);
         return new UnionType();
     } else {
         if ($right_is_array && !$left->canCastToUnionType(ArrayType::instance()->asUnionType())) {
             Log::err(Log::ETYPE, "invalid operator: right operand is array and left is not", $this->context->getFile(), $node->lineno);
             return new UnionType();
         } else {
             if ($left_is_array || $right_is_array) {
                 // If it is a '+' and we know one side is an array
                 // and the other is unknown, assume array
                 return ArrayType::instance()->asUnionType();
             }
         }
     }
     return new UnionType([IntType::instance(), FloatType::instance()]);
 }
コード例 #8
0
ファイル: UnionType.php プロジェクト: zhangf911/phan
 /**
  * Takes "a|b[]|c|d[]|e" and returns "b|d"
  *
  * @return UnionType
  * The subset of types in this
  */
 public function genericArrayElementTypes() : UnionType
 {
     // If array is in there, then it can be any type
     // Same for mixed
     if ($this->hasType(ArrayType::instance()) || $this->hasType(MixedType::instance())) {
         return MixedType::instance()->asUnionType();
     }
     if ($this->hasType(ArrayType::instance())) {
         return NullType::instance()->asUnionType();
     }
     return new UnionType(array_filter(array_map(function (Type $type) {
         if (!$type->isGenericArray()) {
             return null;
         }
         return $type->genericArrayElementType();
     }, $this->getTypeList())));
 }
コード例 #9
0
ファイル: TypeTest.class.php プロジェクト: xp-framework/core
 public function arrayOfString()
 {
     $this->assertEquals(ArrayType::forName('string[]'), Type::forName('string[]'));
 }
コード例 #10
0
ファイル: UnionTypeVisitor.php プロジェクト: themarios/phan
 /**
  * Visit a node with kind `\ast\AST_CAST`
  *
  * @param Node $node
  * A node of the type indicated by the method name that we'd
  * like to figure out the type that it produces.
  *
  * @return UnionType
  * The set of types that are possibly produced by the
  * given node
  */
 public function visitCast(Node $node) : UnionType
 {
     switch ($node->flags) {
         case \ast\flags\TYPE_NULL:
             return NullType::instance()->asUnionType();
         case \ast\flags\TYPE_BOOL:
             return BoolType::instance()->asUnionType();
         case \ast\flags\TYPE_LONG:
             return IntType::instance()->asUnionType();
         case \ast\flags\TYPE_DOUBLE:
             return FloatType::instance()->asUnionType();
         case \ast\flags\TYPE_STRING:
             return StringType::instance()->asUnionType();
         case \ast\flags\TYPE_ARRAY:
             return ArrayType::instance()->asUnionType();
         case \ast\flags\TYPE_OBJECT:
             return ObjectType::instance()->asUnionType();
         default:
             Log::err(Log::EFATAL, "Unknown type (" . $node->flags . ") in cast");
     }
 }
コード例 #11
0
 public static function classOfValue($value)
 {
     $className = '';
     $type = gettype($value);
     if ($type == 'boolean') {
         $className = 'boolean';
     } else {
         if ($type == 'integer') {
             $className = 'int';
         } else {
             if ($type == 'double') {
                 $className = 'double';
             } else {
                 if ($type == 'string') {
                     $className = 'String';
                 } else {
                     if ($type == 'array') {
                         return ArrayType::autoClass($value);
                     } else {
                         if ($type == 'object') {
                             if ($value instanceof Magic) {
                                 return new MagicClazz(get_class($value), $value);
                             } else {
                                 $className = get_class($value);
                             }
                         } else {
                             if ($type == 'resource') {
                                 throw new ClassNotFoundException('Resource have no class');
                             } else {
                                 if ($type == 'NULL') {
                                     $className = 'Void';
                                 } else {
                                     throw new ClassNotFoundException('Type of value is unknown');
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     return self::classOf($className);
 }
コード例 #12
0
 public function visitArray(ArrayValueCommand $x, Context $ctx)
 {
     if ($this->maybePushBackRef($x)) {
         $values = $x->getComponentValues();
         $array = ArrayType::newInstance(null, count($values));
         $size = count($values);
         for ($i = 0; $i < $size; $i++) {
             $this->accept($values[$i]);
             $array[$i] = array_pop($this->values);
         }
         $this->push($x, $array);
     }
     return false;
 }
コード例 #13
0
 public function varArgsArrayType()
 {
     $this->assertEquals(ArrayType::forName('string[]'), $this->methodParameter('printf', 1)->getType());
 }
コード例 #14
0
ファイル: lang.base.php プロジェクト: Gamepay/xp-framework
function typeof($arg)
{
    if ($arg instanceof Generic) {
        return $arg->getClass();
    } else {
        if (NULL === $arg) {
            return Type::$VOID;
        } else {
            if (is_array($arg)) {
                return 0 === key($arg) ? ArrayType::forName('var[]') : MapType::forName('[:var]');
            } else {
                return Type::forName(gettype($arg));
            }
        }
    }
}
コード例 #15
0
 private function makeArray(Clazz $type, array &$value)
 {
     $toReturn = new ArrayValueCommand($type->getComponentType());
     $this->identityMap->put($value, $toReturn);
     for ($i = 0, $j = ArrayType::getLength($value); $i < $j; $i++) {
         $arrayValue = ArrayType::get($value, $i);
         if (is_null($arrayValue)) {
             $toReturn->add(NullValueCommand::INSTANCE());
         } else {
             $valueType = $type->getComponentType()->isPrimitive() ? $type->getComponentType() : Classes::classOfValue($arrayValue);
             $toReturn->add($this->makeValue($valueType, $arrayValue));
         }
     }
     return $toReturn;
 }
コード例 #16
0
ファイル: ArgumentType.php プロジェクト: Seldaek/phan
 /**
  * Check to see if the given Clazz is a duplicate
  *
  * @param Method $method
  * The method we're analyzing arguments for
  *
  * @param Node $node
  * The node holding the method call we're looking at
  *
  * @param Context $context
  * The context in which we see the call
  *
  * @param CodeBase $code_base
  *
  * @return null
  *
  * @see \Phan\Deprecated\Pass2::arg_check
  * Formerly `function arg_check`
  */
 private static function analyzeInternalArgumentType(Method $method, Node $node, Context $context, CodeBase $code_base)
 {
     $arglist = $node->children['args'];
     $argcount = count($arglist->children);
     switch ($method->getName()) {
         case 'join':
         case 'implode':
             // (string glue, array pieces),
             // (array pieces, string glue) or
             // (array pieces)
             if ($argcount == 1) {
                 self::analyzeNodeUnionTypeCast($arglist->children[0], $context, $code_base, ArrayType::instance()->asUnionType(), "arg#1(pieces) is %s but {$method->getFQSEN()}() takes array when passed only 1 arg");
                 return;
             } else {
                 if ($argcount == 2) {
                     $arg1_type = UnionType::fromNode($context, $code_base, $arglist->children[0]);
                     $arg2_type = UnionType::fromNode($context, $code_base, $arglist->children[1]);
                     if ((string) $arg1_type == 'array') {
                         if (!$arg1_type->canCastToUnionType(StringType::instance()->asUnionType())) {
                             Log::err(Log::EPARAM, "arg#2(glue) is {$arg2_type} but {$method->getFQSEN()}() takes string when arg#1 is array", $context->getFile(), $context->getLineNumberStart());
                         }
                     } else {
                         if ((string) $arg1_type == 'string') {
                             if (!$arg2_type->canCastToUnionType(ArrayType::instance()->asUnionType())) {
                                 Log::err(Log::EPARAM, "arg#2(pieces) is {$arg2_type} but {$method->getFQSEN()}() takes array when arg#1 is string", $context->getFile(), $context->getLineNumberStart());
                             }
                         }
                     }
                     return;
                 }
             }
             // Any other arg counts we will let the regular
             // checks handle
             break;
         case 'array_udiff':
         case 'array_diff_uassoc':
         case 'array_uintersect_assoc':
         case 'array_intersect_ukey':
             if ($argcount < 3) {
                 Log::err(Log::EPARAM, "call with {$argcount} arg(s) to {$method->getFQSEN()}() which requires {$method->getNumberOfRequiredParameters()} arg(s)", $context->getFile(), $context->getLineNumberStart());
                 return;
             }
             self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 1], $context, $code_base, CallableType::instance()->asUnionType(), "The last argument to {$method->getFQSEN()} must be a callable");
             for ($i = 0; $i < $argcount - 1; $i++) {
                 self::analyzeNodeUnionTypeCast($arglist->children[$i], $context, $code_base, CallableType::instance()->asUnionType(), "arg#" . ($i + 1) . " is %s but {$method->getFQSEN()}() takes array");
             }
             return;
         case 'array_diff_uassoc':
         case 'array_uintersect_uassoc':
             if ($argcount < 4) {
                 Log::err(Log::EPARAM, "call with {$argcount} arg(s) to {$method->getFQSEN()}() which requires {$method->getNumberOfRequiredParameters()} arg(s)", $context->getFile(), $context->getLineNumberStart());
                 return;
             }
             // The last 2 arguments must be a callable and there
             // can be a variable number of arrays before it
             self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 1], $context, $code_base, CallableType::instance()->asUnionType(), "The last argument to {$method->getFQSEN()} must be a callable");
             self::analyzeNodeUnionTypeCast($arglist->children[$argcount - 2], $context, $code_base, CallableType::instance()->asUnionType(), "The second last argument to {$method->getFQSEN()} must be a callable");
             for ($i = 0; $i < $argcount - 2; $i++) {
                 self::analyzeNodeUnionTypeCast($arglist->children[$i], $context, $code_base, ArrayType::instance()->asUnionType(), "arg#" . ($i + 1) . " is %s but {$method->getFQSEN()}() takes array");
             }
             return;
         case 'strtok':
             // (string str, string token) or (string token)
             if ($argcount == 1) {
                 // If we have just one arg it must be a string token
                 self::analyzeNodeUnionTypeCast($arglist->children[0], $context, $code_base, ArrayType::instance()->asUnionType(), "arg#1(token) is %s but {$method->getFQSEN()}() takes string when passed only one arg");
             }
             // The arginfo check will handle the other case
             break;
         case 'min':
         case 'max':
             if ($argcount == 1) {
                 // If we have just one arg it must be an array
                 if (!self::analyzeNodeUnionTypeCast($arglist->children[0], $context, $code_base, ArrayType::instance()->asUnionType(), "arg#1(values) is %s but {$method->getFQSEN()}() takes array when passed only one arg")) {
                     return;
                 }
             }
             // The arginfo check will handle the other case
             break;
         default:
             break;
     }
 }
コード例 #17
0
 public function arrayComponentType()
 {
     $this->assertEquals(ArrayType::forName('int[]'), MapType::forName('[:int[]]')->componentType());
 }
コード例 #18
0
ファイル: Type.php プロジェクト: nikkisnow/phan
 /**
  * @return Type
  * Get a new type which is the generic array version of
  * this type. For instance, 'int' will produce 'int[]'.
  */
 public function asGenericArrayType() : Type
 {
     if ($this->name == 'array' || $this->name == 'mixed' || strpos($this->name, '[]') !== false) {
         return ArrayType::instance();
     }
     return new \Phan\Language\Type\GenericArrayType($this);
 }
コード例 #19
0
 public function intArray()
 {
     $this->assertEquals(ArrayType::forName('var[]'), typeof(array(1, 2, 3)));
 }
コード例 #20
0
 public function array_of_issues()
 {
     $issue1 = new net·xp_framework·unittest·webservices·rest·IssueWithField(1, 'test1');
     $issue2 = new net·xp_framework·unittest·webservices·rest·IssueWithField(2, 'test2');
     $this->assertEquals(array($issue1, $issue2), $this->fixture->convert(ArrayType::forName($issue1->getClassName() . '[]'), array(array('issue_id' => 1, 'title' => 'test1'), array('issue_id' => 2, 'title' => 'test2'))));
 }
コード例 #21
0
 private function constructorFunctionArray(ArrayValueCommand $x)
 {
     $targetClass = ArrayType::clazz($x->getComponentType());
     $functionName = $this->constructorFunctions->get($targetClass);
     if (!is_null($functionName)) {
         return $functionName;
     }
     $initValuesId = $this->clientOracle->getMethodIdByClassName('com.google.gwt.lang.Array', 'initValues', array('Ljava/lang/Class;', 'Lcom/google/gwt/core/client/JavaScriptObject;', 'I', 'Lcom/google/gwt/lang/Array;'));
     assert(!is_null($initValuesId));
     $classLitId = $this->clientOracle->getFieldIdByClassName('com.google.gwt.lang.ClassLiteralHolder', $this->getJavahSignatureName($x->getComponentType()) . '_classLit');
     assert(!is_null($classLitId));
     $functionName = $this->clientOracle->createUnusedIdent($classLitId);
     $this->constructorFunctions->put($targetClass, $functionName);
     $castableTypeData = $this->clientOracle->getCastableTypeData($targetClass);
     if (is_null($castableTypeData)) {
         $castableTypeData = $this->clientOracle->getCastableTypeData(Classes::classOf('Object[]'));
     }
     $queryId = $this->clientOracle->getQueryId($x->getComponentType());
     if ($queryId == 0) {
         $queryId = $this->clientOracle->getQueryId(Classes::classOf('Object'));
     }
     $ident = '_0';
     // function foo(_0) {return initValues(classLid, castableTypeData, queryId, _0)}
     $this->_function();
     $this->push($functionName);
     $this->lparen();
     $this->push($ident);
     $this->rparen();
     $this->lbrace();
     $this->_return();
     $this->push($initValuesId);
     $this->lparen();
     $this->push($classLitId);
     $this->comma();
     $this->push($castableTypeData->toJs());
     $this->comma();
     $this->push(String::valueOf($queryId));
     $this->comma();
     $this->push($ident);
     $this->rparen();
     $this->rbrace();
     $this->flush($x);
     return $functionName;
 }
コード例 #22
0
 public function varArrayAssignableFromIntArray()
 {
     $this->assertFalse(ArrayType::forName('var[]')->isAssignableFrom('int[]'));
 }
コード例 #23
0
 private function findClass($token)
 {
     if (isset($this->classCache[$token])) {
         return $this->classCache[$token];
     }
     $className = $this->clientOracle->getTypeName($token);
     if ($className == null) {
         $className = $token;
     }
     if (strstr($className, '[]') !== false) {
         $firstIndex = -1;
         $j = -1;
         $dims = 0;
         while (($j = strpos($className, '[')) !== false) {
             if ($dims++ == 0) {
                 $firstIndex = $j;
             }
         }
         $componentType = $this->findClass(substr($className, 0, $firstIndex));
         assert($componentType != null);
         $clazz = ArrayType::clazz($componentType, $dims);
     } else {
         $clazz = Classes::classOf($className);
     }
     $this->classCache[$token] = $clazz;
     return $clazz;
 }
コード例 #24
0
 /**
  * @param Node $node
  * A node to check types on
  *
  * @return UnionType
  * The resulting type(s) of the binary operation
  */
 public function visitBinaryAdd(Node $node) : UnionType
 {
     $left = UnionType::fromNode($this->context, $this->code_base, $node->children['left']);
     $right = UnionType::fromNode($this->context, $this->code_base, $node->children['right']);
     // fast-track common cases
     if ($left->isType(IntType::instance()) && $right->isType(IntType::instance())) {
         return IntType::instance()->asUnionType();
     }
     // If both left and right are arrays, then this is array
     // concatenation.
     if ($left->isGenericArray() && $right->isGenericArray()) {
         if ($left->isEqualTo($right)) {
             return $left;
         }
         return ArrayType::instance()->asUnionType();
     }
     if (($left->isType(IntType::instance()) || $left->isType(FloatType::instance())) && ($right->isType(IntType::instance()) || $right->isType(FloatType::instance()))) {
         return FloatType::instance()->asUnionType();
     }
     $left_is_array = !empty($left->genericArrayElementTypes()) && empty($left->nonGenericArrayTypes()) || $left->isType(ArrayType::instance());
     $right_is_array = !empty($right->genericArrayElementTypes()) && empty($right->nonGenericArrayTypes()) || $right->isType(ArrayType::instance());
     if ($left_is_array && !$right->canCastToUnionType(ArrayType::instance()->asUnionType())) {
         Issue::maybeEmit($this->code_base, $this->context, Issue::TypeInvalidRightOperand, $node->lineno ?? 0);
         return new UnionType();
     } elseif ($right_is_array && !$left->canCastToUnionType(ArrayType::instance()->asUnionType())) {
         Issue::maybeEmit($this->code_base, $this->context, Issue::TypeInvalidLeftOperand, $node->lineno ?? 0);
         return new UnionType();
     } elseif ($left_is_array || $right_is_array) {
         // If it is a '+' and we know one side is an array
         // and the other is unknown, assume array
         return ArrayType::instance()->asUnionType();
     }
     return new UnionType([IntType::instance(), FloatType::instance()]);
 }
コード例 #25
0
 /**
  * Gets a type for a given name
  *
  * Checks for:
  * <ul>
  *   <li>Primitive types (string, integer, double, boolean, array)</li>
  *   <li>Array notations (string[] or string*)</li>
  *   <li>Resources</li>
  *   <li>Any type (var or *)</li>
  *   <li>Generic notations (util.collections.HashTable<lang.types.String, lang.Generic>)</li>
  *   <li>Anything else will be passed to XPClass::forName()</li>
  * </ul>
  *
  * @param   string name
  * @return  lang.Type
  */
 public static function forName($name)
 {
     static $deprecated = array('char' => 'string', 'integer' => 'int', 'boolean' => 'bool', 'float' => 'double', 'mixed' => 'var', '*' => 'var', 'array' => 'var[]', 'resource' => 'var');
     static $primitives = array('string' => TRUE, 'int' => TRUE, 'double' => TRUE, 'bool' => TRUE);
     // Map deprecated type names
     $type = isset($deprecated[$name]) ? $deprecated[$name] : $name;
     // Map well-known primitives, var and void, handle rest syntactically:
     // * T[] is an array
     // * [:T] is a map
     // * T* is a vararg
     // * T<K, V> is a generic
     // * Anything else is a qualified or unqualified class name
     if (isset($primitives[$type])) {
         return Primitive::forName($type);
     } else {
         if ('var' === $type) {
             return self::$VAR;
         } else {
             if ('void' === $type) {
                 return self::$VOID;
                 return $type;
             } else {
                 if ('[]' === substr($type, -2)) {
                     return ArrayType::forName($type);
                 } else {
                     if ('[:' === substr($type, 0, 2)) {
                         return MapType::forName($type);
                     } else {
                         if ('*' === substr($type, -1)) {
                             return ArrayType::forName(substr($type, 0, -1) . '[]');
                         } else {
                             if (FALSE === ($p = strpos($type, '<'))) {
                                 return strstr($type, '.') ? XPClass::forName($type) : new XPClass($type);
                             }
                         }
                     }
                 }
             }
         }
     }
     // Generics
     // * D<K, V> is a generic type definition D with K and V componenty
     // * Deprecated: array<T> is T[], array<K, V> is [:T]
     $base = substr($type, 0, $p);
     $components = self::forNames(substr($type, $p + 1, -1));
     if ('array' !== $base) {
         return cast(self::forName($base), 'lang.XPClass')->newGenericType($components);
     }
     $s = sizeof($components);
     if (2 === $s) {
         return MapType::forName('[:' . $components[1]->name . ']');
     } else {
         if (1 === $s) {
             return ArrayType::forName($components[0]->name . '[]');
         }
     }
     throw new IllegalArgumentException('Unparseable name ' . $name);
 }