Files
MingSimulator/Content/JavaScript/puerts/callable.js
T

312 lines
12 KiB
JavaScript

/*
* Tencent is pleased to support the open source community by making Puerts available.
* Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved.
* Puerts is licensed under the BSD 3-Clause License, except for the third-party components listed in the file 'LICENSE' which may be subject to their corresponding license terms.
* This file is subject to the terms and conditions defined in file 'LICENSE', which is part of this source code package.
*/
var global = global || (function () { return this; }());
(function (global) {
"use strict";
let sendRequestSync = puerts.sendRequestSync;
function getClassInfo(className) {
let classInfo = sendRequestSync('getClassInfo', className);
return JSON.parse(classInfo);
}
function ref(x) {
return {value:x};
}
function unref(r) {
return r.value;
}
function ret(o) {
return o.ReturnValue;
}
function retWithWrap(o){
return wrapUObject(o.ReturnValue);
}
function retWithAsyncWrap(o){
return wrapUObject(o.ReturnValue, true);
}
function id(x) {
return x;
}
function makeArgumentUnpack(paramInfo) {
if (paramInfo.isOut && paramInfo.isUObject) {
return x => ref(wrapUObject(x));
} else if(paramInfo.isOut) {
return ref;
} else if (paramInfo.isUObject) {
return wrapUObject;
} else {
return id;
}
}
function wrapCallback(functionInfo, callback) {
let paramsInfo = functionInfo.parameters;
paramsInfo.forEach( paramInfo => paramInfo.unpack = makeArgumentUnpack(paramInfo));
return (argsObj) => {
let args = paramsInfo.map(paramInfo => paramInfo.unpack(argsObj[paramInfo.name]));
callback.apply(null, args);
};
}
let registerCallbacks = Object.create(null);
var allocedCallbackId = 0;
function registerCallback(callback) {
let callbackId = allocedCallbackId++;
registerCallbacks[callbackId] = callback;
return callbackId;
}
function unregisterCallback(callbackId) {
registerCallbacks[callbackId] = undefined;
}
let wrapGenerators = Object.create(null);
class Delegate {
constructor(objectId, propertyName, propertyType) {
this.objectId = objectId;
this.propertyName = propertyName;
this.propertyType = propertyType;
}
Bind(func) {
if (typeof func !== 'function') {
throw new Error('function needed!');
}
this.Unbind();
let callbackId = registerCallback(wrapCallback(this.propertyType, func));
sendRequestSync('bindDelegate', this.objectId + '#' + this.propertyName + '#' + callbackId);
this.bindedCallbackId = callbackId;
}
Unbind() {
if (this.bindedCallbackId) {
sendRequestSync('unbindDelegate', this.bindedCallbackId);
unregisterCallback(this.bindedCallbackId);
this.bindedCallbackId = undefined;
}
}
}
class MulticastDelegate {
constructor(objectId, propertyName, propertyType) {
this.objectId = objectId;
this.propertyName = propertyName;
this.propertyType = propertyType;
}
Add(func) {
if (typeof func !== 'function') {
throw new Error('function needed!');
}
let callbackId = registerCallback(wrapCallback(this.propertyType, func));
sendRequestSync('bindDelegate', this.objectId + '#' + this.propertyName + '#' + callbackId);
return callbackId;
}
Remove(callbackId) {
sendRequestSync('unbindDelegate', callbackId);
unregisterCallback(callbackId);
}
}
let pendingAsyncCall = [];
function getWrapGenerator(className, optClassInfo) {
if (!(className in wrapGenerators)) {
let classInfo = optClassInfo || getClassInfo(className);
wrapGenerators[className] = (objectId, isClass, isAsync) => {
let target = {$functions: classInfo.functions, $id:objectId, toJSON:() => objectId, $toAsync:() => getWrapGenerator(className, optClassInfo)(objectId, true, true)};
if (!isClass) {
for (var propertyName in classInfo.delegates) {
target[propertyName] = new Delegate(objectId, propertyName, classInfo.delegates[propertyName]);
}
for (var propertyName in classInfo.multicasts) {
target[propertyName] = new MulticastDelegate(objectId, propertyName, classInfo.multicasts[propertyName]);
}
} else {
target["StaticClass"] = () => classInfo.pathName;
}
return new Proxy(target, {
get: function(cfg, name) {
if (!(name in cfg)) {
let functionInfo = cfg.$functions[name];
let id = cfg.$id
//之前抛异常不太适合,传统js是duck typing,有时会通过是否有某函数判断能力。
//比如,promise里resolve传入的对象,thenable对象是通过是否有then函数来决定的,
//所以异步返回,resolve传入wrap的uobject时,会抛无then函数的异常;
if (!functionInfo) return;
let paramsInfo = functionInfo.parameters;
let r = functionInfo.isUObject ? (isAsync ? retWithAsyncWrap : retWithWrap) : ret;
cfg[name] = isAsync ? function() {
let argsObj = {};
paramsInfo.forEach((paramInfo, idx) => argsObj[paramInfo.name] = paramInfo.isOut ? arguments[idx].value : arguments[idx]);
let argsString = JSON.stringify(argsObj);
let outterArgs = arguments;
return new Promise(function(resolve, reject) {
let args = id + '#' + name + '#' + argsString;
pendingAsyncCall.push({args: args.length + 'A' + args, resolve: res => {
paramsInfo.forEach((paramInfo, idx) => {
if(paramInfo.isOut) {
outterArgs[idx].value = paramInfo.isUObject ? wrapUObject(res[paramInfo.name]) :res[paramInfo.name];
}
});
resolve(r(res));
}, reject: reject});
});
} : function() {
let argsObj = {};
paramsInfo.forEach((paramInfo, idx) => argsObj[paramInfo.name] = paramInfo.isOut ? arguments[idx].value : arguments[idx]);
let argsString = JSON.stringify(argsObj);
//puerts.console.log('id:' + id + ',method:' + name + ',args:' + argsString);
let res = JSON.parse(sendRequestSync('callUObjectMethod', id + '#' + name + '#' + argsString));
paramsInfo.forEach((paramInfo, idx) => {
if(paramInfo.isOut) {
arguments[idx].value = paramInfo.isUObject ? wrapUObject(res[paramInfo.name]) :res[paramInfo.name];
}
});
return r(res);
}
}
return cfg[name];
},
set: function(cfg, name, value) {
throw new Error('set not allow for ' + name);
}
});
};
}
return wrapGenerators[className];
}
function wrapUObject(objectId, isAsync) {
if (objectId === "0000000000000000") return null;
let className = objectId.substring(objectId.indexOf('-') + 1);
return getWrapGenerator(className)(objectId, false, isAsync);
}
let UE = new Proxy(Object.create(null), {
get: function(classWrapers, name) {
if (!(name in classWrapers)) {
let classInfo = getClassInfo(name);
classWrapers[name] = getWrapGenerator(name, classInfo)(classInfo.pathName, true);
}
return classWrapers[name];
}
});
puerts.registerRequestHandler('invokeCallback', (msg, onFinished) => {
let split = msg.indexOf('#');
let callbackId = msg.substring(0, split);
let cb = registerCallbacks[callbackId];
if (cb) {
cb(JSON.parse(msg.substring(split + 1)));
onFinished("");
} else {
onFinished(undefined, "no callback " + callbackId);
}
});
puerts.registerRequestHandler('onCallbackRelease', callbackId => {
registerCallbacks[callbackId] = undefined;
});
var mainObject = undefined;
function getMainObject() {
if (!mainObject) {
mainObject = wrapUObject(sendRequestSync('getMainObjectId', ''));
}
return mainObject;
}
function getProperties(obj, ...fields) {
let args = (typeof obj === 'string') ? obj : obj.toJSON();
if (fields.length > 0) {
args += ("#" + fields.join('#'));
}
return JSON.parse(sendRequestSync('getProperties', args));
}
function getPropertiesAsync(obj, ...fields) {
let args = (typeof obj === 'string') ? obj : obj.toJSON();
if (fields.length > 0) {
args += ("#" + fields.join('#'));
}
return new Promise(function(resolve, reject) {
pendingAsyncCall.push({args: args.length + 'C' + args, resolve: resolve, reject: reject});
});
}
function setProperties(obj, properties) {
let objectId = (typeof obj === 'string') ? obj : obj.toJSON();
sendRequestSync('setProperties', objectId + '#' +JSON.stringify(properties));
}
function setPropertiesAsync(obj, properties) {
let objectId = (typeof obj === 'string') ? obj : obj.toJSON();
let args = objectId + '#' +JSON.stringify(properties);
return new Promise(function(resolve, reject) {
pendingAsyncCall.push({args: args.length + 'B' + args, resolve: resolve, reject: reject});
});
}
function flushAsyncCall(trace) {
if (pendingAsyncCall.length == 0) return 0;
let sliceCalls = pendingAsyncCall;
let batchArgs = sliceCalls.map(x =>x.args).join('');
if (trace) {
puerts.console.log(sliceCalls.map(x =>x.args).join('\n'))
}
pendingAsyncCall = [];
let batchResult = sendRequestSync('batchCall', batchArgs);
let callIndex = 0;
let split1 = 0;
let split2;
while((split2 = batchResult.indexOf('#', split1)) >= 0) {
let len = parseInt(batchResult.slice(split1, split2));
let result = batchResult.substr(split2 + 1, len);
if (result.startsWith('#')) {
let r = result.substr(1);
if (r == '') {
sliceCalls[callIndex].resolve();
} else {
sliceCalls[callIndex].resolve(JSON.parse(result.substr(1)));
}
} else {
sliceCalls[callIndex].reject(new Error(result));
}
++callIndex;
split1 = split2 + 1 + len;
}
return sliceCalls.length
}
puerts.getMainObject = getMainObject;
puerts.getProperties = getProperties;
puerts.getPropertiesAsync = getPropertiesAsync;
puerts.setProperties = setProperties;
puerts.setPropertiesAsync = setPropertiesAsync;
puerts.flushAsyncCall = flushAsyncCall;
puerts.$ref = ref;
puerts.$unref = unref;
puerts.$async = x => x.$toAsync();
puerts.registerBuildinModule('ue', UE);
}(global));