public void TrackRemoteScript(TES5ScriptHeader scriptHeader) { this.trackedScript = scriptHeader; ITES5Type ourNativeType = this.propertyType.NativeType; ITES5Type remoteNativeType = this.trackedScript.ScriptType.NativeType; /* * Scenario 1 - types are equal or the remote type is higher than ours in which case we do nothing as they have the good type anyway */ if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(remoteNativeType, ourNativeType)) { return; } /* * Scenario 2 - Our current native type is extending remote script"s extended type - we need to set it properly */ else if (TES5InheritanceGraphAnalyzer.IsExtending(ourNativeType, remoteNativeType)) { this.trackedScript.SetNativeType(ourNativeType); } else { //WTM: Note: Special Cases: Let some script transpile without throwing. if (!( (ourNativeType == TES5BasicType.T_ACTIVATOR && remoteNativeType == TES5BasicType.T_OBJECTREFERENCE && (scriptHeader.EDID == "SE01MetronomeScript" || scriptHeader.EDID == "SE11TrigZoneHowlingHallsEnterSCRIPT")) || (ourNativeType == TES5BasicType.T_ACTORBASE && remoteNativeType == TES5BasicType.T_ACTOR && (scriptHeader.EDID == "HieronymusLexScript" || scriptHeader.EDID == "SERunsInCirclesSCRIPT")))) { throw new ConversionException(nameof(TES5Property) + "." + nameof(TrackRemoteScript) + ": The definitions of local property type and remote property type have diverged. (Ours: " + ourNativeType.Value + ", remote: " + remoteNativeType.Value + ")"); } } }
public override void TrackRemoteScript(TES5ScriptHeader scriptHeader) { this.trackedScript = scriptHeader; ITES5Type ourNativeType = this.propertyType.NativeType; ITES5Type remoteNativeType = this.trackedScript.ScriptType.NativeType; /* * Scenario 1 - types are equal or the remote type is higher than ours in which case we do nothing as they have the good type anyway */ if (ourNativeType == remoteNativeType || TES5InheritanceGraphAnalyzer.IsExtending(remoteNativeType, ourNativeType)) { return; } /* * Scenario 2 - Our current native type is extending remote script"s extended type - we need to set it properly */ else if (TES5InheritanceGraphAnalyzer.IsExtending(ourNativeType, remoteNativeType)) { this.trackedScript.SetNativeType(ourNativeType); } else { throw new ConversionException(nameof(TES5Property) + "." + nameof(TrackRemoteScript) + ": The definitions of local property type and remote property type have diverged. (Ours: " + ourNativeType.Value + ", remote: " + remoteNativeType.Value); } }
/* * @throws ConversionException */ public void SetNativeType(ITES5Type scriptType, bool ensureNewTypeExtendsCurrentType = true) { if (ensureNewTypeExtendsCurrentType && !TES5InheritanceGraphAnalyzer.IsExtending(scriptType, this.ScriptType.NativeType)) { throw new ConversionException("Cannot set script type to non-extending type - current native type " + this.ScriptType.NativeType.Value + ", new type " + scriptType.Value); } this.ScriptType.NativeType = scriptType.NativeType; }
/* * Try to inference variable"s type with type. * * * Needed for proxifying the properties to other scripts * - Will return true if inferencing succeeded, false otherwise. * @throws ConversionException */ private bool InferenceType(ITES5VariableOrProperty variable, ITES5Type type, TES5MultipleScriptsScope multipleScriptsScope) { if (!TES5InheritanceGraphAnalyzer.IsExtending(type, variable.TES5Type.NativeType)) { return(false); } variable.TES5Type = type; return(true); }
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)); }
/* * Try to inference variable"s type with type. * * * Needed for proxifying the properties to other scripts * - Will return true if inferencing succeeded, false otherwise. * @throws ConversionTypeMismatchException */ private void InferenceType(ITES5VariableOrProperty variable, TES5BasicType type) { if (!TES5InheritanceGraphAnalyzer.IsTypeOrExtendsTypeOrIsNumberType(type, variable.TES5Type.NativeType, false)) { if (TES5InheritanceGraphAnalyzer.IsExtending(variable.TES5Type.NativeType, type)) { return; } throw new ConversionTypeMismatchException("Could not extend " + variable.TES5Type.NativeType.Value + " to " + type.Value + "."); } if (variable.TES5Type.AllowInference) { variable.TES5Type = type; } else if (variable.TES5Type.AllowNativeTypeInference) { variable.TES5Type.NativeType = type; } else { throw new ConversionTypeMismatchException(variable.Name + " (" + variable.TES5DeclaredType.OriginalName + " : " + variable.TES5DeclaredType.NativeType.Name + ") could not be inferenced to a " + type.Name + " because inference was not allowed."); } }
/* * Joins N QF subfragments into one QF fragment that can be properly binded into Skyrim VM * @throws ConversionException */ public TES5Target JoinQFFragments(IBuildTarget target, string resultingFragmentName, List <QuestStageScript> subfragmentsTrees) { int tes4FormID = GetTES4FormID(resultingFragmentName); StageMap stageMap = StageMapBuilder.Build(target, resultingFragmentName, esmAnalyzer, tes4FormID); /* * We need script fragment for objective handling for each stage, so when parsing the script fragments, * we"ll be marking them there, and intersecting this with stage. * This will give us an array of stages which don"t have script fragment, but will need it anyways * for objective handling. */ TES5ScriptHeader resultingScriptHeader = TES5ScriptHeaderFactory.GetFromCacheOrConstructByBasicType(resultingFragmentName, TES5BasicType.T_QUEST, TES5TypeFactory.TES4_Prefix, true); TES5GlobalScope resultingGlobalScope = new TES5GlobalScope(resultingScriptHeader); string[] referenceAliases = ReferenceAliasBuilder.Build(target, resultingFragmentName, esmAnalyzer, tes4FormID).ToArray(); foreach (string propertyName in referenceAliases) { resultingGlobalScope.AddProperty(TES5PropertyFactory.ConstructWithoutFormID(propertyName, TES5BasicType.T_REFERENCEALIAS, propertyName)); } List <QuestStageBlock> questStageBlocks = new List <QuestStageBlock>(); HashSet <int> implementedStages = new HashSet <int>(); HashSet <string> propertiesNamesDeclared = new HashSet <string>(); foreach (var subfragment in subfragmentsTrees) { TES5Target subfragmentsTree = subfragment.Script; TES5Script subfragmentScript = subfragmentsTree.Script; TES5GlobalScope subfragmentGlobalScope = subfragmentScript.GlobalScope; foreach (TES5Property subfragmentProperty in subfragmentGlobalScope.Properties) { /* * Move over the properties to the new global scope */ if (propertiesNamesDeclared.Add(subfragmentProperty.Name)) { resultingGlobalScope.AddProperty(subfragmentProperty); } else { if (subfragmentProperty.IsPlayerRef) { continue; } //WTM: Change: I don't think renaming these properties actually helps anything. /* * string propertyName = GeneratePropertyName(subfragmentScript.ScriptHeader, subfragmentProperty); * subfragmentProperty.Rename(propertyName); * if (!propertiesNamesDeclared.Add(subfragmentProperty.Name)) * { * throw new ConversionException(nameof(propertiesNamesDeclared) + " already contained property " + subfragmentProperty.Name + "."); * } */ //WTM: Change: I'm trying to unify properties and include extended type declarations. TES5Property existingProperty = resultingGlobalScope.GetPropertyByName(subfragmentProperty.Name); if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(existingProperty.TES5Type, subfragmentProperty.TES5Type)) { continue; } if (TES5InheritanceGraphAnalyzer.IsExtending(subfragmentProperty.TES5Type, existingProperty.TES5Type)) { existingProperty.TES5Type = subfragmentProperty.TES5Type; continue; } if (TES5InheritanceGraphAnalyzer.IsExtending(existingProperty.TES5Type, subfragmentProperty.TES5Type.NativeType)) { subfragmentProperty.TES5Type.NativeType = existingProperty.TES5Type.NativeType; existingProperty.TES5Type = subfragmentProperty.TES5Type; continue; } throw new ConversionException("Types were not compatible for property " + subfragmentProperty.Name + ": " + subfragmentProperty.TES5Type.Value + " should extend " + existingProperty.TES5Type.Value + " (" + existingProperty.TES5Type.NativeType.Value + ")."); } } List <ITES5CodeBlock> subfragmentBlocks = subfragmentScript.BlockList.Blocks; if (subfragmentBlocks.Count != 1) { throw new ConversionException("Wrong QF fragment, actual function count: " + subfragmentBlocks.Count + ".."); } ITES5CodeBlock subfragmentBlock = subfragmentBlocks[0]; if (subfragmentBlock.FunctionScope.BlockName != TES5FragmentFactory.GetFragmentName(0)) { throw new ConversionException("Wrong QF fragment funcname, actual function name: " + subfragmentBlock.FunctionScope.BlockName + "."); } string newFragmentFunctionName = TES5FragmentFactory.GetFragmentName(subfragment.Stage, subfragment.LogIndex); subfragmentBlock.FunctionScope.Rename(newFragmentFunctionName); List <int>?stageMapOfStage = stageMap.TryGetStageTargetsMap(subfragment.Stage); if (stageMapOfStage != null) { var objectiveCodeChunks = objectiveHandlingFactory.GenerateObjectiveHandling(subfragmentBlock, resultingGlobalScope, stageMapOfStage); foreach (var newCodeChunk in objectiveCodeChunks) { subfragmentBlock.AddChunk(newCodeChunk); } } questStageBlocks.Add(new QuestStageBlock(subfragment.Stage, subfragment.LogIndex, subfragmentBlock)); implementedStages.Add(subfragment.Stage); } /* * Diff to find stages which we still need to mark */ int[] nonDoneStages = stageMap.StageIDs.Where(stageID => !implementedStages.Contains(stageID)).ToArray(); foreach (int nonDoneStage in nonDoneStages) { TES5FunctionCodeBlock fragment = objectiveHandlingFactory.CreateEnclosedFragment(resultingGlobalScope, nonDoneStage, stageMap.GetStageTargetsMap(nonDoneStage)); questStageBlocks.Add(new QuestStageBlock(nonDoneStage, 0, fragment)); } this.mappedTargetsLogService.WriteScriptName(resultingFragmentName); foreach (var kvp in stageMap.MappedTargetsIndex) { var originalTargetIndex = kvp.Key; var mappedTargetIndexes = kvp.Value; this.mappedTargetsLogService.WriteLine(originalTargetIndex, mappedTargetIndexes); } TES5BlockList resultingBlockList = new TES5BlockList(questStageBlocks.OrderBy(b => b.StageID).ThenBy(b => b.LogIndex).Select(b => b.CodeBlock)); TES5Script resultingTree = new TES5Script(resultingGlobalScope, resultingBlockList, true); string outputPath = target.GetTranspileToPath(resultingFragmentName); return(new TES5Target(resultingTree, outputPath)); }
private bool ReferenceIsIntAndValueExtendsForm() { return(this.Reference.TES5Type == TES5BasicType.T_INT && TES5InheritanceGraphAnalyzer.IsExtending(this.value.TES5Type, TES5BasicType.T_FORM)); }