This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
2021-09-16 13:01:47 +08:00

367 lines
12 KiB
JavaScript

/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { uniqueId } from "../util/uniqueId";
import { loge } from "../util/log";
import "reflect-metadata";
function hookBeforeNativeCall(context) {
if (context) {
setContext(context);
context.hookBeforeNativeCall();
}
}
function getContext() {
return Reflect.getMetadata('__doric_context__', global);
}
function setContext(context) {
Reflect.defineMetadata('__doric_context__', context, global);
}
export function jsCallResolve(contextId, callbackId, args) {
const context = gContexts.get(contextId);
if (context === undefined) {
loge(`Cannot find context for context id:${contextId}`);
return;
}
const callback = context.callbacks.get(callbackId);
if (callback === undefined) {
loge(`Cannot find call for context id:${contextId},callback id:${callbackId}`);
return;
}
const argumentsList = [];
for (let i = 2; i < arguments.length; i++) {
argumentsList.push(arguments[i]);
}
hookBeforeNativeCall(context);
Reflect.apply(callback.resolve, context, argumentsList);
}
export function jsCallReject(contextId, callbackId, args) {
const context = gContexts.get(contextId);
if (context === undefined) {
loge(`Cannot find context for context id:${contextId}`);
return;
}
const callback = context.callbacks.get(callbackId);
if (callback === undefined) {
loge(`Cannot find call for context id:${contextId},callback id:${callbackId}`);
return;
}
const argumentsList = [];
for (let i = 2; i < arguments.length; i++) {
argumentsList.push(arguments[i]);
}
hookBeforeNativeCall(context);
Reflect.apply(callback.reject, context.entity, argumentsList);
}
export class Context {
constructor(id) {
this.callbacks = new Map;
this.classes = new Map;
this.id = id;
return new Proxy(this, {
get: (target, p) => {
if (Reflect.has(target, p)) {
return Reflect.get(target, p);
}
else {
const namespace = p;
return new Proxy({}, {
get: (target, p) => {
if (Reflect.has(target, p)) {
return Reflect.get(target, p);
}
else {
const context = this;
return function () {
const args = [];
args.push(namespace);
args.push(p);
for (let arg of arguments) {
args.push(arg);
}
return Reflect.apply(context.callNative, context, args);
};
}
}
});
}
}
});
}
hookBeforeNativeCall() {
if (this.entity && Reflect.has(this.entity, 'hookBeforeNativeCall')) {
Reflect.apply(Reflect.get(this.entity, 'hookBeforeNativeCall'), this.entity, []);
}
}
hookAfterNativeCall() {
if (this.entity && Reflect.has(this.entity, 'hookAfterNativeCall')) {
Reflect.apply(Reflect.get(this.entity, 'hookAfterNativeCall'), this.entity, []);
}
}
callNative(namespace, method, args) {
const callbackId = uniqueId('callback');
return new Promise((resolve, reject) => {
this.callbacks.set(callbackId, {
resolve,
reject,
});
nativeBridge(this.id, namespace, method, callbackId, args);
});
}
register(instance) {
this.entity = instance;
}
function2Id(func) {
const functionId = uniqueId('function');
this.callbacks.set(functionId, {
resolve: func,
reject: () => { loge("This should not be called"); }
});
return functionId;
}
removeFuncById(funcId) {
this.callbacks.delete(funcId);
}
}
const gContexts = new Map;
const gModules = new Map;
export function allContexts() {
return gContexts.values();
}
export function jsObtainContext(id) {
if (gContexts.has(id)) {
const context = gContexts.get(id);
setContext(context);
return context;
}
else {
const context = new Context(id);
gContexts.set(id, context);
setContext(context);
return context;
}
}
export function jsReleaseContext(id) {
const context = gContexts.get(id);
const args = arguments;
if (context) {
timerInfos.forEach((v, k) => {
if (v.context === context) {
if (global.nativeClearTimer === undefined) {
return Reflect.apply(_clearTimeout, undefined, args);
}
timerInfos.delete(k);
nativeClearTimer(k);
}
});
}
gContexts.delete(id);
}
export function __require__(name) {
if (gModules.has(name)) {
return gModules.get(name);
}
else {
if (nativeRequire(name)) {
return gModules.get(name);
}
else {
return undefined;
}
}
}
export function jsRegisterModule(name, moduleObject) {
gModules.set(name, moduleObject);
}
export function jsCallEntityMethod(contextId, methodName, args) {
const context = gContexts.get(contextId);
if (context === undefined) {
loge(`Cannot find context for context id:${contextId}`);
return;
}
if (context.entity === undefined) {
loge(`Cannot find holder for context id:${contextId}`);
return;
}
if (Reflect.has(context.entity, methodName)) {
const argumentsList = [];
for (let i = 2; i < arguments.length; i++) {
argumentsList.push(arguments[i]);
}
hookBeforeNativeCall(context);
const ret = Reflect.apply(Reflect.get(context.entity, methodName), context.entity, argumentsList);
return ret;
}
else {
loge(`Cannot find method for context id:${contextId},method name is:${methodName}`);
}
}
export function pureCallEntityMethod(contextId, methodName, args) {
const context = gContexts.get(contextId);
if (context === undefined) {
loge(`Cannot find context for context id:${contextId}`);
return;
}
if (context.entity === undefined) {
loge(`Cannot find holder for context id:${contextId}`);
return;
}
if (Reflect.has(context.entity, methodName)) {
const argumentsList = [];
for (let i = 2; i < arguments.length; i++) {
argumentsList.push(arguments[i]);
}
return Reflect.apply(Reflect.get(context.entity, methodName), context.entity, argumentsList);
}
else {
loge(`Cannot find method for context id:${contextId},method name is:${methodName}`);
}
}
export function jsObtainEntry(contextId) {
const context = jsObtainContext(contextId);
const exportFunc = (constructor) => {
context === null || context === void 0 ? void 0 : context.classes.set(constructor.name, constructor);
const ret = new constructor;
Reflect.set(ret, 'context', context);
context === null || context === void 0 ? void 0 : context.register(ret);
return constructor;
};
return function () {
if (arguments.length === 1) {
const args = arguments[0];
if (args instanceof Array) {
args.forEach(clz => {
context === null || context === void 0 ? void 0 : context.classes.set(clz.name, clz);
});
return exportFunc;
}
else {
return exportFunc(args);
}
}
else if (arguments.length === 2) {
const srcContextId = arguments[0];
const className = arguments[1];
const srcContext = gContexts.get(srcContextId);
if (srcContext) {
srcContext.classes.forEach((v, k) => {
context === null || context === void 0 ? void 0 : context.classes.set(k, v);
});
const clz = srcContext.classes.get(className);
if (clz) {
return exportFunc(clz);
}
else {
throw new Error(`Cannot find class:${className} in context:${srcContextId}`);
}
}
else {
throw new Error(`Cannot find context for ${srcContextId}`);
}
}
else {
throw new Error(`Entry arguments error:${arguments}`);
}
};
}
const global = Function('return this')();
let __timerId__ = 1;
const timerInfos = new Map;
const _setTimeout = global.setTimeout;
const _setInterval = global.setInterval;
const _clearTimeout = global.clearTimeout;
const _clearInterval = global.clearInterval;
const doricSetTimeout = function (handler, timeout, ...args) {
if (global.nativeSetTimer === undefined) {
return Reflect.apply(_setTimeout, undefined, arguments);
}
const id = __timerId__++;
timerInfos.set(id, {
callback: () => {
Reflect.apply(handler, undefined, args);
timerInfos.delete(id);
},
context: getContext(),
});
nativeSetTimer(id, timeout || 0, false);
return id;
};
const doricSetInterval = function (handler, timeout, ...args) {
if (global.nativeSetTimer === undefined) {
return Reflect.apply(_setInterval, undefined, arguments);
}
const id = __timerId__++;
timerInfos.set(id, {
callback: () => {
Reflect.apply(handler, undefined, args);
},
context: getContext(),
});
nativeSetTimer(id, timeout || 0, true);
return id;
};
const doricClearTimeout = function (timerId) {
if (global.nativeClearTimer === undefined) {
return Reflect.apply(_clearTimeout, undefined, arguments);
}
timerInfos.delete(timerId);
nativeClearTimer(timerId);
};
const doricClearInterval = function (timerId) {
if (global.nativeClearTimer === undefined) {
return Reflect.apply(_clearInterval, undefined, arguments);
}
timerInfos.delete(timerId);
nativeClearTimer(timerId);
};
if (!global.setTimeout) {
global.setTimeout = doricSetTimeout;
}
else {
global.doricSetTimeout = doricSetTimeout;
}
if (!global.setInterval) {
global.setInterval = doricSetInterval;
}
else {
global.doricSetInterval = doricSetInterval;
}
if (!global.clearTimeout) {
global.clearTimeout = doricClearTimeout;
}
else {
global.doricClearTimeout = doricClearTimeout;
}
if (!global.clearInterval) {
global.clearInterval = doricClearInterval;
}
else {
global.doricClearInterval = doricClearInterval;
}
export function jsCallbackTimer(timerId) {
const timerInfo = timerInfos.get(timerId);
if (timerInfo === undefined) {
return;
}
if (timerInfo.callback instanceof Function) {
setContext(timerInfo.context);
hookBeforeNativeCall(timerInfo.context);
Reflect.apply(timerInfo.callback, timerInfo.context, []);
}
}
export function jsHookAfterNativeCall() {
const context = getContext();
context === null || context === void 0 ? void 0 : context.hookAfterNativeCall();
}