Files

1093 lines
54 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const UE = require("ue");
const puerts_1 = require("puerts");
const ts = require("typescript");
const tsi = require("./TypeScriptInternal");
const uemeta = require("./UEMeta");
const cpp = require("cpp");
function getCustomSystem() {
const customSystem = {
args: [],
newLine: '\n',
useCaseSensitiveFileNames: true,
write,
readFile,
writeFile,
resolvePath: tsi.resolvePath,
fileExists,
directoryExists: tsi.directoryExists,
createDirectory: tsi.createDirectory,
getExecutingFilePath,
getCurrentDirectory,
getDirectories,
readDirectory,
exit,
};
function fileExists(path) {
let res = UE.FileSystemOperation.FileExists(path);
//console.log(`${path} exists? ${res}`);
return res;
}
function write(s) {
console.log(s);
}
function readFile(path, encoding) {
let data = puerts_1.$ref(undefined);
const res = UE.FileSystemOperation.ReadFile(path, data);
if (res) {
return puerts_1.$unref(data);
}
else {
console.warn("readFile: read file fail! path=" + path + ", stack:" + new Error().stack);
return undefined;
}
}
function writeFile(path, data, writeByteOrderMark) {
throw new Error("forbiden!");
}
/*function resolvePath(path: string): string {
throw new Error("resolvePath no supported!");
}*/
function readDirectory(path, extensions, excludes, includes, depth) {
//throw new Error("readDirectory no supported!");
return tsi.matchFiles(path, extensions, excludes, includes, true, getCurrentDirectory(), depth, tsi.getAccessibleFileSystemEntries, tsi.realpath);
}
function exit(exitCode) {
throw new Error("exit with code:" + exitCode);
}
function getExecutingFilePath() {
return getCurrentDirectory() + "Content/JavaScript/PuertsEditor/node_modules/typescript/lib/tsc.js";
}
function getCurrentDirectory() {
return UE.FileSystemOperation.GetCurrentDirectory();
}
function getDirectories(path) {
let result = [];
let dirs = UE.FileSystemOperation.GetDirectories(path);
for (var i = 0; i < dirs.Num(); i++) {
result.push(dirs.Get(i));
}
return result;
}
//for debug only
/*return new Proxy({}, {
get: function(target, name) {
if (!(name in target)) {
if (typeof name === 'string') {
if (!(name in tgamejsSystem)) {
return undefined;
}
let maybeFunc = tgamejsSystem[name];
if (typeof maybeFunc === 'function') {
target[name] = function(...args: any[]) {
const res = maybeFunc(...args);
console.log("method:", name, ", args:", JSON.stringify(args), ",res:", res);
return res;
}
} else {
target[name] = tgamejsSystem[name];
}
}
}
return target[name]
}
}) as ts.System;*/
return customSystem;
}
let customSystem = getCustomSystem();
if (!ts.sys) {
let t = ts;
t.sys = customSystem;
}
function logErrors(allDiagnostics) {
allDiagnostics.forEach(diagnostic => {
let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
if (diagnostic.file) {
let { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
console.error(` Error ${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
}
else {
console.error(` Error: ${message}`);
}
});
}
const FunctionFlags = {
FUNC_None: 0x00000000,
FUNC_Final: 0x00000001,
FUNC_RequiredAPI: 0x00000002,
FUNC_BlueprintAuthorityOnly: 0x00000004,
FUNC_BlueprintCosmetic: 0x00000008,
// FUNC_ : 0x00000010, // unused.
// FUNC_ : 0x00000020, // unused.
FUNC_Net: 0x00000040,
FUNC_NetReliable: 0x00000080,
FUNC_NetRequest: 0x00000100,
FUNC_Exec: 0x00000200,
FUNC_Native: 0x00000400,
FUNC_Event: 0x00000800,
FUNC_NetResponse: 0x00001000,
FUNC_Static: 0x00002000,
FUNC_NetMulticast: 0x00004000,
FUNC_UbergraphFunction: 0x00008000,
FUNC_MulticastDelegate: 0x00010000,
FUNC_Public: 0x00020000,
FUNC_Private: 0x00040000,
FUNC_Protected: 0x00080000,
FUNC_Delegate: 0x00100000,
FUNC_NetServer: 0x00200000,
FUNC_HasOutParms: 0x00400000,
FUNC_HasDefaults: 0x00800000,
FUNC_NetClient: 0x01000000,
FUNC_DLLImport: 0x02000000,
FUNC_BlueprintCallable: 0x04000000,
FUNC_BlueprintEvent: 0x08000000,
FUNC_BlueprintPure: 0x10000000,
FUNC_EditorOnly: 0x20000000,
FUNC_Const: 0x40000000,
FUNC_NetValidate: 0x80000000,
FUNC_AllFlags: 0xFFFFFFFF,
};
const PropertyFlags = {
CPF_None: 0,
CPF_Edit: 0x0000000000000001,
CPF_ConstParm: 0x0000000000000002,
CPF_BlueprintVisible: 0x0000000000000004,
CPF_ExportObject: 0x0000000000000008,
CPF_BlueprintReadOnly: 0x0000000000000010,
CPF_Net: 0x0000000000000020,
CPF_EditFixedSize: 0x0000000000000040,
CPF_Parm: 0x0000000000000080,
CPF_OutParm: 0x0000000000000100,
CPF_ZeroConstructor: 0x0000000000000200,
CPF_ReturnParm: 0x0000000000000400,
CPF_DisableEditOnTemplate: 0x0000000000000800,
//CPF_ : 0x0000000000001000, ///<
CPF_Transient: 0x0000000000002000,
CPF_Config: 0x0000000000004000,
//CPF_ : 0x0000000000008000, ///<
CPF_DisableEditOnInstance: 0x0000000000010000,
CPF_EditConst: 0x0000000000020000,
CPF_GlobalConfig: 0x0000000000040000,
CPF_InstancedReference: 0x0000000000080000,
//CPF_ : 0x0000000000100000, ///<
CPF_DuplicateTransient: 0x0000000000200000,
CPF_SubobjectReference: 0x0000000000400000,
//CPF_ : 0x0000000000800000, ///<
CPF_SaveGame: 0x0000000001000000,
CPF_NoClear: 0x0000000002000000,
//CPF_ : 0x0000000004000000, ///<
CPF_ReferenceParm: 0x0000000008000000,
CPF_BlueprintAssignable: 0x0000000010000000,
CPF_Deprecated: 0x0000000020000000,
CPF_IsPlainOldData: 0x0000000040000000,
CPF_RepSkip: 0x0000000080000000,
CPF_RepNotify: 0x0000000100000000,
CPF_Interp: 0x0000000200000000,
CPF_NonTransactional: 0x0000000400000000,
CPF_EditorOnly: 0x0000000800000000,
CPF_NoDestructor: 0x0000001000000000,
//CPF_ : 0x0000002000000000, ///<
CPF_AutoWeak: 0x0000004000000000,
CPF_ContainsInstancedReference: 0x0000008000000000,
CPF_AssetRegistrySearchable: 0x0000010000000000,
CPF_SimpleDisplay: 0x0000020000000000,
CPF_AdvancedDisplay: 0x0000040000000000,
CPF_Protected: 0x0000080000000000,
CPF_BlueprintCallable: 0x0000100000000000,
CPF_BlueprintAuthorityOnly: 0x0000200000000000,
CPF_TextExportTransient: 0x0000400000000000,
CPF_NonPIEDuplicateTransient: 0x0000800000000000,
CPF_ExposeOnSpawn: 0x0001000000000000,
CPF_PersistentInstance: 0x0002000000000000,
CPF_UObjectWrapper: 0x0004000000000000,
CPF_HasGetValueTypeHash: 0x0008000000000000,
CPF_NativeAccessSpecifierPublic: 0x0010000000000000,
CPF_NativeAccessSpecifierProtected: 0x0020000000000000,
CPF_NativeAccessSpecifierPrivate: 0x0040000000000000,
CPF_SkipSerialization: 0x0080000000000000,
};
const ELifetimeCondition = {
"COND_InitialOnly": 1,
"COND_OwnerOnly": 2,
"COND_SkipOwner": 3,
"COND_SimulatedOnly": 4,
"COND_AutonomousOnly": 5,
"COND_SimulatedOrPhysics": 6,
"COND_InitialOrOwner": 7,
"COND_Custom": 8,
"COND_ReplayOrOwner": 9,
"COND_ReplayOnly": 10,
"COND_SimulatedOnlyNoReplay": 11,
"COND_SimulatedOrPhysicsNoReplay": 12,
"COND_SkipReplay": 13,
"COND_Never": 15,
};
function readAndParseConfigFile(configFilePath) {
let readResult = ts.readConfigFile(configFilePath, customSystem.readFile);
return ts.parseJsonConfigFileContent(readResult.config, {
useCaseSensitiveFileNames: true,
readDirectory: customSystem.readDirectory,
fileExists: customSystem.fileExists,
readFile: customSystem.readFile,
trace: s => console.log(s)
}, customSystem.getCurrentDirectory());
}
function watch(configFilePath) {
let { fileNames, options } = readAndParseConfigFile(configFilePath);
console.log("start watch..", JSON.stringify({ fileNames: fileNames, options: options }));
const versionsFilePath = tsi.getDirectoryPath(configFilePath) + "/ts_file_versions_info.json";
const fileVersions = {};
let beginTime = new Date().getTime();
fileNames.forEach(fileName => {
fileVersions[fileName] = { version: UE.FileSystemOperation.FileMD5Hash(fileName), processed: false, isBP: false };
});
console.log("calc md5 using " + (new Date().getTime() - beginTime) + "ms");
function getDefaultLibLocation() {
return tsi.getDirectoryPath(tsi.normalizePath(customSystem.getExecutingFilePath()));
}
const scriptSnapshotsCache = new Map();
// Create the language service host to allow the LS to communicate with the host
const servicesHost = {
getScriptFileNames: () => fileNames,
getScriptVersion: fileName => {
if (fileName in fileVersions) {
return fileVersions[fileName] && fileVersions[fileName].version.toString();
}
else {
let md5 = UE.FileSystemOperation.FileMD5Hash(fileName);
fileVersions[fileName] = { version: md5, processed: false };
return md5;
}
},
getScriptSnapshot: fileName => {
if (!customSystem.fileExists(fileName)) {
console.error("getScriptSnapshot: file not existed! path=" + fileName);
return undefined;
}
if (!(fileName in fileVersions)) {
fileVersions[fileName] = { version: UE.FileSystemOperation.FileMD5Hash(fileName), processed: false };
}
if (!scriptSnapshotsCache.has(fileName)) {
const sourceFile = customSystem.readFile(fileName);
if (!sourceFile) {
console.error("getScriptSnapshot: read file failed! path=" + fileName);
return undefined;
}
scriptSnapshotsCache.set(fileName, {
version: fileVersions[fileName].version,
scriptSnapshot: ts.ScriptSnapshot.fromString(sourceFile)
});
}
let scriptSnapshotsInfo = scriptSnapshotsCache.get(fileName);
if (scriptSnapshotsInfo.version != fileVersions[fileName].version) {
const sourceFile = customSystem.readFile(fileName);
if (!sourceFile) {
console.error("getScriptSnapshot: read file failed! path=" + fileName);
return undefined;
}
scriptSnapshotsInfo.version = fileVersions[fileName].version;
scriptSnapshotsInfo.scriptSnapshot = ts.ScriptSnapshot.fromString(sourceFile);
}
//console.log("getScriptSnapshot:"+ fileName + ",in:" + new Error().stack)
return scriptSnapshotsInfo.scriptSnapshot;
},
getCurrentDirectory: customSystem.getCurrentDirectory,
getCompilationSettings: () => options,
getDefaultLibFileName: options => tsi.combinePaths(getDefaultLibLocation(), ts.getDefaultLibFileName(options)),
fileExists: ts.sys.fileExists,
readFile: ts.sys.readFile,
readDirectory: ts.sys.readDirectory,
directoryExists: ts.sys.directoryExists,
getDirectories: ts.sys.getDirectories,
};
let service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
function getProgramFromService() {
while (true) {
try {
return service.getProgram();
}
catch (e) {
console.error(e);
}
//异常了从新创建Language Service,有可能不断失败,UE的文件读取偶尔会失败,失败后ts增量编译会不断的在tryReuseStructureFromOldProgram那断言失败
service = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());
}
}
let pendingBlueprintRefleshJobs = [];
function isChildOf(a, b) {
let baseTypes = a.getBaseTypes();
if (baseTypes.indexOf(b) >= 0) {
return true;
}
for (let i = 0; i < baseTypes.length; ++i) {
if (isChildOf(baseTypes[i], b)) {
return true;
}
}
return false;
}
function topologicalSort(classes) {
const visited = new Set();
const result = [];
function visit(node) {
if (!visited.has(node)) {
visited.add(node);
for (const other of classes) {
if (isChildOf(node.type, other.type)) {
visit(other);
}
}
result.push(node);
}
}
for (const cls of classes) {
visit(cls);
}
return result;
}
function refreshBlueprints() {
if (pendingBlueprintRefleshJobs.length > 0) {
pendingBlueprintRefleshJobs = topologicalSort(pendingBlueprintRefleshJobs);
pendingBlueprintRefleshJobs.forEach(job => {
job.op();
});
pendingBlueprintRefleshJobs = [];
}
}
beginTime = new Date().getTime();
let program = getProgramFromService();
console.log("full compile using " + (new Date().getTime() - beginTime) + "ms");
let diagnostics = ts.getPreEmitDiagnostics(program);
let restoredFileVersions = {};
var changed = false;
const versionsFileExisted = customSystem.fileExists(versionsFilePath);
if (versionsFileExisted) {
try {
restoredFileVersions = JSON.parse(customSystem.readFile(versionsFilePath));
console.log("restore versions from ", versionsFilePath);
}
catch { }
}
if (diagnostics.length > 0) {
fileNames.forEach(fileName => {
fileVersions[fileName] = restoredFileVersions[fileName] || fileVersions[fileName];
});
logErrors(diagnostics);
}
else {
function getClassPathInfo(sourceFilePath) {
let modulePath = undefined;
let moduleFileName = undefined;
if (sourceFilePath.endsWith(".ts")) {
if (options.baseUrl && sourceFilePath.startsWith(options.baseUrl)) {
moduleFileName = sourceFilePath.substr(options.baseUrl.length + 1);
modulePath = tsi.getDirectoryPath(moduleFileName);
moduleFileName = tsi.removeExtension(moduleFileName, ".ts");
}
}
return { moduleFileName, modulePath };
}
fileNames.forEach(fileName => {
if (!(fileName in restoredFileVersions) || restoredFileVersions[fileName].version != fileVersions[fileName].version || !restoredFileVersions[fileName].processed) {
onSourceFileAddOrChange(fileName, false, program, true, false);
changed = true;
}
else {
fileVersions[fileName].processed = true;
}
if (fileName in restoredFileVersions) {
fileVersions[fileName].isBP = restoredFileVersions[fileName].isBP;
}
});
fileNames.forEach(fileName => {
if (versionsFileExisted && (!(fileName in restoredFileVersions)))
return;
if (versionsFileExisted && (!restoredFileVersions[fileName].isBP))
return;
const { moduleFileName, modulePath } = getClassPathInfo(fileName);
let BPExisted = false;
if (moduleFileName) {
BPExisted = UE.PEBlueprintAsset.Existed(moduleFileName, modulePath);
}
if (!versionsFileExisted || restoredFileVersions[fileName].version != fileVersions[fileName].version || !restoredFileVersions[fileName].processed || !BPExisted) {
onSourceFileAddOrChange(fileName, false, program, false);
changed = true;
}
else {
fileVersions[fileName].processed = true;
}
});
refreshBlueprints();
if (changed) {
UE.FileSystemOperation.WriteFile(versionsFilePath, JSON.stringify(fileVersions, null, 4));
}
}
var dirWatcher = new UE.PEDirectoryWatcher();
global.__dirWatcher = dirWatcher; //防止被释放?
dirWatcher.OnChanged.Add((added, modified, removed) => {
setTimeout(() => {
var changed = false;
let modifiedFiles = [];
for (var i = 0; i < modified.Num(); i++) {
modifiedFiles.push(modified.Get(i));
}
let removedSet = new Set();
for (var i = 0; i < removed.Num(); i++) {
removedSet.add(removed.Get(i));
}
let addFiles = [];
for (var i = 0; i < added.Num(); i++) {
let fileName = added.Get(i);
//remove and add is the same as modified
if (removedSet.has(fileName)) {
modifiedFiles.push(fileName);
}
else {
addFiles.push(fileName);
}
}
if (addFiles.length > 0) {
onFileAdded();
changed = true;
}
modifiedFiles.forEach(fileName => {
if (fileName in fileVersions) {
let md5 = UE.FileSystemOperation.FileMD5Hash(fileName);
if (md5 === fileVersions[fileName].version) {
console.log(fileName + " md5 not changed, so skiped!");
}
else {
console.log(`${fileName} md5 from ${fileVersions[fileName].version} to ${md5}`);
fileVersions[fileName].version = md5;
onSourceFileAddOrChange(fileName, true);
changed = true;
}
}
});
refreshBlueprints();
if (changed) {
console.log("versions saved to " + versionsFilePath);
UE.FileSystemOperation.WriteFile(versionsFilePath, JSON.stringify(fileVersions, null, 4));
}
}, 100); //延时100毫秒,防止因为读冲突而文件读取失败
});
dirWatcher.Watch(customSystem.getCurrentDirectory());
function onFileAdded() {
let cmdLine = readAndParseConfigFile(configFilePath);
let newFiles = [];
cmdLine.fileNames.forEach(fileName => {
if (!(fileName in fileVersions)) {
console.log(`new file: ${fileName} ...`);
newFiles.push(fileName);
fileVersions[fileName] = { version: UE.FileSystemOperation.FileMD5Hash(fileName), processed: false };
}
});
if (newFiles.length > 0) {
fileNames = cmdLine.fileNames;
options = cmdLine.options;
program = getProgramFromService();
newFiles.forEach(fileName => onSourceFileAddOrChange(fileName, true, program));
}
}
function onSourceFileAddOrChange(sourceFilePath, reload, program, doEmitJs = true, doEmitBP = true) {
if (!program) {
let beginTime = new Date().getTime();
program = getProgramFromService();
console.log("incremental compile " + sourceFilePath + " using " + (new Date().getTime() - beginTime) + "ms");
}
let sourceFile = program.getSourceFile(sourceFilePath);
if (sourceFile) {
const diagnostics = [
...program.getSyntacticDiagnostics(sourceFile),
...program.getSemanticDiagnostics(sourceFile)
];
let checker = program.getTypeChecker();
if (diagnostics.length > 0) {
logErrors(diagnostics);
}
else {
if (doEmitBP) {
fileVersions[sourceFilePath].isBP = false;
}
fileVersions[sourceFilePath].processed = true;
if (!sourceFile.isDeclarationFile) {
let emitOutput = service.getEmitOutput(sourceFilePath);
if (!emitOutput.emitSkipped) {
let modulePath = undefined;
let moduleFileName = undefined;
let jsSource = undefined;
emitOutput.outputFiles.forEach(output => {
if (doEmitJs) {
console.log(`write ${output.name} ...`);
UE.FileSystemOperation.WriteFile(output.name, output.text);
}
if (output.name.endsWith(".js") || output.name.endsWith(".mjs")) {
jsSource = output.text;
if (options.outDir && output.name.startsWith(options.outDir)) {
moduleFileName = output.name.substr(options.outDir.length + 1);
modulePath = tsi.getDirectoryPath(moduleFileName);
moduleFileName = tsi.removeExtension(moduleFileName, output.name.endsWith(".js") ? ".js" : ".mjs");
}
}
});
if (moduleFileName && reload) {
UE.FileSystemOperation.PuertsNotifyChange(moduleFileName, jsSource);
}
if (!doEmitBP)
return;
let foundType = undefined;
let foundBaseTypeUClass = undefined;
ts.forEachChild(sourceFile, (node) => {
if (ts.isExportAssignment(node) && ts.isIdentifier(node.expression)) {
const type = checker.getTypeAtLocation(node.expression);
if (!type || !type.getSymbol())
return;
if (type.getSymbol().getName() != tsi.getBaseFileName(moduleFileName)) {
//console.error("type name must the same as file name!");
return;
}
let baseTypes = type.getBaseTypes();
if (!baseTypes || baseTypes.length != 1)
return;
let structOfType = getUClassOfType(baseTypes[0]);
let baseTypeUClass = undefined;
if (!structOfType) {
return;
}
if (structOfType.GetClass().IsChildOf(UE.Class.StaticClass())) {
baseTypeUClass = structOfType;
}
else {
console.warn("do not support UStruct:" + checker.typeToString(type));
return;
}
if (baseTypeUClass) {
if (isSubclassOf(type, "Subsystem")) {
console.error("do not support Subsystem " + checker.typeToString(type));
return;
}
if (!baseTypeUClass.IsNative()) {
let moduleNames = getModuleNames(baseTypes[0]);
if (moduleNames.length > 1 && moduleNames[0] == 'ue') {
console.error(`${checker.typeToString(type)} extends a blueprint`);
return;
}
}
foundType = type;
foundBaseTypeUClass = baseTypeUClass;
}
else {
console.warn("can not find base for " + checker.typeToString(type));
}
}
});
if (foundType && foundBaseTypeUClass) {
fileVersions[sourceFilePath].isBP = true;
//onBlueprintTypeAddOrChange(foundBaseTypeUClass, foundType, modulePath);
pendingBlueprintRefleshJobs.push({ type: foundType, op: () => onBlueprintTypeAddOrChange(foundBaseTypeUClass, foundType, modulePath) });
}
}
}
}
function typeNameToString(node) {
if (ts.isIdentifier(node)) {
return node.text;
}
else {
return node.right.text;
}
}
function isSubclassOf(type, baseTypeName) {
let baseTypes = type.getBaseTypes();
if (baseTypes.length != 1)
return false;
if (baseTypes[0].getSymbol().getName() == baseTypeName) {
return true;
}
return isSubclassOf(baseTypes[0], baseTypeName);
}
function getUClassOfType(type) {
if (!type)
return undefined;
let moduleNames = getModuleNames(type);
if (moduleNames.length > 0 && moduleNames[0] == 'ue') {
if (moduleNames.length == 1) {
try {
let jsCls = UE[type.symbol.getName()];
if (typeof jsCls.StaticClass == 'function') {
return jsCls.StaticClass();
}
}
catch (e) {
console.error(`load ue type [${type.symbol.getName()}], throw: ${e}`);
}
}
else if (moduleNames.length == 2) {
let classPath = '/' + moduleNames[1] + '.' + type.symbol.getName();
return UE.Field.Load(classPath, true);
}
}
else if (type.symbol && type.symbol.valueDeclaration) {
//eturn undefined;
let baseTypes = type.getBaseTypes();
if (!baseTypes || baseTypes.length != 1)
return undefined;
let baseTypeUClass = getUClassOfType(baseTypes[0]);
if (!baseTypeUClass)
return undefined;
//console.error("modulePath:", getModulePath(type.symbol.valueDeclaration.getSourceFile().fileName));
let sourceFile = type.symbol.valueDeclaration.getSourceFile();
let sourceFileName;
program.emit(sourceFile, writeFile, undefined, false, undefined);
function writeFile(fileName, text, writeByteOrderMark) {
if (fileName.endsWith('.js')) {
sourceFileName = tsi.removeExtension(fileName, '.js');
}
}
if (tsi.getBaseFileName(sourceFileName) != type.symbol.getName()) {
console.error("type name must the same as file name!");
return undefined;
}
if (options.outDir && sourceFileName.startsWith(options.outDir)) {
let moduleFileName = sourceFileName.substr(options.outDir.length + 1);
let modulePath = tsi.getDirectoryPath(moduleFileName);
let bp = new UE.PEBlueprintAsset();
bp.LoadOrCreate(type.getSymbol().getName(), modulePath, baseTypeUClass, 0, 0);
bp.Save();
return bp.GeneratedClass;
}
}
}
function getSymbolTypeNode(symbol) {
if (symbol.valueDeclaration) {
for (var i = symbol.valueDeclaration.getChildCount() - 1; i >= 0; i--) {
var child = symbol.valueDeclaration.getChildAt(i);
if (child.kind == ts.SyntaxKind.TypeReference) {
return child;
}
}
}
}
function tsTypeToPinType(type, node) {
if (!type)
return undefined;
try {
let typeNode = checker.typeToTypeNode(type, undefined, undefined);
//console.log(checker.typeToString(type), tds)
if (ts.isTypeReferenceNode(typeNode) && type.symbol) {
let typeName = type.symbol.getName();
if (typeName == 'BigInt') {
let category = "int64";
let pinType = new UE.PEGraphPinType(category, undefined, UE.EPinContainerType.None, false, false);
return { pinType: pinType };
}
if (!typeNode.typeArguments || typeNode.typeArguments.length == 0) {
let category = "object";
let uclass = getUClassOfType(type);
if (!uclass) {
let uenum = UE.Enum.Find(type.symbol.getName());
if (uenum) {
return { pinType: new UE.PEGraphPinType("byte", uenum, UE.EPinContainerType.None, false, false) };
}
console.warn("can not find type of " + typeName);
return undefined;
}
let pinType = new UE.PEGraphPinType(category, uclass, UE.EPinContainerType.None, false, false);
return { pinType: pinType };
}
else { //TArray, TSet, TMap
let typeRef = type;
var children = [];
let typeArguments = typeRef.typeArguments || typeRef.aliasTypeArguments;
if (typeRef.aliasTypeArguments && typeRef.aliasSymbol) {
typeName = typeRef.aliasSymbol.getName();
}
if (!typeArguments) {
console.warn("can not find type arguments of " + node.getFullText());
return undefined;
}
if (node) {
node.forEachChild(child => {
children.push(child);
});
}
let result = tsTypeToPinType(typeArguments[0], children[1]);
if (!result || result.pinType.PinContainerType != UE.EPinContainerType.None && typeName != '$Ref' && typeName != '$InRef') {
console.warn("can not find pin type of typeArguments[0] " + typeName);
return undefined;
}
if (children[1]) {
postProcessPinType(children[1], result.pinType, false);
}
if (typeName == 'TArray' || typeName == 'TSet') {
result.pinType.PinContainerType = typeName == 'TArray' ? UE.EPinContainerType.Array : UE.EPinContainerType.Set;
return result;
}
else if (typeName == 'TSubclassOf') {
let category = "class";
result.pinType.PinCategory = category;
return result;
}
else if (typeName == 'TSoftObjectPtr') {
let category = "softobject";
result.pinType.PinCategory = category;
return result;
}
else if (typeName == 'TSoftClassPtr') {
let category = "softclass";
result.pinType.PinCategory = category;
return result;
}
else if (typeName == '$Ref') {
result.pinType.bIsReference = true;
return result;
}
else if (typeName == '$InRef') {
result.pinType.bIsReference = true;
result.pinType.bIn = true;
return result;
}
else if (typeName == 'TMap') {
let valuePinType = tsTypeToPinType(typeArguments[1], undefined);
if (!valuePinType || valuePinType.pinType.PinContainerType != UE.EPinContainerType.None) {
console.warn("can not find pin type of typeArguments[1] " + typeName);
return undefined;
}
if (children[2]) {
postProcessPinType(children[2], valuePinType.pinType, false);
}
result.pinType.PinContainerType = UE.EPinContainerType.Map;
result.pinValueType = new UE.PEGraphTerminalType(valuePinType.pinType.PinCategory, valuePinType.pinType.PinSubCategoryObject);
return result;
}
else {
console.warn("not support generic type: " + typeName);
return undefined;
}
}
}
else {
//"bool" | "class" | "int64" | "string" | "object" | "struct" | "float";
let category;
switch (typeNode.kind) {
case ts.SyntaxKind.NumberKeyword:
category = "float";
break;
case ts.SyntaxKind.StringKeyword:
category = 'string';
break;
case ts.SyntaxKind.BigIntKeyword:
category = 'int64';
break;
case ts.SyntaxKind.BooleanKeyword:
category = 'bool';
break;
default:
console.warn("not support kind: " + typeNode.kind);
return undefined;
}
let pinType = new UE.PEGraphPinType(category, undefined, UE.EPinContainerType.None, false, false);
return { pinType: pinType };
}
}
catch (e) {
console.error(e.stack || e);
return undefined;
}
}
function manualSkip(valueDeclaration) {
const commentRanges = ts.getLeadingCommentRanges(sourceFile.getFullText(), valueDeclaration.getFullStart());
return !!(commentRanges && commentRanges.find(r => sourceFile.getFullText().slice(r.pos, r.end).indexOf("@no-blueprint") > 0)) || hasDecorator(valueDeclaration, "no_blueprint");
}
function tryGetAnnotation(valueDeclaration, key, leading) {
const commentRanges = (leading ? ts.getLeadingCommentRanges : ts.getTrailingCommentRanges)(sourceFile.getFullText(), valueDeclaration.getFullStart() + (leading ? 0 : valueDeclaration.getFullWidth()));
if (commentRanges) {
let ret;
commentRanges.forEach(r => {
let m = sourceFile.getFullText().slice(r.pos, r.end).match(new RegExp(`@${key}:([^*]*)`));
if (m) {
ret = m[1].trim();
}
});
return ret;
}
}
function postProcessPinType(valueDeclaration, pinType, leading) {
if (pinType.PinContainerType == UE.EPinContainerType.None) {
let pc = pinType.PinCategory;
if (pc === "float") {
let cppType = tryGetAnnotation(valueDeclaration, "cpp", leading);
if (cppType === "int" || cppType === "byte") {
pinType.PinCategory = cppType;
}
}
else if (pc === "string") {
let cppType = tryGetAnnotation(valueDeclaration, "cpp", leading);
if (cppType === "name" || cppType === "text") {
pinType.PinCategory = cppType;
}
}
}
}
function getFlagsValue(str, flagsDef) {
if (!str)
return 0;
return str.split("|").map(x => x.trim()).map(x => x in flagsDef ? flagsDef[x] : 0).reduce((x, y) => x | y);
}
function getDecoratorFlagsValue(valueDeclaration, posfix, flagsDef) {
if (valueDeclaration && valueDeclaration.decorators) {
let decorators = valueDeclaration.decorators;
let ret = 0n;
decorators.forEach((decorator, index) => {
let expression = decorator.expression;
if (ts.isCallExpression(expression)) {
if (expression.expression.getFullText() == posfix || expression.expression.getFullText().endsWith('.' + posfix)) {
expression.arguments.forEach((value, index) => {
let e = value.getFullText().split("|").map(x => x.trim().replace(/^.*[\.]/, ''))
.map(x => x in flagsDef ? BigInt(flagsDef[x]) : 0n)
.reduce((x, y) => BigInt(x) | BigInt(y));
ret = ret | e;
});
}
}
});
return ret;
}
else {
return 0n;
}
}
function hasDecorator(valueDeclaration, posfix) {
let ret = false;
if (valueDeclaration && valueDeclaration.decorators) {
let decorators = valueDeclaration.decorators;
decorators.forEach((decorator, index) => {
let expression = decorator.expression;
if (ts.isCallExpression(expression)) {
if (expression.expression.getFullText() == posfix || expression.expression.getFullText().endsWith('.' + posfix)) {
ret = true;
}
}
});
}
return ret;
}
function onBlueprintTypeAddOrChange(baseTypeUClass, type, modulePath) {
console.log(`gen blueprint for ${type.getSymbol().getName()}, path: ${modulePath}`);
let lsFunctionLibrary = baseTypeUClass && baseTypeUClass.GetName() === "BlueprintFunctionLibrary";
let bp = new UE.PEBlueprintAsset();
bp.LoadOrCreateWithMetaData(type.getSymbol().getName(), modulePath, baseTypeUClass, 0, 0, uemeta.compileClassMetaData(type));
let hasConstructor = false;
let properties = [];
type.symbol.valueDeclaration.forEachChild(x => {
if (ts.isMethodDeclaration(x) && !manualSkip(x)) {
let isStatic = !!(ts.getCombinedModifierFlags(x) & ts.ModifierFlags.Static);
if (isStatic && !lsFunctionLibrary) {
console.warn(`do not support static function [${x.name.getText()}]`);
return;
}
if (!isStatic && lsFunctionLibrary) {
console.warn(`do not support non-static function [${x.name.getText()}] in BlueprintFunctionLibrary`);
return;
}
if (x.name.getText() === 'ReceiveInit') {
if (baseTypeUClass == UE.GameInstance.StaticClass() || baseTypeUClass.IsChildOf(UE.GameInstance.StaticClass())) {
console.warn(`do not support override GameInstance.ReceiveInit in ${type.getSymbol().getName()}`);
return;
}
}
properties.push(checker.getSymbolAtLocation(x.name));
}
else if (ts.isPropertyDeclaration(x) && !manualSkip(x)) {
let isStatic = !!(ts.getCombinedModifierFlags(x) & ts.ModifierFlags.Static);
if (isStatic) {
console.warn("static property:" + x.name.getText() + ' not support');
return;
}
properties.push(checker.getSymbolAtLocation(x.name));
}
});
let attachments = UE.NewMap(UE.BuiltinName, UE.BuiltinName);
properties
.filter(x => ts.isClassDeclaration(x.valueDeclaration.parent) && checker.getSymbolAtLocation(x.valueDeclaration.parent.name) == type.symbol)
.forEach((symbol) => {
if (ts.isMethodDeclaration(symbol.valueDeclaration)) {
if (symbol.getName() === 'Constructor') {
hasConstructor = true;
return;
}
let methodType = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration);
let signatures = checker.getSignaturesOfType(methodType, ts.SignatureKind.Call);
if (!signatures) {
console.warn(`can not find signature for ${symbol.getName()} `);
return;
}
if (signatures.length != 1) {
console.warn(`find more than one signature for ${symbol.getName()} `);
return;
}
let signature = signatures[0];
for (var i = 0; i < signature.parameters.length; i++) {
let paramType = checker.getTypeOfSymbolAtLocation(signature.parameters[i], signature.parameters[i].valueDeclaration);
let paramPinType = tsTypeToPinType(paramType, getSymbolTypeNode(signature.parameters[i]));
if (!paramPinType) {
console.warn(symbol.getName() + " of " + checker.typeToString(type) + " has not supported parameter!");
bp.ClearParameter();
return;
}
postProcessPinType(signature.parameters[i].valueDeclaration, paramPinType.pinType, false);
// bp.AddParameter(signature.parameters[i].getName(), paramPinType.pinType, paramPinType.pinValueType);
bp.AddParameterWithMetaData(signature.parameters[i].getName(), paramPinType.pinType, paramPinType.pinValueType, uemeta.compileParamMetaData(signature.parameters[i]));
}
//console.log("add function", symbol.getName());
let sflags = tryGetAnnotation(symbol.valueDeclaration, "flags", true);
let flags = getFlagsValue(sflags, FunctionFlags);
let clearFlags = 0;
if (symbol.valueDeclaration && symbol.valueDeclaration.decorators) {
flags |= Number(getDecoratorFlagsValue(symbol.valueDeclaration, "flags", FunctionFlags));
flags |= Number(getDecoratorFlagsValue(symbol.valueDeclaration, "set_flags", FunctionFlags));
clearFlags = Number(getDecoratorFlagsValue(symbol.valueDeclaration, "clear_flags", FunctionFlags));
}
if (symbol.valueDeclaration.type && (ts.SyntaxKind.VoidKeyword === symbol.valueDeclaration.type.kind)) {
// bp.AddFunction(symbol.getName(), true, undefined, undefined, flags, clearFlags);
bp.AddFunctionWithMetaData(symbol.getName(), true, undefined, undefined, flags, clearFlags, uemeta.compileFunctionMetaData(symbol));
}
else {
let returnType = signature.getReturnType();
let resultPinType = tsTypeToPinType(returnType, getSymbolTypeNode(symbol));
if (!resultPinType) {
console.warn(symbol.getName() + " of " + checker.typeToString(type) + " has not supported return type!");
bp.ClearParameter();
return;
}
postProcessPinType(symbol.valueDeclaration, resultPinType.pinType, true);
// bp.AddFunction(symbol.getName(), false, resultPinType.pinType, resultPinType.pinValueType, flags, clearFlags);
bp.AddFunctionWithMetaData(symbol.getName(), false, resultPinType.pinType, resultPinType.pinValueType, flags, clearFlags, uemeta.compileFunctionMetaData(symbol));
}
bp.ClearParameter();
}
else {
let propType = checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration);
let propPinType = tsTypeToPinType(propType, getSymbolTypeNode(symbol));
if (!propPinType) {
console.warn(symbol.getName() + " of " + checker.typeToString(type) + " not support!");
}
else {
postProcessPinType(symbol.valueDeclaration, propPinType.pinType, true);
//console.log("add member variable", symbol.getName());
let sflags = tryGetAnnotation(symbol.valueDeclaration, "flags", true);
let flags = BigInt(getFlagsValue(sflags, PropertyFlags));
let cond = 0;
if (symbol.valueDeclaration && symbol.valueDeclaration.decorators) {
cond = Number(getDecoratorFlagsValue(symbol.valueDeclaration, "condition", ELifetimeCondition));
if (cond != 0) {
flags = flags | BigInt(PropertyFlags.CPF_Net);
}
flags = flags | getDecoratorFlagsValue(symbol.valueDeclaration, "flags", PropertyFlags);
symbol.valueDeclaration.decorators.forEach((decorator) => {
let expression = decorator.expression;
if (ts.isCallExpression(expression)) {
if (expression.expression.getFullText().endsWith("uproperty.attach")) {
expression.arguments.forEach((value) => {
attachments.Add(symbol.getName(), value.getFullText().slice(1, -1));
});
}
}
});
}
// Invalid code, only recognizes UPROPERTY UMETA, causing the DisableEditOnInstance flag to be added every time.
// if (!hasDecorator(symbol.valueDeclaration, "edit_on_instance")) {
// flags = flags | BigInt(PropertyFlags.CPF_DisableEditOnInstance);
// }
// bp.AddMemberVariable(symbol.getName(), propPinType.pinType, propPinType.pinValueType, Number(flags & 0xffffffffn), Number(flags >> 32n), cond);
bp.AddMemberVariableWithMetaData(symbol.getName(), propPinType.pinType, propPinType.pinValueType, Number(flags & 0xffffffffn), Number(flags >> 32n), cond, uemeta.compilePropertyMetaData(symbol));
}
}
});
bp.RemoveNotExistedComponent();
bp.RemoveNotExistedMemberVariable();
bp.RemoveNotExistedFunction();
bp.SetupAttachments(attachments);
bp.HasConstructor = hasConstructor;
bp.Save();
}
function getModuleNames(type) {
let ret = [];
if (type.symbol && type.symbol.valueDeclaration && type.symbol.valueDeclaration.parent && ts.isModuleBlock(type.symbol.valueDeclaration.parent)) {
let moduleBody = type.symbol.valueDeclaration.parent;
while (moduleBody) {
let moduleDeclaration = moduleBody.parent;
let nameOfModule = undefined;
while (moduleDeclaration) {
let ns = moduleDeclaration.name.text;
ns = ns.startsWith("$") ? ns.substring(1) : ns;
nameOfModule = nameOfModule ? (ns + '/' + nameOfModule) : ns;
if (ts.isModuleDeclaration(moduleDeclaration.parent)) {
moduleDeclaration = moduleDeclaration.parent;
}
else {
break;
}
}
ret.push(nameOfModule);
if (moduleDeclaration && ts.isModuleBlock(moduleDeclaration.parent)) {
moduleBody = moduleDeclaration.parent;
}
else {
break;
}
}
}
return ret.reverse();
}
}
}
//function getOwnEmitOutputFilePath(fileName: string) {
function getModulePath(fileName) {
const compilerOptions = options;
let emitOutputFilePathWithoutExtension;
if (compilerOptions.outDir) {
emitOutputFilePathWithoutExtension = tsi.removeFileExtension(tsi.getSourceFilePathInNewDir(fileName, customSystem.getCurrentDirectory(), program.getCommonSourceDirectory(), options.outDir));
}
else {
emitOutputFilePathWithoutExtension = tsi.removeFileExtension(fileName);
}
return emitOutputFilePathWithoutExtension;
}
function list(pattern) {
var re = new RegExp(pattern ? pattern : '.*');
console.log(`id\t\t\t\t\t\t\t\t\tprocessed\tisBP\tpath`);
for (var key in fileVersions) {
var value = fileVersions[key];
if (!pattern || re.test(key)) {
console.log(`${value.version}\t${!!value.processed}\t\t${!!value.isBP}\t${key}`);
}
}
}
function compile(id) {
for (var key in fileVersions) {
var value = fileVersions[key];
if (value.version === id.trim()) {
console.log(`compiling ${key} ...`);
onSourceFileAddOrChange(key, true);
}
}
refreshBlueprints();
}
function dispatchCmd(cmd, args) {
if (cmd == 'ls') {
list(args);
}
else if (cmd == 'compile') {
compile(args);
}
else {
console.error(`unknow command for Puerts ${cmd}`);
}
}
cpp.FPuertsEditorModule.SetCmdCallback(dispatchCmd);
}
watch(customSystem.getCurrentDirectory() + "tsconfig.json");
//# sourceMappingURL=CodeAnalyze.js.map