/**
  * @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()]);
 }
 /**
  * @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()]);
 }
Exemple #3
0
 /**
  * @param object|array|null
  * @expectedException \InvalidArgumentException
  * @expectedExceptionMessage Expected double (a float), got
  * @dataProvider invalidFloatProvider
  */
 public function testInvalidUnrestrictedDouble($value)
 {
     FloatType::toUnrestrictedDouble($value);
 }
Exemple #4
0
 /**
  * @param UnionType $target
  * A type to check to see if this can cast to it
  *
  * @return bool
  * True if this type is allowed to cast to the given type
  * i.e. int->float is allowed  while float->int is not.
  *
  * @see \Phan\Deprecated\Pass2::type_check
  * Formerly 'function type_check'
  */
 public function canCastToUnionType(UnionType $target) : bool
 {
     // Fast-track most common cases first
     // If either type is unknown, we can't call it
     // a success
     if ($this->isEmpty() || $target->isEmpty()) {
         return true;
     }
     // T === T
     if ($this->isEqualTo($target)) {
         return true;
     }
     if (Config::get()->null_casts_as_any_type) {
         // null <-> null
         if ($this->isType(NullType::instance()) || $target->isType(NullType::instance())) {
             return true;
         }
     }
     // mixed <-> mixed
     if ($target->hasType(MixedType::instance()) || $this->hasType(MixedType::instance())) {
         return true;
     }
     // int -> float
     if ($this->isType(IntType::instance()) && $target->isType(FloatType::instance())) {
         return true;
     }
     // Check conversion on the cross product of all
     // type combinations and see if any can cast to
     // any.
     foreach ($this->getTypeList() as $source_type) {
         if (empty($source_type)) {
             continue;
         }
         foreach ($target->getTypeList() as $target_type) {
             if (empty($target_type)) {
                 continue;
             }
             if ($source_type->canCastToType($target_type)) {
                 return true;
             }
         }
     }
     // Only if no source types can be cast to any target
     // types do we say that we cannot perform the cast
     return false;
 }
