/** * @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()]); }
/** * @param object|array|null * @expectedException \InvalidArgumentException * @expectedExceptionMessage Expected double (a float), got * @dataProvider invalidFloatProvider */ public function testInvalidUnrestrictedDouble($value) { FloatType::toUnrestrictedDouble($value); }
/** * @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; }
/** * 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"); } }
public function testFromStringWithFloat() { $this->assertSame(FloatType::getInstance(), MixedType::fromString("float")); }
/** * 与えられた値を、指定された型に変換して返します。 * @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; }