private function inferenceTypeOfMethodArguments(TES5ObjectCall $objectCall, TES5MultipleScriptsScope $multipleScriptsScope) { /** * Inference the arguments */ $arguments = $objectCall->getArguments(); $argumentNumber = 0; $calledOnType = $objectCall->getAccessedObject()->getType()->getNativeType(); foreach ($arguments->getArguments() as $argument) { /** * Get the argument type according to TES5Inheritance graph. */ $argumentTargetType = TES5InheritanceGraphAnalyzer::findTypeByMethodParameter($calledOnType, $objectCall->getFunctionName(), $argumentNumber); if ($argument->getType() == $argumentTargetType) { ++$argumentNumber; continue; //Same type matched. We do not need to do anything :) } /** * todo - maybe we should move getReferencesTo() to TES5Value and make all of the rest TES5Values just have null references as they do not reference anything? :) */ if (TES5InheritanceGraphAnalyzer::isExtending($argumentTargetType, $argument->getType()->getNativeType()) && $argument instanceof TES5Referencer) { //HACKY! $this->inferenceType($argument->getReferencesTo(), $argumentTargetType, $multipleScriptsScope); } else { //So there's one , one special case where we actually have to cast a var from one to another even though they are not ,,inheriting" from themselves, because they are primitives. //Scenario: there's an T_INT argument, and we feed it with a T_FLOAT variable reference. It won't work :( //We need to cast it on call level ( NOT inference it ) to make it work and not break other possible scenarios ( more specifically, when a float would be inferenced to int and there's a //float assigment somewhere in the code ) if ($argumentTargetType == TES5BasicType::T_INT() && $argument->getType() == TES5BasicType::T_FLOAT()) { if ($argument instanceof TES5Reference) { //HACKY! When we'll clean up this interface, it will dissapear :) $argument->setManualCastTo(TES5BasicType::T_INT()); } } } ++$argumentNumber; } }
/** * @param TES5ObjectCall $objectCall * @return mixed|TES5Type * @throws \Ormin\OBSLexicalParser\TES5\Exception\ConversionException * */ public static function findTypeByMethod(TES5ObjectCall $objectCall) { $methodName = $objectCall->getFunctionName(); $possibleMatches = []; foreach (self::$callReturns as $type => $methods) { foreach ($methods as $method => $functionData) { if (strtolower($method) == strtolower($methodName)) { $possibleMatches[] = TES5TypeFactory::memberByValue($type); } } } $calledOn = $objectCall->getAccessedObject()->getReferencesTo(); $extendingMatches = []; $actualType = $calledOn->getPropertyType()->getNativeType(); /** * @var TES5Type[] $possibleMatches */ foreach ($possibleMatches as $possibleMatch) { if ($possibleMatch == $actualType) { return $possibleMatch; //if the possible match matches the actual basic type, it means that it surely IS one of those. } //Ok, so are those matches somehow connected at all? if (TES5InheritanceGraphAnalyzer::isExtending($possibleMatch, $actualType) || TES5InheritanceGraphAnalyzer::isExtending($actualType, $possibleMatch)) { $extendingMatches[] = $possibleMatch; } } switch (count($extendingMatches)) { case 0: $concatTypes = []; foreach ($possibleMatches as $possibleMatch) { $concatTypes[] = $possibleMatch->value(); } throw new ConversionException("Cannot find any possible type for method " . $methodName . ", trying to extend " . $actualType->value() . " with following types: " . implode(', ', $concatTypes)); case 1: return current($extendingMatches); default: //We analyze the property name and check inside the ESM analyzer. $formType = ESMAnalyzer::instance()->getFormTypeByEDID($calledOn->getReferenceEdid()); if (!in_array($formType, $extendingMatches)) { throw new ConversionException("ESM <-> Inheritance Graph conflict, ESM returned " . $formType->value() . ", which is not present in possible matches from inheritance graph"); } return $formType; } }