From d888d9ab4e68bfc1f3b79bee2f85ca135f53f5ff Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Tue, 2 Jun 2020 18:01:56 +0800 Subject: [PATCH] feat: add node-shim & move plugin interface Signed-off-by: MiaoWoo --- packages/api/src/interfaces/plugin.ts | 3 +- packages/common/src/reflect.ts | 174 ++++++++++++++------------ packages/core/src/index.ts | 1 + packages/ployfill/src/index.ts | 14 +-- packages/ployfill/src/node-shim.ts | 38 +++++- packages/plugin/src/interfaces.ts | 4 +- packages/plugin/src/manager.ts | 64 +++++----- 7 files changed, 172 insertions(+), 126 deletions(-) diff --git a/packages/api/src/interfaces/plugin.ts b/packages/api/src/interfaces/plugin.ts index a44c5307..bd5bebfe 100644 --- a/packages/api/src/interfaces/plugin.ts +++ b/packages/api/src/interfaces/plugin.ts @@ -26,7 +26,8 @@ export namespace plugin { enable(...args: any[]): void; disable(...args: any[]): void; reload(...args: any[]): void; - getPlugins(): Map; + getPlugin(name: string): plugin.Plugin; + getPlugins(): Map; } export interface Plugin { description: PluginMetadata; diff --git a/packages/common/src/reflect.ts b/packages/common/src/reflect.ts index bbadccd7..4b201a6b 100644 --- a/packages/common/src/reflect.ts +++ b/packages/common/src/reflect.ts @@ -1,196 +1,204 @@ +/// + /** * 反射工具类 * Created by MiaoWoo on 2017/2/9 0009. */ -const JavaClass = Java.type('java.lang.Class'); +const JavaClass = Java.type('java.lang.Class') const JavaObject = Java.type('java.lang.Object') -const NoSuchFieldException = Java.type('java.lang.NoSuchFieldException'); -const methodCache = []; +const NoSuchFieldException = Java.type('java.lang.NoSuchFieldException') +const methodCache = [] class Reflect { - private obj: any; - private class: any + private obj: java.lang.Object + private class: java.lang.Class constructor(obj: any) { // if (obj === undefined || obj === null) { throw Error(`reflect object can't be ${obj}!`) } if (obj instanceof JavaClass) { - this.obj = null; - this.class = obj; + this.obj = null + this.class = obj } else { - this.obj = obj; + this.obj = obj if (obj !== null && obj !== undefined && obj.class) { - this.class = obj.class; + this.class = obj.class } } } - method(...args: any[]) { - return declaredMethod(this.class, args[0], types(args.slice(1))); + method(name: string, ...args: any[]) { + return declaredMethod(this.class, name, args) } methods() { - return Java.from(declaredMethods(this.class)); + return Java.from(declaredMethods(this.class)) } - field(name): Reflect { + field(name: string | java.lang.String): Reflect { try { // Try getting a public field - let field = this.class.field(name); - return on(field.get(this.obj)); + let field = this.class.getField(name) + return on(field.get(this.obj)) } catch (ex) { // Try again, getting a non-public field - return on(accessible(declaredField(this.class, name)).get(this.obj)); + return on(accessible(declaredField(this.class, name)).get(this.obj)) } - }; + } - fields(declared = false) { - return Java.from(declared ? this.class.declaredFields : this.class.fields); + fields(declared = false): java.lang.reflect.Field[] { + return Java.from(declared ? this.class.getDeclaredFields() : this.class.getFields()) } values(declared = false) { - let cache = {}; - this.fields(declared).forEach(fed => cache[fed.name] = this.field(fed.name).get()) - return cache; + return this.fields(declared).reduce((cache, field) => { return cache[field.getName()] = this.field(field.getName()).get() }, {}) as any } - call(...args): Reflect { - let params = args.slice(1); - let method = accessible(declaredMethod(this.class, args[0], types(params))); - let result = method.invoke(this.get(), params); - return result && on(result); - }; + call(...args: any[]): Reflect { + let params = args.slice(1) + let method = accessible(declaredMethod(this.class, args[0], ...types(params))) + let result = method.invoke(this.get(), params) + return result && on(result) + } - get(...args): Reflect | any { - return args.length === 1 ? this.field(args[0]) : this.obj; - }; + get(): any + get(index: number, declared?: boolean): Reflect + get(prop: string): Reflect + get(param?: string | number, declared: boolean = true): Reflect | any { + if (param == undefined || param == null) return this.obj + if (typeof param == "number") { + return on(accessible(this.fields(declared)[param]).get(this.obj)) + } + if (typeof param == "string") { + return this.field(param) + } + } - // noinspection JSUnusedGlobalSymbols - set(name, value): Reflect { - accessible(declaredField(this.class, name)).set(this.obj, value); - return this; - }; + set(name: any, value: any): Reflect { + accessible(declaredField(this.class, name)).set(this.obj, value) + return this + } create(...args): Reflect { - return on(declaredConstructor(this.class, args).newInstance(args)); - }; + return on(declaredConstructor(this.class, args).newInstance(args)) + } } /** * Get an array of types for an array of objects */ -function types(values, def?) { +function types(values: any[], def?: any) { if (values === null) { - return []; + return [] } - let result: any[] = []; - values.forEach(t => result.push((t || def) ? JavaObject.class : t instanceof JavaClass ? t : t.class)); - return result; + let result: java.lang.Class[] = [] + values.forEach(t => result.push((t || def) ? JavaObject.class : t instanceof JavaClass ? t : t.class)) + return result } -function accessible(accessible) { +function accessible(accessible: T): T { if (accessible === null) { - return null; + return null } if (!accessible.isAccessible()) { - accessible.setAccessible(true); + accessible.setAccessible(true) } - return accessible; + return accessible } function declaredConstructor(clazz, param) { - let constructor; + let constructor try { - constructor = clazz.getDeclaredConstructor(types(param)); + constructor = clazz.getDeclaredConstructor(types(param)) } catch (ex) { try { - constructor = clazz.getDeclaredConstructor(types(param, true)); + constructor = clazz.getDeclaredConstructor(types(param, true)) } catch (ex) { - constructor = clazz.getDeclaredConstructors()[0]; + constructor = clazz.getDeclaredConstructors()[0] } } - return accessible(constructor); + return accessible(constructor) } -function declaredField(clazz, name) { +function declaredField(clazz: java.lang.Class, name: string | java.lang.String) { if (!clazz) { throw Error(`target class can't be ${clazz}!`) } - let target = clazz; - let field = null; + let target = clazz + let field = null // noinspection JSUnresolvedVariable while (target !== JavaObject.class) { try { - field = target.getDeclaredField(name); - if (field !== null) { break; } + field = target.getDeclaredField(name) + if (field !== null) { break } } catch (e) { - if (target === undefined) { break; } - target = target.getSuperclass(); + if (target === undefined) { break } + target = target.getSuperclass() } } if (field === null) { - throw new NoSuchFieldException(name + " is not found in " + clazz.name); + throw new NoSuchFieldException(name + " is not found in " + clazz.getName()) } - return field; + return field } -function declaredMethod(clazz, name, clazzs) { - let key = clazz.name + '.' + name + ':' + (clazzs || []).join(':'); +function declaredMethod(clazz: java.lang.Class, name: string, ...clazzs: any[]): java.lang.reflect.Method { + let key = clazz.getName() + '.' + name + ':' + (clazzs || []).join(':') if (!methodCache[key]) { try { - methodCache[key] = clazz.getMethod(name, clazzs); + methodCache[key] = clazz.getMethod(name, clazzs as any) } catch (ex) { try { - methodCache[key] = clazz.getDeclaredMethod(name, clazzs); + methodCache[key] = clazz.getDeclaredMethod(name, clazzs as any) } catch (ex) { for (const m of Java.from(declaredMethods(clazz))) { if (m.name == name) { - methodCache[key] = m; - break; + methodCache[key] = m + break } } } } } - return methodCache[key]; + return methodCache[key] } function declaredMethods(clazz) { - return clazz.declaredMethods; + return clazz.declaredMethods } -let classMethodsCache: any[] = []; +let classMethodsCache: any[] = [] function mapToObject(javaObj) { if (!javaObj || !javaObj.class) { throw new TypeError(`参数 ${javaObj} 不是一个Java对象!`) } - let target = {}; - getJavaObjectMethods(javaObj).forEach(t => mapMethod(target, javaObj, t)); - return target; + let target = {} + getJavaObjectMethods(javaObj).forEach(t => mapMethod(target, javaObj, t)) + return target } function getJavaObjectMethods(javaObj) { - let className = javaObj.class.name; + let className = javaObj.class.name if (!classMethodsCache[className]) { - let names: any[] = []; - let methods = javaObj.class.methods; + let names: any[] = [] + let methods = javaObj.class.methods for (let i in methods) { - names.push(methods[i].name); + names.push(methods[i].name) } - classMethodsCache[className] = names; + classMethodsCache[className] = names } - return classMethodsCache[className]; + return classMethodsCache[className] } function mapMethod(target, source, name) { target[name] = function __SimpleDynamicMethod__(...args) { if (args.length > 0) { - return source[name](args); + return source[name](args) } else { - return source[name](); + return source[name]() } - }; + } } function on(obj) { // if (!obj || !obj.class) { throw new TypeError(`参数 ${obj} 不是一个Java对象!`) } - return new Reflect(obj); + return new Reflect(obj) } export = { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 8732b594..6a3fc1a6 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -54,6 +54,7 @@ class MiaoScriptCore { console.i18n("ms.core.engine.disable") this.pluginManager.disable(this.pluginManager.getPlugins()) this.taskManager.disable() + process.exit(0) //@ts-ignore require.disable() } diff --git a/packages/ployfill/src/index.ts b/packages/ployfill/src/index.ts index 54d61c5d..d258a54f 100644 --- a/packages/ployfill/src/index.ts +++ b/packages/ployfill/src/index.ts @@ -1,13 +1,13 @@ /// import i18n from '@ccms/i18n' -let ployfillStartTime = new Date().getTime(); -i18n.initialize(); -console.i18n("ms.ployfill.initialize"); -require('./es5-ext'); -require('core-js'); -require('./node-shim'); +let ployfillStartTime = new Date().getTime() +i18n.initialize() +console.i18n("ms.ployfill.initialize") +require('./es5-ext') +require('./node-shim') +require('core-js') global.setGlobal('Proxy', require('./proxy').Proxy) global.setGlobal('XMLHttpRequest', require('./xml-http-request').XMLHttpRequest) global.setGlobal('Blob', require('blob-polyfill').Blob) -console.i18n("ms.ployfill.completed", { time: (new Date().getTime() - ployfillStartTime) / 1000 }); +console.i18n("ms.ployfill.completed", { time: (new Date().getTime() - ployfillStartTime) / 1000 }) diff --git a/packages/ployfill/src/node-shim.ts b/packages/ployfill/src/node-shim.ts index 78d2ffdc..7ce0b14f 100644 --- a/packages/ployfill/src/node-shim.ts +++ b/packages/ployfill/src/node-shim.ts @@ -1,8 +1,36 @@ -global.setGlobal('process', { - env: { +import { EventEmitter } from 'events' + +const Thread = Java.type('java.lang.Thread') +const ThreadGroup = Java.type("java.lang.ThreadGroup") +const AtomicInteger = Java.type("java.util.concurrent.atomic.AtomicInteger") +const ThreadPoolExecutor = Java.type('java.util.concurrent.ThreadPoolExecutor') +const LinkedBlockingQueue = Java.type("java.util.concurrent.LinkedBlockingQueue") + +const threadCount = new AtomicInteger(0) +const threadGroup = new ThreadGroup("@ccms/ployfill-micro-task") +const microTaskPool = new ThreadPoolExecutor( + 10, 100, 60, Packages.java.util.concurrent.TimeUnit.SECONDS, + new LinkedBlockingQueue(500), + (run: any) => new Thread(threadGroup, run, "@ccms/micro-task-" + threadCount.incrementAndGet()), + new ThreadPoolExecutor.CallerRunsPolicy() +) + +class Process extends EventEmitter { + env = { __noSuchProperty__: (prop) => { return Packages.java.lang.System.getenv(prop) } - }, - platform: Packages.java.lang.System.getProperty("os.name") -}, {}) + } + platform = Packages.java.lang.System.getProperty("os.name") + nextTick(func: Function) { + microTaskPool.execute(func) + } + queueMicrotask(func: Function) { + microTaskPool.execute(func) + } + exit() { + microTaskPool.shutdown(); + } +} +global.setGlobal('process', new Process(), {}) +global.setGlobal('queueMicrotask', (func: any) => microTaskPool.execute(func), {}) diff --git a/packages/plugin/src/interfaces.ts b/packages/plugin/src/interfaces.ts index d02dfc20..02e2ff3f 100644 --- a/packages/plugin/src/interfaces.ts +++ b/packages/plugin/src/interfaces.ts @@ -1,10 +1,10 @@ -import { server, MiaoScriptConsole, event } from "@ccms/api"; +import { server, MiaoScriptConsole, event, plugin } from "@ccms/api"; import { injectable, inject, postConstruct } from "@ccms/container"; import { getPluginMetadata } from "./utils"; export namespace interfaces { @injectable() - export abstract class Plugin { + export abstract class Plugin implements plugin.Plugin { public description: PluginMetadata; public logger: Console; @inject(server.Console) diff --git a/packages/plugin/src/manager.ts b/packages/plugin/src/manager.ts index 2d00d5e3..921d25be 100644 --- a/packages/plugin/src/manager.ts +++ b/packages/plugin/src/manager.ts @@ -26,8 +26,8 @@ export class PluginManagerImpl implements plugin.PluginManager { private initialized: boolean = false private pluginRequireMap: Map - private pluginInstanceMap: Map - private pluginMetadataMap: Map + private pluginInstanceMap: Map + private pluginMetadataMap: Map initialize() { if (this.pluginInstance === undefined) { throw new Error("Can't found Plugin Instance!") } @@ -52,23 +52,27 @@ export class PluginManagerImpl implements plugin.PluginManager { this.buildPlugins() } - private logStage(plugin: interfaces.Plugin, stage: string) { + private logStage(plugin: plugin.Plugin, stage: string) { console.i18n("ms.plugin.manager.stage", { stage, plugin: plugin.description.name, version: plugin.description.version, author: plugin.description.author }) } - private runPluginStage(plugin: interfaces.Plugin, stage: string, ext: Function) { - this.logStage(plugin, i18n.translate(`ms.plugin.manager.stage.${stage}`)) - ext() - this.runCatch(plugin, stage) - this.runCatch(plugin, `${this.serverType}${stage}`) - this.execPluginStage(plugin, stage) + private runPluginStage(plugin: plugin.Plugin, stage: string, ext: Function) { + try { + this.logStage(plugin, i18n.translate(`ms.plugin.manager.stage.${stage}`)) + ext() + this.runCatch(plugin, stage) + this.runCatch(plugin, `${this.serverType}${stage}`) + this.execPluginStage(plugin, stage) + } catch (ex) { + console.i18n("ms.plugin.manager.stage.exec.error", { plugin: plugin.description.name, executor: stage, error: ex }) + } } /** * 从文件加载插件 * @param file java.io.File */ - loadFromFile(file: string): interfaces.Plugin { + loadFromFile(file: string): plugin.Plugin { let metadata = this.loadPlugin(file) let plugin = this.buildPlugin(metadata && metadata.description ? metadata.description : this.pluginMetadataMap.get(file.toString())) this.load(plugin) @@ -77,7 +81,7 @@ export class PluginManagerImpl implements plugin.PluginManager { } load(...args: any[]): void { - this.checkAndGet(args[0]).forEach((plugin: interfaces.Plugin) => { + this.checkAndGet(args[0]).forEach((plugin: plugin.Plugin) => { this.runPluginStage(plugin, 'load', () => { this.loadConfig(plugin) }) @@ -85,7 +89,7 @@ export class PluginManagerImpl implements plugin.PluginManager { } enable(...args: any[]): void { - this.checkAndGet(args[0]).forEach((plugin: interfaces.Plugin) => { + this.checkAndGet(args[0]).forEach((plugin: plugin.Plugin) => { this.runPluginStage(plugin, 'enable', () => { this.registryCommand(plugin) this.registryListener(plugin) @@ -94,7 +98,7 @@ export class PluginManagerImpl implements plugin.PluginManager { } disable(...args: any[]): void { - this.checkAndGet(args[0]).forEach((plugin: interfaces.Plugin) => { + this.checkAndGet(args[0]).forEach((plugin: plugin.Plugin) => { this.runPluginStage(plugin, 'disable', () => { this.saveConfig(plugin) this.unregistryCommand(plugin) @@ -104,12 +108,16 @@ export class PluginManagerImpl implements plugin.PluginManager { } reload(...args: any[]): void { - this.checkAndGet(args[0]).forEach((pl: interfaces.Plugin) => { + this.checkAndGet(args[0]).forEach((pl: plugin.Plugin) => { this.disable(pl) this.loadFromFile(pl.description.source) }) } + getPlugin(name: string) { + return this.pluginInstanceMap.get(name) + } + getPlugins() { return this.pluginInstanceMap } @@ -123,11 +131,11 @@ export class PluginManagerImpl implements plugin.PluginManager { } } - private checkAndGet(name: string | interfaces.Plugin | undefined | any): Map | interfaces.Plugin[] { + private checkAndGet(name: string | plugin.Plugin | undefined | any): Map | plugin.Plugin[] { if (name == this.pluginInstanceMap) { return this.pluginInstanceMap } if (typeof name == 'string' && this.pluginInstanceMap.has(name)) { return [this.pluginInstanceMap.get(name)] } - if (name instanceof interfaces.Plugin) { return [name as interfaces.Plugin] } - if (name.description || name.description.name) { return [name as interfaces.Plugin] } + if (name instanceof interfaces.Plugin) { return [name as plugin.Plugin] } + if (name.description || name.description.name) { return [name as plugin.Plugin] } throw new Error(`Plugin ${JSON.stringify(name)} not exist!`) } @@ -214,12 +222,12 @@ export class PluginManagerImpl implements plugin.PluginManager { } private buildPlugin(metadata: interfaces.PluginMetadata) { - let pluginInstance: interfaces.Plugin + let pluginInstance: plugin.Plugin switch (metadata.type) { case "ioc": try { this.bindPlugin(metadata) - pluginInstance = this.container.getNamed(plugin.Plugin, metadata.name) + pluginInstance = this.container.getNamed(plugin.Plugin, metadata.name) if (!(pluginInstance instanceof interfaces.Plugin)) { console.i18n('ms.plugin.manager.build.not.extends', { source: metadata.source }) return @@ -235,13 +243,13 @@ export class PluginManagerImpl implements plugin.PluginManager { default: throw new Error('§4不支持的插件类型 请检查加载器是否正常启用!') } - this.pluginInstanceMap.set(metadata.name, pluginInstance) + pluginInstance && this.pluginInstanceMap.set(metadata.name, pluginInstance) return pluginInstance } private bindPlugin(metadata: interfaces.PluginMetadata) { try { - let pluginInstance = this.container.getNamed(plugin.Plugin, metadata.name) + let pluginInstance = this.container.getNamed(plugin.Plugin, metadata.name) if (pluginInstance.description.source + '' !== metadata.source + '') { console.i18n('ms.plugin.manager.build.duplicate', { exists: pluginInstance.description.source, source: metadata.source }) } @@ -251,7 +259,7 @@ export class PluginManagerImpl implements plugin.PluginManager { } } - private loadConfig(plugin: interfaces.Plugin) { + private loadConfig(plugin: plugin.Plugin) { let configs = getPluginConfigMetadata(plugin) for (let [_, config] of configs) { try { @@ -276,7 +284,7 @@ export class PluginManagerImpl implements plugin.PluginManager { } } - private saveConfig(plugin: interfaces.Plugin) { + private saveConfig(plugin: plugin.Plugin) { let configs = getPluginConfigMetadata(plugin) for (let [_, config] of configs) { try { @@ -290,7 +298,7 @@ export class PluginManagerImpl implements plugin.PluginManager { } } - private registryCommand(pluginInstance: interfaces.Plugin) { + private registryCommand(pluginInstance: plugin.Plugin) { let cmds = getPluginCommandMetadata(pluginInstance) let tabs = getPluginTabCompleterMetadata(pluginInstance) for (const [_, cmd] of cmds) { @@ -303,7 +311,7 @@ export class PluginManagerImpl implements plugin.PluginManager { } } - private registryListener(pluginInstance: interfaces.Plugin) { + private registryListener(pluginInstance: plugin.Plugin) { let events = getPluginListenerMetadata(pluginInstance) for (const event of events) { // ignore space listener @@ -313,18 +321,18 @@ export class PluginManagerImpl implements plugin.PluginManager { } } - private unregistryCommand(pluginInstance: interfaces.Plugin) { + private unregistryCommand(pluginInstance: plugin.Plugin) { let cmds = getPluginCommandMetadata(pluginInstance) cmds.forEach(cmd => { this.CommandManager.off(pluginInstance, cmd.name) }) } - private unregistryListener(pluginInstance: interfaces.Plugin) { + private unregistryListener(pluginInstance: plugin.Plugin) { this.EventManager.disable(pluginInstance) } - private execPluginStage(pluginInstance: interfaces.Plugin, stageName: string) { + private execPluginStage(pluginInstance: plugin.Plugin, stageName: string) { let stages = getPluginStageMetadata(pluginInstance, stageName) for (const stage of stages) { if (!this.allowProcess(stage.servers)) { continue }