312 lines
12 KiB
JavaScript
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));
|