private void InferenceTypeOfMethodArguments(TES5ObjectCall objectCall, TES5MultipleScriptsScope multipleScriptsScope) { /* * Inference the arguments */ int argumentIndex = 0; ITES5Type calledOnType = objectCall.AccessedObject.TES5Type.NativeType; foreach (ITES5Value argument in objectCall.Arguments) { /* * Get the argument type according to TES5Inheritance graph. */ ITES5Type argumentTargetType = TES5InheritanceGraphAnalyzer.FindTypeByMethodParameter(calledOnType, objectCall.FunctionName, argumentIndex); if (argument.TES5Type != argumentTargetType) { /* * 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? :) */ ITES5Referencer referencerArgument = argument as ITES5Referencer; if (referencerArgument != null && TES5InheritanceGraphAnalyzer.IsExtending(argumentTargetType, argument.TES5Type.NativeType)) { //HACKY! this.InferenceType(referencerArgument.ReferencesTo, 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.TES5Type == TES5BasicType.T_FLOAT) { TES5Castable argumentCastable = argument as TES5Castable; if (argumentCastable != null) { //HACKY! When we"ll clean up this interface, it will dissapear :) argumentCastable.ManualCastTo = argumentTargetType; } } else if ( !TES5InheritanceGraphAnalyzer.IsExtending(argument.TES5Type, argumentTargetType) && !TES5InheritanceGraphAnalyzer.IsNumberTypeOrBoolAndInt(argument.TES5Type, argumentTargetType) && !(argument is TES5None && TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(argumentTargetType, (new TES5None()).TES5Type))) { bool expected = objectCall.AccessedObject.TES5Type.OriginalName == "TES4TimerHelper" && objectCall.FunctionName == "LegacySay"; throw new ConversionException("Argument type mismatch at " + objectCall.FunctionName + " index " + argumentIndex + ". Expected " + argumentTargetType.OriginalName + ". Found " + argument.TES5Type.OriginalName + ".", expected: expected); } } } argumentIndex++; } }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { TES4FunctionArguments functionArguments = function.Arguments; TES5LocalScope localScope = codeScope.LocalScope; TES5ObjectCallArguments arguments = new TES5ObjectCallArguments() { calledOn }; ITES5Value argument1 = this.valueFactory.CreateValue(functionArguments[0], codeScope, globalScope, multipleScriptsScope); if (argument1.TES5Type != TES5BasicType.T_TOPIC) { TES5Castable argument1Castable = argument1 as TES5Reference; if (argument1Castable != null && TES5InheritanceGraphAnalyzer.IsExtending(TES5BasicType.T_TOPIC, argument1.TES5Type)) { argument1Castable.ManualCastTo = TES5BasicType.T_TOPIC; } } arguments.Add(argument1); ITES4StringValue optionalFlag = functionArguments.GetOrNull(2); if (optionalFlag != null) { string optionalFlagDataString = optionalFlag.StringValue; if (this.analyzer.GetFormTypeByEDID(optionalFlagDataString).Value != TES4RecordType.REFR.Name) { this.metadataLogService.WriteLine("ADD_SPEAK_AS_ACTOR", new string[] { optionalFlagDataString }); optionalFlag = new TES4ApiToken(optionalFlag.Data + "Ref"); } arguments.Add(this.valueFactory.CreateValue(optionalFlag, codeScope, globalScope, multipleScriptsScope)); } else { arguments.Add(new TES5None()); } arguments.Add(new TES5Bool(true)); ITES5Referencer timerReference = this.referenceFactory.CreateTimerReadReference(globalScope, multipleScriptsScope, localScope); return(this.objectCallFactory.CreateObjectCall(timerReference, "LegacySay", multipleScriptsScope, arguments)); }