Exemple #5
0
 /**
  * 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");
     }
 }
Exemple #6
0
 public function testFromStringWithFloat()
 {
     $this->assertSame(FloatType::getInstance(), MixedType::fromString("float"));
 }
Exemple #7
0
 /**
  * 与えられた値を、指定された型に変換して返します。
  * @link https://heycam.github.io/webidl/#idl-types Web IDL (Second Edition)
  * @link https://www.w3.org/TR/WebIDL/#idl-types Web IDL
  * @param string $type
  * @param mixed $value
  * @param array $pseudoTypes callback interface 型、列挙型、callback 関数型、または dictionary 型の識別子をキーとした型情報の配列。
  * @return mixed
  */
 public static function to($type, $value, $pseudoTypes = null)
 {
     switch ($type) {
         case 'any':
             $returnValue = $value;
             break;
         case 'boolean':
             $returnValue = BooleanType::toBoolean($value);
             break;
         case '[EnforceRange] byte':
             $returnValue = IntegerType::toByte($value, '[EnforceRange]');
             break;
         case '[Clamp] byte':
             $returnValue = IntegerType::toByte($value, '[Clamp]');
             break;
         case 'byte':
             $returnValue = IntegerType::toByte($value);
             break;
         case '[EnforceRange] octet':
             $returnValue = IntegerType::toOctet($value, '[EnforceRange]');
             break;
         case '[Clamp] octet':
             $returnValue = IntegerType::toOctet($value, '[Clamp]');
             break;
         case 'octet':
             $returnValue = IntegerType::toOctet($value);
             break;
         case '[EnforceRange] short':
             $returnValue = IntegerType::toShort($value, '[EnforceRange]');
             break;
         case '[Clamp] short':
             $returnValue = IntegerType::toShort($value, '[Clamp]');
             break;
         case 'short':
             $returnValue = IntegerType::toShort($value);
             break;
         case '[EnforceRange] unsigned short':
             $returnValue = IntegerType::toUnsignedShort($value, '[EnforceRange]');
             break;
         case '[Clamp] unsigned short':
             $returnValue = IntegerType::toUnsignedShort($value, '[Clamp]');
             break;
         case 'unsigned short':
             $returnValue = IntegerType::toUnsignedShort($value);
             break;
         case '[EnforceRange] long':
             $returnValue = IntegerType::toLong($value, '[EnforceRange]');
             break;
         case '[Clamp] long':
             $returnValue = IntegerType::toLong($value, '[Clamp]');
             break;
         case 'long':
             $returnValue = IntegerType::toLong($value);
             break;
         case '[EnforceRange] unsigned long':
             $returnValue = IntegerType::toUnsignedLong($value, '[EnforceRange]');
             break;
         case '[Clamp] unsigned long':
             $returnValue = IntegerType::toUnsignedLong($value, '[Clamp]');
             break;
         case 'unsigned long':
             $returnValue = IntegerType::toUnsignedLong($value);
             break;
         case '[EnforceRange] long long':
             $returnValue = IntegerType::toLongLong($value, '[EnforceRange]');
             break;
         case '[Clamp] long long':
             $returnValue = IntegerType::toLongLong($value, '[Clamp]');
             break;
         case 'long long':
             $returnValue = IntegerType::toLongLong($value);
             break;
         case '[EnforceRange] unsigned long long':
             $returnValue = IntegerType::toUnsignedLongLong($value, '[EnforceRange]');
             break;
         case '[Clamp] unsigned long long':
             $returnValue = IntegerType::toUnsignedLongLong($value, '[Clamp]');
             break;
         case 'unsigned long long':
             $returnValue = IntegerType::toUnsignedLongLong($value);
             break;
         case 'float':
             $returnValue = FloatType::toFloat($value);
             break;
         case 'unrestricted float':
             $returnValue = FloatType::toUnrestrictedFloat($value);
             break;
         case 'double':
             $returnValue = FloatType::toDouble($value);
             break;
         case 'unrestricted double':
             $returnValue = FloatType::toUnrestrictedDouble($value);
             break;
         case 'DOMString':
             $returnValue = StringType::toDOMString($value);
             break;
         case 'ByteString':
             $returnValue = StringType::toByteString($value);
             break;
         case 'USVString':
             $returnValue = StringType::toUSVString($value);
             break;
         case 'object':
             $returnValue = ObjectType::toObject($value);
             break;
         case 'Date':
             $returnValue = ObjectType::toInterface($value, 'DateTimeInterface');
             break;
         case 'RegExp':
             $returnValue = RegExpType::toRegExp($value);
             break;
         case 'Error':
             $returnValue = self::to('(esperecyan\\webidl\\Error or DOMException)', $value, $pseudoTypes);
             break;
         case 'DOMException':
             $returnValue = ObjectType::toInterface($value, 'DOMException');
             break;
         default:
             $pattern = '/^(?:(?<nullable>.+)\\?|sequence<(?<sequence>.+)>|(?<array>.+)\\[]|(?<union>\\(.+\\))|FrozenArray<(?<FrozenArray>.+)>)$/u';
             if (preg_match($pattern, $type, $matches) === 1) {
                 if (!empty($matches['nullable'])) {
                     $returnValue = NullableType::toNullable($value, $matches['nullable'], $pseudoTypes);
                 } elseif (!empty($matches['sequence'])) {
                     $returnValue = SequenceType::toSequence($value, $matches['sequence'], $pseudoTypes);
                 } elseif (!empty($matches['array'])) {
                     $returnValue = SequenceType::toArray($value, $matches['array'], $pseudoTypes);
                 } elseif (!empty($matches['union'])) {
                     $returnValue = UnionType::toUnion($value, $matches['union'], $pseudoTypes);
                 } elseif (!empty($matches['FrozenArray'])) {
                     $returnValue = SequenceType::toFrozenArray($value, $matches['FrozenArray'], $pseudoTypes);
                 }
             } elseif (isset($pseudoTypes[$type])) {
                 $pseudoType = $pseudoTypes[$type];
                 switch ($pseudoType) {
                     case 'callback interface':
                     case 'single operation callback interface':
                         $returnValue = ObjectType::toCallbackInterface($value, $pseudoType === 'single operation callback interface');
                         break;
                     case 'callback function':
                         $returnValue = ObjectType::toCallbackFunction($value);
                         break;
                     default:
                         if (is_string($pseudoType) || isset($pseudoType[0])) {
                             $returnValue = StringType::toEnumerationValue($value, $type, $pseudoType);
                         } else {
                             $returnValue = DictionaryType::toDictionary($value, $type, $pseudoTypes);
                         }
                 }
             } else {
                 $returnValue = ObjectType::toInterface($value, $type);
             }
     }
     return $returnValue;
 }