refactor: rename ployfill to polyfill
Signed-off-by: MiaoWoo <admin@yumc.pw>
This commit is contained in:
		
							
								
								
									
										26
									
								
								packages/polyfill/src/es5-ext.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								packages/polyfill/src/es5-ext.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
// ES2015 String ployfill must force overwrite to js method
 | 
			
		||||
Object.defineProperty(String.prototype, 'contains', {
 | 
			
		||||
    value: require(`es5-ext/string/#/contains/shim`),
 | 
			
		||||
    configurable: true,
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    writable: true
 | 
			
		||||
})
 | 
			
		||||
Object.defineProperty(String.prototype, 'repeat', {
 | 
			
		||||
    value: require(`es5-ext/string/#/repeat/shim`),
 | 
			
		||||
    configurable: true,
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    writable: true
 | 
			
		||||
})
 | 
			
		||||
Object.defineProperty(String.prototype, 'startsWith', {
 | 
			
		||||
    value: require(`es5-ext/string/#/starts-with/shim`),
 | 
			
		||||
    configurable: true,
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    writable: true
 | 
			
		||||
})
 | 
			
		||||
Object.defineProperty(String.prototype, 'endsWith', {
 | 
			
		||||
    value: require(`es5-ext/string/#/ends-with/shim`),
 | 
			
		||||
    configurable: true,
 | 
			
		||||
    enumerable: false,
 | 
			
		||||
    writable: true
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								packages/polyfill/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								packages/polyfill/src/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
/// <reference types="@ccms/nashorn" />
 | 
			
		||||
import '@ccms/nodejs'
 | 
			
		||||
import i18n from '@ccms/i18n'
 | 
			
		||||
let polyfillStartTime = new Date().getTime()
 | 
			
		||||
i18n.initialize()
 | 
			
		||||
console.i18n("ms.polyfill.initialize")
 | 
			
		||||
import './es5-ext'
 | 
			
		||||
import './node-shim'
 | 
			
		||||
import 'core-js'
 | 
			
		||||
//@ts-ignore
 | 
			
		||||
process.on('exit', () => require.disable())
 | 
			
		||||
global.setGlobal('Proxy', require('./proxy').Proxy)
 | 
			
		||||
global.setGlobal('XMLHttpRequest', require('./xml-http-request').XMLHttpRequest)
 | 
			
		||||
global.setGlobal('Blob', require('blob-polyfill').Blob)
 | 
			
		||||
console.i18n("ms.polyfill.completed", { time: (new Date().getTime() - polyfillStartTime) / 1000 })
 | 
			
		||||
							
								
								
									
										207
									
								
								packages/polyfill/src/node-shim.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								packages/polyfill/src/node-shim.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,207 @@
 | 
			
		||||
import { EventEmitter } from 'events'
 | 
			
		||||
 | 
			
		||||
const System = Java.type('java.lang.System')
 | 
			
		||||
const Thread = Java.type('java.lang.Thread')
 | 
			
		||||
const InterruptedException = Java.type('java.lang.InterruptedException')
 | 
			
		||||
const ThreadGroup = Java.type("java.lang.ThreadGroup")
 | 
			
		||||
const AtomicInteger = Java.type("java.util.concurrent.atomic.AtomicInteger")
 | 
			
		||||
const Callable = Java.type('java.util.concurrent.Callable')
 | 
			
		||||
const ThreadFactory = Java.type("java.util.concurrent.ThreadFactory")
 | 
			
		||||
const TimeoutException = Java.type('java.util.concurrent.TimeoutException')
 | 
			
		||||
const ThreadPoolExecutor = Java.type('java.util.concurrent.ThreadPoolExecutor')
 | 
			
		||||
const LinkedBlockingQueue = Java.type("java.util.concurrent.LinkedBlockingQueue")
 | 
			
		||||
const TimeUnit = Java.type('java.util.concurrent.TimeUnit')
 | 
			
		||||
const DelayQueue = Java.type('java.util.concurrent.DelayQueue')
 | 
			
		||||
const JavaScriptTask = Java.type(base.getJavaScriptTaskClass().name)
 | 
			
		||||
 | 
			
		||||
const threadCount = new AtomicInteger(0)
 | 
			
		||||
const threadGroup = new ThreadGroup("@ccms/ployfill-micro-task")
 | 
			
		||||
const microTaskPool = new ThreadPoolExecutor(
 | 
			
		||||
    100, 200, 60, TimeUnit.SECONDS,
 | 
			
		||||
    new LinkedBlockingQueue(300),
 | 
			
		||||
    new ThreadFactory((run: any) => new Thread(threadGroup, run, "@ccms/micro-task-" + threadCount.incrementAndGet()))
 | 
			
		||||
)
 | 
			
		||||
class Process extends EventEmitter {
 | 
			
		||||
    env = {
 | 
			
		||||
        __noSuchProperty__: (prop) => {
 | 
			
		||||
            return System.getenv(prop)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    platform = System.getProperty("os.name")
 | 
			
		||||
    constructor() {
 | 
			
		||||
        super()
 | 
			
		||||
        this.on('exit', () => {
 | 
			
		||||
            console.log(`await microTaskPool termination! queueTask: ${microTaskPool.shutdownNow().size()} remainTask: ${threadGroup.activeCount()}`)
 | 
			
		||||
            microTaskPool.awaitTermination(3000, TimeUnit.MILLISECONDS)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    on(event: string | symbol, listener: (...args: any[]) => void) {
 | 
			
		||||
        return super.on(event, (...args) => {
 | 
			
		||||
            try {
 | 
			
		||||
                listener(...args)
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                try {
 | 
			
		||||
                    super.emit('error', error)
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    console.ex(error)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    nextTick(func: Function) {
 | 
			
		||||
        microTaskPool.execute(func)
 | 
			
		||||
    }
 | 
			
		||||
    exit(code: number) {
 | 
			
		||||
        console.log(`process exit by code ${code}!`)
 | 
			
		||||
        this.emit('exit', code)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toString() {
 | 
			
		||||
        return "[object process]"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class EventLoop {
 | 
			
		||||
    private eventLoopMainThread = undefined
 | 
			
		||||
    private eventLoopTaskQueue = new DelayQueue()
 | 
			
		||||
    private taskExecTimeout = 3
 | 
			
		||||
    private fixedThreadPool = undefined
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.taskExecTimeout = parseInt(process.env.MS_NODE_EVENT_LOOP_TIMEOUT) || 3
 | 
			
		||||
        this.fixedThreadPool = new ThreadPoolExecutor(
 | 
			
		||||
            1, 1, 0, TimeUnit.SECONDS,
 | 
			
		||||
            new LinkedBlockingQueue(300),
 | 
			
		||||
            new ThreadFactory((run: any) => {
 | 
			
		||||
                let thread = new Thread(run, "@ccms/node-shim/event-loop-exec")
 | 
			
		||||
                thread.setDaemon(true)
 | 
			
		||||
                return thread
 | 
			
		||||
            }))
 | 
			
		||||
        this.eventLoopMainThread = new Thread(() => {
 | 
			
		||||
            let task = undefined
 | 
			
		||||
            try {
 | 
			
		||||
                while (!this.eventLoopMainThread.isInterrupted()) {
 | 
			
		||||
                    task = this.eventLoopTaskQueue.take()
 | 
			
		||||
                    try {
 | 
			
		||||
                        task.getTask()()
 | 
			
		||||
                    } catch (cause) {
 | 
			
		||||
                        try {
 | 
			
		||||
                            process.emit('error', cause)
 | 
			
		||||
                        } catch (error) {
 | 
			
		||||
                            console.error(cause)
 | 
			
		||||
                            console.ex(cause)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                console.log(`EventLoop Thread isInterrupted exit! remainTask: ${this.eventLoopTaskQueue.size()}`)
 | 
			
		||||
                this.eventLoopTaskQueue.clear()
 | 
			
		||||
                this.eventLoopTaskQueue = undefined
 | 
			
		||||
                this.timeoutCount = undefined
 | 
			
		||||
                this.timeoutTasks = undefined
 | 
			
		||||
                this.intervalCount = undefined
 | 
			
		||||
                this.intervalTasks = undefined
 | 
			
		||||
                this.eventLoopMainThread = undefined
 | 
			
		||||
            }
 | 
			
		||||
        }, "@ccms/node-shim/event-loop")
 | 
			
		||||
        this.eventLoopMainThread.setDaemon(true)
 | 
			
		||||
        process.on('exit', () => {
 | 
			
		||||
            this.eventLoopMainThread.interrupt()
 | 
			
		||||
            this.fixedThreadPool.shutdownNow()
 | 
			
		||||
            console.log(`await fixedThreadPool termination!`)
 | 
			
		||||
            this.fixedThreadPool.awaitTermination(3000, TimeUnit.MILLISECONDS)
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    startEventLoop() {
 | 
			
		||||
        this.eventLoopMainThread.start()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private putDelayTask(id: number, callback: Function, ms: number) {
 | 
			
		||||
        this.eventLoopTaskQueue.put(new JavaScriptTask(id, callback, ms))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private limitTimeTask(name: string, callback: Function, ...args: any[]) {
 | 
			
		||||
        if (!callback) {
 | 
			
		||||
            throw new Error(`task ${name} callback function can't be null!`)
 | 
			
		||||
        }
 | 
			
		||||
        if (this.fixedThreadPool.isShutdown()) { return console.warn(`FixedThreadPool isTerminated! ignore Task ${name}!`) }
 | 
			
		||||
        try {
 | 
			
		||||
            this.fixedThreadPool.submit(new Callable({
 | 
			
		||||
                call: () => {
 | 
			
		||||
                    try {
 | 
			
		||||
                        callback.apply(undefined, args)
 | 
			
		||||
                    } catch (cause) {
 | 
			
		||||
                        cause = cause.getCause && cause.getCause() || cause
 | 
			
		||||
                        try {
 | 
			
		||||
                            process.emit('error', cause)
 | 
			
		||||
                        } catch (error) {
 | 
			
		||||
                            console.error(cause)
 | 
			
		||||
                            console.ex(cause)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            })).get(this.taskExecTimeout, TimeUnit.SECONDS)
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            if (error instanceof InterruptedException) {
 | 
			
		||||
                return console.warn(`FixedThreadPool isInterrupted exit! Task ${name} exec exit!`)
 | 
			
		||||
            }
 | 
			
		||||
            if (error instanceof TimeoutException) {
 | 
			
		||||
                return console.warn(`Task ${name} => ${callback} exec time greater than ${this.taskExecTimeout}s!`)
 | 
			
		||||
            }
 | 
			
		||||
            throw error.getCause && error.getCause() || error
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private timeoutCount = new AtomicInteger(0)
 | 
			
		||||
    private timeoutTasks = []
 | 
			
		||||
    setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]) {
 | 
			
		||||
        let taskId = this.timeoutCount.incrementAndGet()
 | 
			
		||||
        this.timeoutTasks[taskId] = callback
 | 
			
		||||
        console.trace(`create setTimeout task ${taskId} => ${callback}`)
 | 
			
		||||
        this.putDelayTask(taskId, () => {
 | 
			
		||||
            if (this.timeoutTasks[taskId]) {
 | 
			
		||||
                this.limitTimeTask(`setTimeout-${taskId}`, callback, ...args)
 | 
			
		||||
            } else {
 | 
			
		||||
                console.trace(`ignore setTimeout ${ms}ms task ${taskId} because it's cancelled!`)
 | 
			
		||||
            }
 | 
			
		||||
        }, ms)
 | 
			
		||||
        return taskId
 | 
			
		||||
    }
 | 
			
		||||
    clearTimeout(taskId: number) {
 | 
			
		||||
        delete this.timeoutTasks[taskId]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private intervalCount = new AtomicInteger(0)
 | 
			
		||||
    private intervalTasks = []
 | 
			
		||||
    setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]) {
 | 
			
		||||
        let taskId = this.intervalCount.incrementAndGet()
 | 
			
		||||
        this.intervalTasks[taskId] = callback
 | 
			
		||||
        console.trace(`create setInterval ${ms}ms task ${taskId} => ${callback}`)
 | 
			
		||||
        let intervalTask = () => {
 | 
			
		||||
            if (this.intervalTasks[taskId]) {
 | 
			
		||||
                this.limitTimeTask(`setInterval-${taskId}`, callback, ...args)
 | 
			
		||||
                this.putDelayTask(taskId, intervalTask, ms)
 | 
			
		||||
            } else {
 | 
			
		||||
                console.trace(`ignore setInterval task ${taskId} because it's cancelled!`)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this.putDelayTask(taskId, intervalTask, ms)
 | 
			
		||||
        return taskId
 | 
			
		||||
    }
 | 
			
		||||
    clearInterval(taskId: number) {
 | 
			
		||||
        delete this.intervalTasks[taskId]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
global.setGlobal('process', new Process(), {})
 | 
			
		||||
Object.defineProperty(process, require('core-js/es/symbol/to-string-tag'), { value: '[object process]' })
 | 
			
		||||
const eventLoop = new EventLoop()
 | 
			
		||||
Object.defineProperty(process, 'eventLoop', { value: eventLoop })
 | 
			
		||||
eventLoop.startEventLoop()
 | 
			
		||||
global.setGlobal('queueMicrotask', (func: any) => microTaskPool.execute(func), {})
 | 
			
		||||
global.setGlobal('setTimeout', eventLoop.setTimeout.bind(eventLoop), {})
 | 
			
		||||
global.setGlobal('clearTimeout', eventLoop.clearTimeout.bind(eventLoop), {})
 | 
			
		||||
global.setGlobal('setInterval', eventLoop.setInterval.bind(eventLoop), {})
 | 
			
		||||
global.setGlobal('clearInterval', eventLoop.clearInterval.bind(eventLoop), {})
 | 
			
		||||
global.setGlobal('setImmediate', (callback: (...args: any[]) => void, ...args: any[]) => eventLoop.setTimeout(callback, 0, ...args), { writable: true })
 | 
			
		||||
global.setGlobal('clearImmediate ', eventLoop.clearTimeout.bind(eventLoop), { writable: true })
 | 
			
		||||
							
								
								
									
										23
									
								
								packages/polyfill/src/proxy.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								packages/polyfill/src/proxy.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
import type { ProxyHandle } from '@ccms/nashorn'
 | 
			
		||||
 | 
			
		||||
// Nashorn JSAdapter See https://wiki.openjdk.java.net/display/Nashorn/Nashorn+extensions#Nashornextensions-JSAdapterconstructor
 | 
			
		||||
let createProxy = eval(`
 | 
			
		||||
    function(handle){ return new JSAdapter(handle) }
 | 
			
		||||
`)
 | 
			
		||||
export class Proxy {
 | 
			
		||||
    static newProxy(target: any, handle: Partial<ProxyHandle>): any {
 | 
			
		||||
        return new Proxy(target, handle)
 | 
			
		||||
    }
 | 
			
		||||
    constructor(target: any, handle: Partial<ProxyHandle>) {
 | 
			
		||||
        return createProxy({
 | 
			
		||||
            __get__: (name: string) => handle.get ? handle.get(target, name, undefined) : target[name],
 | 
			
		||||
            __put__: (name: string, value: any) => handle.set ? handle.set(target, name, value, undefined) : target[name] = value,
 | 
			
		||||
            __call__: (name: string, ...args: any) => handle.apply ? handle.apply(target, name, args) : target[name].apply(target, args),
 | 
			
		||||
            __new__: (...args: any) => handle.construct ? handle.construct(target, args, target) : new target(...args),
 | 
			
		||||
            __getIds__: () => handle.ownKeys ? handle.ownKeys(target) : Object.keys(target),
 | 
			
		||||
            __getValues__: () => handle.values ? handle.values(target) : Object.values(target),
 | 
			
		||||
            __has__: (name: string) => handle.has ? handle.has(target, name) : Object.getOwnPropertyDescriptor(target, name) != undefined,
 | 
			
		||||
            __delete__: (name: string) => handle.deleteProperty ? handle.deleteProperty(target, name) : delete target[name]
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										141
									
								
								packages/polyfill/src/task.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								packages/polyfill/src/task.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,141 @@
 | 
			
		||||
(function nashornEventLoopMain(context) {
 | 
			
		||||
    'use strict';
 | 
			
		||||
 | 
			
		||||
    var Thread = Java.type('java.lang.Thread');
 | 
			
		||||
    var Phaser = Java.type('java.util.concurrent.Phaser');
 | 
			
		||||
    var ArrayDeque = Java.type('java.util.ArrayDeque');
 | 
			
		||||
    var HashMap = Java.type('java.util.HashMap');
 | 
			
		||||
    var TimeUnit = Java.type("java.util.concurrent.TimeUnit");
 | 
			
		||||
    var Runnable = Java.type('java.lang.Runnable');
 | 
			
		||||
 | 
			
		||||
    var globalTimerId;
 | 
			
		||||
    var timerMap;
 | 
			
		||||
    var eventLoop;
 | 
			
		||||
    var phaser = new Phaser();
 | 
			
		||||
 | 
			
		||||
    // __NASHORN_POLYFILL_TIMER__ type is ScheduledExecutorService
 | 
			
		||||
    var scheduler = context.__NASHORN_POLYFILL_TIMER__;
 | 
			
		||||
 | 
			
		||||
    resetEventLoop();
 | 
			
		||||
 | 
			
		||||
    //   console.log('main javasript thread ' + Thread.currentThread().getName());
 | 
			
		||||
 | 
			
		||||
    function resetEventLoop() {
 | 
			
		||||
        globalTimerId = 1;
 | 
			
		||||
        if (timerMap) {
 | 
			
		||||
            timerMap.forEach(function(key, value) {
 | 
			
		||||
                value.cancel(true);
 | 
			
		||||
            })
 | 
			
		||||
        }
 | 
			
		||||
        timerMap = new HashMap();
 | 
			
		||||
        eventLoop = new ArrayDeque();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function waitForMessages() {
 | 
			
		||||
        phaser.register();
 | 
			
		||||
        var wait = !(eventLoop.size() === 0);
 | 
			
		||||
        phaser.arriveAndDeregister();
 | 
			
		||||
        return wait;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function processNextMessages() {
 | 
			
		||||
        var remaining = 1;
 | 
			
		||||
        while (remaining) {
 | 
			
		||||
            phaser.register();
 | 
			
		||||
            var message = eventLoop.removeFirst();
 | 
			
		||||
            remaining = eventLoop.size();
 | 
			
		||||
            phaser.arriveAndDeregister();
 | 
			
		||||
 | 
			
		||||
            var fn = message.fn;
 | 
			
		||||
            var args = message.args;
 | 
			
		||||
 | 
			
		||||
            try {
 | 
			
		||||
                fn.apply(context, args);
 | 
			
		||||
            } catch (e) {
 | 
			
		||||
                console.trace(e);
 | 
			
		||||
                console.trace(fn);
 | 
			
		||||
                console.trace(args);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    context.nashornEventLoop = {
 | 
			
		||||
        process: function() {
 | 
			
		||||
            while (waitForMessages()) {
 | 
			
		||||
                processNextMessages()
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        reset: resetEventLoop
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    function createRunnable(fn, timerId, args, repeated) {
 | 
			
		||||
        return new Runnable({
 | 
			
		||||
            run: function() {
 | 
			
		||||
                try {
 | 
			
		||||
                    var phase = phaser.register();
 | 
			
		||||
                    eventLoop.addLast({
 | 
			
		||||
                        fn: fn,
 | 
			
		||||
                        args: args
 | 
			
		||||
                    });
 | 
			
		||||
                } catch (e) {
 | 
			
		||||
                    console.trace(e);
 | 
			
		||||
                } finally {
 | 
			
		||||
                    if (!repeated) timerMap.remove(timerId);
 | 
			
		||||
                    phaser.arriveAndDeregister();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var setTimeout = function(fn, millis /* [, args...] */) {
 | 
			
		||||
        var args = [].slice.call(arguments, 2, arguments.length);
 | 
			
		||||
 | 
			
		||||
        var timerId = globalTimerId++;
 | 
			
		||||
        var runnable = createRunnable(fn, timerId, args, false);
 | 
			
		||||
 | 
			
		||||
        var task = scheduler.schedule(runnable, millis, TimeUnit.MILLISECONDS);
 | 
			
		||||
        timerMap.put(timerId, task);
 | 
			
		||||
 | 
			
		||||
        return timerId;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var setImmediate = function(fn /* [, args...] */) {
 | 
			
		||||
        var args = [].slice.call(arguments, 1, arguments.length);
 | 
			
		||||
        // @ts-ignore
 | 
			
		||||
        return setTimeout(fn, 0, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var clearImmediate = function(timerId) {
 | 
			
		||||
        clearTimeout(timerId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var clearTimeout = function(timerId) {
 | 
			
		||||
        var task = timerMap.get(timerId);
 | 
			
		||||
        if (task) {
 | 
			
		||||
            task.cancel(true);
 | 
			
		||||
            timerMap.remove(timerId);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var setInterval = function(fn, delay /* [, args...] */) {
 | 
			
		||||
        var args = [].slice.call(arguments, 2, arguments.length);
 | 
			
		||||
        var timerId = globalTimerId++;
 | 
			
		||||
        var runnable = createRunnable(fn, timerId, args, true);
 | 
			
		||||
        var task = scheduler.scheduleWithFixedDelay(runnable, delay, delay, TimeUnit.MILLISECONDS);
 | 
			
		||||
        timerMap.put(timerId, task);
 | 
			
		||||
        return timerId;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    var clearInterval = function(timerId) {
 | 
			
		||||
        clearTimeout(timerId);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    context.setTimeout = setTimeout;
 | 
			
		||||
    context.clearTimeout = clearTimeout;
 | 
			
		||||
    context.setImmediate = setImmediate;
 | 
			
		||||
    context.clearImmediate = clearImmediate;
 | 
			
		||||
    context.setInterval = setInterval;
 | 
			
		||||
    context.clearInterval = clearInterval;
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
})(typeof global !== "undefined" && global || typeof self !== "undefined" && self || this);
 | 
			
		||||
							
								
								
									
										255
									
								
								packages/polyfill/src/xml-http-request.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										255
									
								
								packages/polyfill/src/xml-http-request.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,255 @@
 | 
			
		||||
import '@ccms/nashorn'
 | 
			
		||||
 | 
			
		||||
const URL = Java.type("java.net.URL")
 | 
			
		||||
const Files = Java.type("java.nio.file.Files")
 | 
			
		||||
const StandardCopyOption = Java.type("java.nio.file.StandardCopyOption")
 | 
			
		||||
const JavaString = Java.type("java.lang.String")
 | 
			
		||||
const SecureRandom = Java.type("java.security.SecureRandom")
 | 
			
		||||
const SSLContext = Java.type("javax.net.ssl.SSLContext")
 | 
			
		||||
const HttpsURLConnection = Java.type("javax.net.ssl.HttpsURLConnection")
 | 
			
		||||
const HostnameVerifier = Java.type("javax.net.ssl.HostnameVerifier")
 | 
			
		||||
const X509TrustManager = Java.type("javax.net.ssl.X509TrustManager")
 | 
			
		||||
 | 
			
		||||
const SocketTimeoutException = Java.type('java.net.SocketTimeoutException')
 | 
			
		||||
 | 
			
		||||
const Callable = Java.type('java.util.concurrent.Callable')
 | 
			
		||||
const Executors = Java.type('java.util.concurrent.Executors')
 | 
			
		||||
 | 
			
		||||
const UTF_8 = "UTF-8"
 | 
			
		||||
 | 
			
		||||
const TrustAnyHostnameVerifier = new HostnameVerifier({ verify: () => true })
 | 
			
		||||
 | 
			
		||||
const SSLSocketFactory = function initSSLSocketFactory() {
 | 
			
		||||
    let sslContext = SSLContext.getInstance("TLS")
 | 
			
		||||
    sslContext.init(null, [new X509TrustManager({
 | 
			
		||||
        getAcceptedIssuers: () => null,
 | 
			
		||||
        checkClientTrusted: () => { },
 | 
			
		||||
        checkServerTrusted: () => { }
 | 
			
		||||
    })], new SecureRandom())
 | 
			
		||||
    return sslContext.getSocketFactory()
 | 
			
		||||
}()
 | 
			
		||||
 | 
			
		||||
interface Future<T> {
 | 
			
		||||
    cancel(): boolean
 | 
			
		||||
    isCancelled(): boolean
 | 
			
		||||
    isDone(): boolean
 | 
			
		||||
    get(): T
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum ReadyState {
 | 
			
		||||
    UNSENT,//Client has been created. open() not called yet.
 | 
			
		||||
    OPENED,//open() has been called.
 | 
			
		||||
    HEADERS_RECEIVED,//send() has been called, and headers and status are available.
 | 
			
		||||
    LOADING,//Downloading; responseText holds partial data.
 | 
			
		||||
    DONE,//The operation is complete.
 | 
			
		||||
}
 | 
			
		||||
type RequestMethod =
 | 
			
		||||
    | 'get' | 'GET'
 | 
			
		||||
    | 'delete' | 'DELETE'
 | 
			
		||||
    | 'head' | 'HEAD'
 | 
			
		||||
    | 'options' | 'OPTIONS'
 | 
			
		||||
    | 'post' | 'POST'
 | 
			
		||||
    | 'put' | 'PUT'
 | 
			
		||||
    | 'patch' | 'PATCH'
 | 
			
		||||
type ResponseType =
 | 
			
		||||
    | 'arraybuffer'
 | 
			
		||||
    | 'blob'
 | 
			
		||||
    | 'document'
 | 
			
		||||
    | 'json'
 | 
			
		||||
    | 'text'
 | 
			
		||||
    | 'stream'
 | 
			
		||||
type EventType =
 | 
			
		||||
    | 'load'
 | 
			
		||||
    | 'error'
 | 
			
		||||
    | 'abort'
 | 
			
		||||
    | 'progress'
 | 
			
		||||
    | 'timeout'
 | 
			
		||||
    | 'loadend'
 | 
			
		||||
    | 'loadstart'
 | 
			
		||||
type HttpHeader = { [key: string]: string }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const executor = Executors.newCachedThreadPool()
 | 
			
		||||
 | 
			
		||||
export class XMLHttpRequest {
 | 
			
		||||
    private _timeout: number
 | 
			
		||||
    private _responseType: ResponseType = 'text';
 | 
			
		||||
    private _withCredentials: boolean
 | 
			
		||||
 | 
			
		||||
    private _readyState: ReadyState = ReadyState.UNSENT;
 | 
			
		||||
 | 
			
		||||
    private _method: RequestMethod
 | 
			
		||||
    private _url: string
 | 
			
		||||
    private _async: boolean
 | 
			
		||||
    private _mimeType: string
 | 
			
		||||
    private _requestHeaders: HttpHeader = {};
 | 
			
		||||
 | 
			
		||||
    private _status: number = 0;
 | 
			
		||||
    private _statusText: string = null;
 | 
			
		||||
    private _response: any
 | 
			
		||||
    private _responseText: any
 | 
			
		||||
    private _responseURL: string
 | 
			
		||||
    private _responseHeaders: HttpHeader = {};
 | 
			
		||||
 | 
			
		||||
    private _connection = null;
 | 
			
		||||
 | 
			
		||||
    get timeout() {
 | 
			
		||||
        return this._timeout
 | 
			
		||||
    }
 | 
			
		||||
    set timeout(timeout: number) {
 | 
			
		||||
        this._timeout = timeout
 | 
			
		||||
    }
 | 
			
		||||
    get readyState() {
 | 
			
		||||
        return this._readyState
 | 
			
		||||
    }
 | 
			
		||||
    set responseType(type: ResponseType) {
 | 
			
		||||
        this._responseType = type
 | 
			
		||||
    }
 | 
			
		||||
    get responseType() {
 | 
			
		||||
        return this._responseType
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    get status() {
 | 
			
		||||
        return this._status
 | 
			
		||||
    }
 | 
			
		||||
    get statusText() {
 | 
			
		||||
        return this._statusText
 | 
			
		||||
    }
 | 
			
		||||
    get response() {
 | 
			
		||||
        return this._response || this.get()
 | 
			
		||||
    }
 | 
			
		||||
    get responseText() {
 | 
			
		||||
        return this._responseText
 | 
			
		||||
    }
 | 
			
		||||
    get responseXML() {
 | 
			
		||||
        return this._response
 | 
			
		||||
    }
 | 
			
		||||
    get responseURL() {
 | 
			
		||||
        return this._responseURL
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public onload: () => void
 | 
			
		||||
    public onerror: (ex: Error) => void
 | 
			
		||||
    public onabort: () => void
 | 
			
		||||
    public onprogress: () => void
 | 
			
		||||
    public ontimeout: (ex: Error) => void
 | 
			
		||||
    public onloadend: () => void
 | 
			
		||||
    public onloadstart: () => void
 | 
			
		||||
    public onreadystatechange: () => void
 | 
			
		||||
 | 
			
		||||
    setRequestHeader(key: string, val: string) {
 | 
			
		||||
        this._requestHeaders[key] = val
 | 
			
		||||
    }
 | 
			
		||||
    getResponseHeader(key: string): string {
 | 
			
		||||
        return this._responseHeaders[key]
 | 
			
		||||
    }
 | 
			
		||||
    getAllResponseHeaders(): any {
 | 
			
		||||
        return this._responseHeaders
 | 
			
		||||
    }
 | 
			
		||||
    addEventListener(event: EventType, callback: Function) {
 | 
			
		||||
        this[`on${event.toLowerCase()}`] = callback
 | 
			
		||||
    }
 | 
			
		||||
    overrideMimeType(mimeType: string) {
 | 
			
		||||
        this._mimeType = mimeType
 | 
			
		||||
    }
 | 
			
		||||
    open(method: RequestMethod, url: string, async: boolean = true, user?: string, password?: string) {
 | 
			
		||||
        if (this._readyState !== ReadyState.UNSENT) { throw new Error(`Error Status ${this._readyState}!`) }
 | 
			
		||||
 | 
			
		||||
        this._method = method
 | 
			
		||||
        this._url = url
 | 
			
		||||
        this._async = async
 | 
			
		||||
 | 
			
		||||
        this._connection = new URL(this._url).openConnection()
 | 
			
		||||
        if (this._connection instanceof HttpsURLConnection) {
 | 
			
		||||
            this._connection.setHostnameVerifier(TrustAnyHostnameVerifier)
 | 
			
		||||
            this._connection.setSSLSocketFactory(SSLSocketFactory)
 | 
			
		||||
        }
 | 
			
		||||
        this._connection.setRequestMethod(this._method)
 | 
			
		||||
        this._connection.setDoOutput(true)
 | 
			
		||||
        this._connection.setDoInput(true)
 | 
			
		||||
        this._connection.setConnectTimeout(this._timeout)
 | 
			
		||||
        this._connection.setReadTimeout(this._timeout)
 | 
			
		||||
 | 
			
		||||
        this.setReadyState(ReadyState.OPENED)
 | 
			
		||||
    }
 | 
			
		||||
    send(body?: string | object): Future<string> {
 | 
			
		||||
        for (const header in this._requestHeaders) {
 | 
			
		||||
            this._connection.setRequestProperty(header, this._requestHeaders[header])
 | 
			
		||||
        }
 | 
			
		||||
        if (this._readyState !== ReadyState.OPENED) { throw new Error(`Error Status ${this._readyState}!`) }
 | 
			
		||||
        let future = executor.submit(new Callable({ call: () => this._send(body) }))
 | 
			
		||||
        if (!this._async) { future.get() }
 | 
			
		||||
        return future
 | 
			
		||||
    }
 | 
			
		||||
    get() {
 | 
			
		||||
        if (this._response === undefined) {
 | 
			
		||||
            switch (this._responseType) {
 | 
			
		||||
                case "json":
 | 
			
		||||
                    return this._response = JSON.parse(this._responseText)
 | 
			
		||||
                case "text":
 | 
			
		||||
                    return this._response = this._responseText
 | 
			
		||||
                default:
 | 
			
		||||
                    throw Error(`Unsupport ResponseType: ${this._responseType} !`)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return this._response
 | 
			
		||||
    }
 | 
			
		||||
    abort() {
 | 
			
		||||
        this._connection.disconnect()
 | 
			
		||||
        this.onabort && this.onabort()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private _send(body?: string | object) {
 | 
			
		||||
        try {
 | 
			
		||||
            this._connection.connect()
 | 
			
		||||
            this.onloadstart && this.onloadstart()
 | 
			
		||||
            if (body) {
 | 
			
		||||
                let bodyType = Object.prototype.toString.call(body)
 | 
			
		||||
                if (typeof body !== "string") { throw new Error(`body(${bodyType}) must be string!`) }
 | 
			
		||||
                var out = this._connection.getOutputStream()
 | 
			
		||||
                out.write(new JavaString(body).getBytes(UTF_8))
 | 
			
		||||
                out.flush()
 | 
			
		||||
                out.close()
 | 
			
		||||
            }
 | 
			
		||||
            this.setReadyState(ReadyState.LOADING)
 | 
			
		||||
            this._status = this._connection.getResponseCode()
 | 
			
		||||
            this._statusText = this._connection.getResponseMessage()
 | 
			
		||||
            if (this._status >= 0 && this._status < 300) {
 | 
			
		||||
                this._responseText = this.readOutput(this._connection.getInputStream())
 | 
			
		||||
            } else if (this._status >= 300 && this._status < 400) {
 | 
			
		||||
                this._responseURL = this.getResponseHeader('Location')
 | 
			
		||||
            } else {
 | 
			
		||||
                this._responseText = this.readOutput(this._connection.getErrorStream())
 | 
			
		||||
            }
 | 
			
		||||
            this.setResponseHeaders(this._connection.getHeaderFields())
 | 
			
		||||
            this.onloadend && this.onloadend()
 | 
			
		||||
        } catch (ex) {
 | 
			
		||||
            if (ex instanceof SocketTimeoutException && this.ontimeout) {
 | 
			
		||||
                return this.ontimeout(ex)
 | 
			
		||||
            } else if (this.onerror) {
 | 
			
		||||
                return this.onerror(ex)
 | 
			
		||||
            }
 | 
			
		||||
            throw ex
 | 
			
		||||
        } finally {
 | 
			
		||||
            this._connection.disconnect()
 | 
			
		||||
            this.setReadyState(ReadyState.DONE)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private setResponseHeaders(header: any) {
 | 
			
		||||
        header.forEach((key: string | number, value: string | any[]) => {
 | 
			
		||||
            this._responseHeaders[key + ''] = value[value.length - 1] + ''
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private setReadyState(state: ReadyState) {
 | 
			
		||||
        this._readyState = state
 | 
			
		||||
        this.onreadystatechange && this.onreadystatechange()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private readOutput(input: any) {
 | 
			
		||||
        var tempFile = Files.createTempFile('xhr', '.response')
 | 
			
		||||
        Files.copy(input, tempFile, StandardCopyOption['REPLACE_EXISTING']); tempFile.toFile().deleteOnExit()
 | 
			
		||||
        return new JavaString(Files.readAllBytes(tempFile), 'UTF-8')
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user