/* * Create a generic-purpose reference. */ private ITES5Referencer CreateReference(string referenceName, TES5BasicType?typeForNewProperty, string?tes4ReferenceNameForType, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope, TES5LocalScope localScope) { if (TES5PlayerReference.EqualsPlayer(referenceName)) { return(CreateReferenceToPlayer(globalScope)); } Match match = ReferenceAndPropertyNameRegex.Match(referenceName); if (match.Success) { TES5ObjectProperty propertyReference = this.objectPropertyFactory.CreateObjectProperty(match.Groups[1].Value, match.Groups[2].Value, this, localScope, globalScope, multipleScriptsScope); return(propertyReference); } ITES5VariableOrProperty?property = localScope.TryGetVariable(referenceName); if (property == null) { property = globalScope.TryGetPropertyByOriginalName(referenceName); //todo rethink how to unify the prefix searching if (property == null) { Nullable <int> tes4FormID = null; ITES5Type propertyType = typeForNewProperty != null ? typeForNewProperty : GetPropertyTypeAndFormID(referenceName, tes4ReferenceNameForType, globalScope, multipleScriptsScope, out tes4FormID); TES5Property propertyToAddToGlobalScope = TES5PropertyFactory.ConstructWithTES4FormID(referenceName, propertyType, referenceName, tes4FormID); globalScope.AddProperty(propertyToAddToGlobalScope); property = propertyToAddToGlobalScope; } } return(CreateReferenceToVariableOrProperty(property)); }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { TES5LocalScope localScope = codeScope.LocalScope; TES4FunctionArguments functionArguments = function.Arguments; string dataString = functionArguments[0].StringValue; ITES5Referencer targetReference = this.referenceFactory.CreateReadReference(dataString, globalScope, multipleScriptsScope, localScope); ITES5Type dataType = this.analyzer.GetFormTypeByEDID(dataString); TES5ObjectCall owner; ITES5Referencer baseReference; if (dataType == TES5BasicType.T_FACTION) { owner = this.objectCallFactory.CreateObjectCall(calledOn, "GetFactionOwner", multipleScriptsScope); baseReference = targetReference; } else { owner = this.objectCallFactory.CreateObjectCall(calledOn, "GetActorOwner", multipleScriptsScope); baseReference = this.objectCallFactory.CreateGetActorBase(targetReference, multipleScriptsScope); } TES5ComparisonExpression expression = TES5ExpressionFactory.CreateComparisonExpression(owner, TES5ComparisonExpressionOperator.OPERATOR_EQUAL, baseReference); return(expression); }
/* * Extracts implicit reference from calls. * Returns a reference from calls like: * Enable * Disable * Activate * GetInFaction whatsoever */ public ITES5Referencer ExtractImplicitReference(TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope, TES5LocalScope localScope) { ITES5Type type = globalScope.ScriptHeader.BasicScriptType; if (type == TES5BasicType.T_OBJECTREFERENCE || type == TES5BasicType.T_ACTOR)//Change: WTM: Added Actor here. { return(CreateReferenceToSelf(globalScope)); } else if (type == TES5BasicType.T_ACTIVEMAGICEFFECT) { TES5SelfReference self = CreateReferenceToSelf(globalScope); return(this.objectCallFactory.CreateObjectCall(self, "GetTargetActor", multipleScriptsScope)); } else if (type == TES5BasicType.T_QUEST) { //todo - this should not be done like this //we should actually not try to extract the implicit reference on the non-reference oblivion functions like "stopQuest" //think of this line as a hacky way to just get code forward. return(CreateReferenceToSelf(globalScope)); } else if (type == TES5BasicType.T_TOPICINFO) { /* * TIF Fragments */ return(this.CreateReadReference("akSpeakerRef", globalScope, multipleScriptsScope, localScope)); } throw new ConversionException("Cannot extract implicit reference - unknown basic script type."); }
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); } }
public ITES5ValueCodeChunk ConvertFunction(ITES5Referencer calledOn, TES4Function function, TES5CodeScope codeScope, TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope) { TES4FunctionArguments functionArguments = function.Arguments; TES5ObjectCallArguments args; string functionName; if (functionArguments.Any()) { args = this.objectCallArgumentsFactory.CreateArgumentList(functionArguments, codeScope, globalScope, multipleScriptsScope); ITES5Type arg0Type = analyzer.GetFormTypeByEDID(functionArguments[0].StringValue); if (arg0Type == TES5BasicType.T_ACTOR) { functionName = "SetActorOwner"; } else if (arg0Type == TES5BasicType.T_FACTION) { functionName = "SetFactionOwner"; } else { throw new ConversionException("Unknown setOwnership() param"); } } else { functionName = "SetActorOwner"; args = new TES5ObjectCallArguments() { this.objectCallFactory.CreateGetActorBaseOfPlayer(multipleScriptsScope) }; } return(this.objectCallFactory.CreateObjectCall(calledOn, functionName, multipleScriptsScope, args)); }
private TES5ScriptHeader CreateHeader(TES4Script script) { string edid = script.ScriptHeader.ScriptName; ITES5Type type = this.esmAnalyzer.GetScriptTypeByScriptNameFromCache(edid); return(TES5ScriptHeaderFactory.GetFromCacheOrConstructByBasicType(edid, type, TES5TypeFactory.TES4Prefix, false)); }
private void InferenceTypeOfCalledObject(TES5ObjectCall objectCall, TES5MultipleScriptsScope multipleScriptsScope) { ITES5Type inferencableType = objectCall.AccessedObject.TES5Type.NativeType; /* * Check if we have something to inference inside the code, not some static class or method call return */ if (objectCall.AccessedObject.ReferencesTo != null) { //this is not "exactly" nice solution, but its enough. For now. ITES5Type inferenceType = TES5InheritanceGraphAnalyzer.FindTypeByMethod(objectCall); if (inferencableType == null) { throw new ConversionException("Cannot inference a null type"); } if (inferencableType == inferenceType) { return; //We already have the good type. } if (this.InferenceType(objectCall.AccessedObject.ReferencesTo, inferenceType, multipleScriptsScope)) { return; } } }
public TES5Property(string propertyName, ITES5Type propertyType, string referenceEDID) : base(AddPropertyNameSuffix(propertyName)) { this.propertyType = propertyType; //If we"re tracking a script, this won"t be used anymore this.referenceEDID = referenceEDID; this.trackedScript = null; }
/* * @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; }
public TES5FunctionCodeBlock(TES5FunctionScope functionScope, TES5CodeScope codeScope, ITES5Type returnType, bool isStandalone, bool isQuestFragmentOrTopicFragment) { this.FunctionScope = functionScope; this.CodeScope = codeScope; this.returnType = returnType; this.isStandalone = isStandalone; this.isQuestFragmentOrTopicFragment = isQuestFragmentOrTopicFragment; }
/* * Inference the variable by its reference EDID * * @throws ConversionTypeMismatchException */ public void InferenceVariableByReferenceEdid(ITES5VariableOrProperty variable, TES5MultipleScriptsScope multipleScriptsScope) { if (variable.TES5Type.AllowInference) { ITES5Type type = this.GetScriptTypeByReferenceEdid(variable); this.InferenceWithCustomType(variable, type, multipleScriptsScope); } }
public TES5CustomType(string originalName, string prefix, ITES5Type nativeType) { this.escapedName = NameTransformer.Limit(originalName, prefix); this.prefix = prefix; this.originalName = originalName; //qt = new ReflectionClass(get_class(this));WTM: Change: Unused //this.constants = qt.getConstants(); this.NativeType = nativeType; }
public TES5ScriptHeader(string scriptName, ITES5Type type, string scriptNamePrefix, bool isHidden) { this.OriginalScriptName = scriptName; this.EscapedScriptName = NameTransformer.Limit(scriptName, scriptNamePrefix); this.EDID = scriptName; this.scriptNamePrefix = scriptNamePrefix; this.ScriptType = type; this.isHidden = isHidden; }
private void InferenceWithCustomType(ITES5VariableOrProperty variable, ITES5Type type, TES5MultipleScriptsScope multipleScriptsScope) { /* * We"re referencing another script - find the script and make it a variable that property will track remotely */ TES5ScriptHeader scriptHeader = multipleScriptsScope.GetScriptHeaderOfScript(type.OriginalName); variable.TrackRemoteScript(scriptHeader); }
public TES5ScriptHeader(string scriptName, ITES5Type scriptType, string scriptNamePrefix, bool isHidden = false) { this.OriginalScriptName = scriptName; this.EscapedScriptName = NameTransformer.Limit(scriptName, scriptNamePrefix); this.EDID = scriptName; this.scriptNamePrefix = scriptNamePrefix; this.ScriptType = TES5TypeFactory.MemberByValue(scriptName, scriptType); this.BasicScriptType = scriptType; this.isHidden = isHidden; }
/* * 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 readonly bool isStandalone;//Only needed for PHP_COMPAT public TES5FunctionCodeBlock(TES5FunctionScope functionScope, TES5CodeScope codeScope, ITES5Type returnType, bool isStandalone = false) { if (returnType == null) { throw new ArgumentNullException(nameof(returnType)); } this.FunctionScope = functionScope; this.CodeScope = codeScope; this.returnType = returnType; this.isStandalone = isStandalone; }
public TES5SignatureParameter(string nameWithSuffix, TES5BasicType type, bool hasFixedDeclaration, TES5LocalVariableParameterMeaning[]?meanings = null) { Name = nameWithSuffix; TES5Type = type; declarationType = type; this.hasFixedDeclaration = hasFixedDeclaration; if (meanings == null) { meanings = new TES5LocalVariableParameterMeaning[] { }; } this.Meanings = meanings; }
public TES5Property(string name, ITES5Type propertyType, string?referenceEDID, Nullable <int> tes4FormID, Nullable <int> tes5FormID) { this.IsPlayerRef = name == TES5PlayerReference.PlayerRefName && propertyType == TES5PlayerReference.TES5TypeStatic && referenceEDID == TES5PlayerReference.PlayerRefName; this.OriginalName = name; this.AllowNameTransformation = !this.IsPlayerRef; this.Name = AllowNameTransformation ? AddPropertyNameSuffix(PapyrusCompiler.FixReferenceName(name)) : name; this.propertyType = propertyType; originalPropertyType = propertyType; this.ReferenceEDID = referenceEDID; this.TES4FormID = tes4FormID; this.tes5FormID = tes5FormID; this.trackedScript = null; }
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++; } }
private void InferenceTypeOfCalledObject(TES5ObjectCall objectCall) { /* * Check if we have something to inference inside the code, not some static class or method call return */ if (objectCall.AccessedObject.ReferencesTo == null) { return; } ITES5Type inferencableType = objectCall.AccessedObject.TES5Type.NativeType; //this is not "exactly" nice solution, but its enough. For now. TES5BasicType inferenceType = TES5InheritanceGraphAnalyzer.FindTypeByMethod(objectCall, esmAnalyzer); if (TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(inferencableType, inferenceType)) { return; //We already have the good type. } this.InferenceType(objectCall.AccessedObject.ReferencesTo, inferenceType); }
/* * @todo REFACTOR, it"s really ugly! * @throws ConversionException */ public ITES5Type ResolveScriptTypeByItsAttachedName(string attachedName) { string attachedNameLower = attachedName.ToLower(); ITES5Type value; if (this.attachedNameCache.TryGetValue(attachedNameLower, out value)) { return(value); } TES4LoadedRecord attachedNameRecord = FindInTES4Collection(attachedName, false); if (attachedNameRecord == null) { throw new ConversionException("Cannot resolve script type by searching its base form edid - no record found, " + attachedName); } TES4RecordType attachedNameRecordType = attachedNameRecord.RecordType; if (attachedNameRecordType == TES4RecordType.REFR || attachedNameRecordType == TES4RecordType.ACRE || attachedNameRecordType == TES4RecordType.ACHR) { //Resolve the reference Nullable <int> baseFormid = attachedNameRecord.GetSubrecordAsFormid("NAME"); attachedNameRecord = esm.FindByFormid(baseFormid.Value); } Nullable <int> scriptFormid = attachedNameRecord.GetSubrecordAsFormid("SCRI"); if (scriptFormid == null) { throw new ConversionException("Cannot resolve script type for " + attachedName + " - Asked base record has no script bound."); } TES4LoadedRecord scriptRecord = esm.FindByFormid(scriptFormid.Value); ITES5Type customType = TES5TypeFactory.MemberByValue(scriptRecord.GetSubrecordTrim("EDID")); this.attachedNameCache.Add(attachedNameLower, customType); return(customType); }
/* * Extracts implicit reference from calls. * Returns a reference from calls like: * Enable * Disable * Activate * GetInFaction whatsoever */ public ITES5Referencer ExtractImplicitReference(TES5GlobalScope globalScope, TES5MultipleScriptsScope multipleScriptsScope, TES5LocalScope localScope) { ITES5Type type = globalScope.ScriptHeader.ScriptType.NativeType; if (type == TES5BasicType.T_ACTIVEMAGICEFFECT) { TES5SelfReference self = CreateReferenceToSelf(globalScope); return(this.objectCallFactory.CreateObjectCall(self, "GetTargetActor")); } if (type == TES5BasicType.T_QUEST) { //todo - this should not be done like this //we should actually not try to extract the implicit reference on the non-reference oblivion functions like "stopQuest" //think of this line as a hacky way to just get code forward. return(CreateReferenceToSelf(globalScope)); } if (type == TES5BasicType.T_TOPICINFO) { //TIF Fragments return(this.CreateReadReference("akSpeakerRef", globalScope, multipleScriptsScope, localScope)); } //WTM: Change: I made this the new default result instead of the previous algorithm of exhaustively listing types and throwing an exception if no type matches. return(CreateReferenceToSelf(globalScope)); }
private static void InferEventBlockContainingType(string functionBlockName, TES5GlobalScope globalScope) { TES5BasicType[]? allowedTypes = eventNameToTypes[functionBlockName];//null indicates that any type is allowed if (allowedTypes == null || allowedTypes.Any(t => TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(globalScope.ScriptHeader.ScriptType, t))) { return; } if (allowedTypes.Length == 1) { TES5BasicType singleAllowedType = allowedTypes[0]; if (globalScope.ScriptHeader.ScriptType.AllowNativeTypeInference && TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(singleAllowedType, globalScope.ScriptHeader.ScriptType.NativeType)) { if (globalScope.ScriptHeader.ScriptType.NativeType != singleAllowedType) { globalScope.ScriptHeader.SetNativeType(singleAllowedType); } return; } } ITES5Type basicScriptType = globalScope.ScriptHeader.ScriptType.NativeType; bool expected = functionBlockName == "OnHit" && TES5InheritanceGraphAnalyzer.IsTypeOrExtendsType(basicScriptType, TES5BasicType.T_ACTIVEMAGICEFFECT); throw new ConversionTypeMismatchException("Event " + functionBlockName + " is not allowed on " + globalScope.ScriptHeader.ScriptType.Value + " (" + basicScriptType.Value + ").", expected: expected); }
/* * @param memberByValue string Type to be created. * @param basicType TES5BasicType You might override the basic type for this custom type created. */ public static ITES5Type MemberByValue(string memberByValue, ITES5Type basicType = null) { if (memberByValue == null) { throw new ArgumentNullException(nameof(memberByValue)); } if (memberByValue == "void") { return(new TES5VoidType()); } TES5BasicType tes5BasicType = TES5BasicType.GetFirstOrNull(PHPFunction.UCWords(memberByValue)); if (tes5BasicType != null) { return(tes5BasicType); } //Ugly - todo: REFACTOR THIS TO NON-STATIC CLASS AND MOVE THIS TO DI if (basicType == null) { ESMAnalyzer analyzer = ESMAnalyzer._instance(); basicType = analyzer.GetScriptType(memberByValue); } return(new TES5CustomType(memberByValue, TES4Prefix, basicType)); }
public TES5ObjectCallCustom CreateObjectCallCustom(ITES5Referencer callable, string functionName, ITES5Type returnType, TES5ObjectCallArguments arguments = null) { return(new TES5ObjectCallCustom(callable, functionName, returnType, arguments)); }
public TES5LocalVariable(string nameWithSuffix, TES5BasicType type) { Name = nameWithSuffix; TES5Type = type; }
public static TES5ScriptHeader GetFromCacheOrConstructByBasicType(string scriptName, ITES5Type type, string scriptNamePrefix, bool isHidden) { TES5ScriptHeader?header = TryGetScriptHeader(scriptName); if (header != null) { return(header); } TES5BasicType?basicType = type as TES5BasicType; if (basicType != null) { type = TES5TypeFactory.MemberByValue(scriptName, basicType); } header = Construct(scriptName, type, scriptNamePrefix, isHidden); cache.Add(scriptName, header); return(header); }
private static TES5ScriptHeader Construct(string scriptName, ITES5Type type, string scriptNamePrefix, bool isHidden) { return(new TES5ScriptHeader(scriptName, type, scriptNamePrefix, isHidden)); }