提交 45a8193d 编写于 作者: fxy060608's avatar fxy060608

wip(uts): compiler

上级 a41a8adf
import Log from 'android.util.Log'
import { login } from './login.uts' import { login } from './login.uts'
export class User { export class User {
async login(name: string, pwd: string) { async login(name: string, pwd: string) {
login(name, pwd) login(name, pwd)
Log.info('123')
} }
} }
package uts.modules.testUniPlugin;
import kotlinx.coroutines.*;
import io.dcloud.uts.runtime.*;
import android.util.Log;
fun login(name: String, pwd: String): UtsJSONObject { fun login(name: String, pwd: String): UtsJSONObject {
return object : UtsJSONObject() { return object : UtsJSONObject() {
var name = name var name = name
...@@ -5,7 +9,8 @@ fun login(name: String, pwd: String): UtsJSONObject { ...@@ -5,7 +9,8 @@ fun login(name: String, pwd: String): UtsJSONObject {
}; };
} }
open class User { open class User {
open fun async login(name: String, pwd: String) { open suspend fun login(name: String, pwd: String) = CoroutineScope(Dispatchers.Default).async {
login(name, pwd); login(name, pwd);
Log.info("123");
} }
} }
{"version":3,"sources":["/Users/fxy/Projects/GitHub/uni-app/uni-app-next/packages/playground/uts/uni_modules/test-uniplugin/app-android/login.uts","/Users/fxy/Projects/GitHub/uni-app/uni-app-next/packages/playground/uts/uni_modules/test-uniplugin/app-android/index.uts"],"sourcesContent":["export function login(name: string, pwd: string) {\n return { name, pwd }\n}\n","import { login } from './login.uts'\nexport class User {\n async login(name: string, pwd: string) {\n login(name, pwd)\n }\n}\n"],"names":[],"mappings":"AAAO,IAAS,KAAK,CAAC,IAAY,EAAN,MAAM,EAAE,GAAW,EAAN,MAAM,iBAAE;IAC/C,OAAO;QAAE,IAAA,IAAI,GAAJ,IAAI;QAAE,IAAA,GAAG,GAAH,GAAG;KAAE,CAAA;;ACAf,WAAM,IAAI;IACf,eAAM,KAAK,CAAC,IAAY,EAAN,MAAM,EAAE,GAAW,EAAN,MAAM,EAAE;QACrC,MAAM,IAAI,EAAE,GAAG,CAAC;;CAEnB"} {"version":3,"sources":["/Users/fxy/Projects/GitHub/uni-app/uni-app-next/packages/playground/uts/uni_modules/test-uniplugin/app-android/index.uts","/Users/fxy/Projects/GitHub/uni-app/uni-app-next/packages/playground/uts/uni_modules/test-uniplugin/app-android/login.uts"],"sourcesContent":["import Log from 'android.util.Log'\nimport { login } from './login.uts'\nexport class User {\n async login(name: string, pwd: string) {\n login(name, pwd)\n Log.info('123')\n }\n}\n","export function login(name: string, pwd: string) {\n return { name, pwd }\n}\n"],"names":[],"mappings":";;;AAAA,OAAgB,gBAAkB,CAAA;ACA3B,IAAS,KAAK,CAAC,IAAY,EAAN,MAAM,EAAE,GAAW,EAAN,MAAM,iBAAE;IAC/C,OAAO;QAAE,IAAA,IAAI,GAAJ,IAAI;QAAE,IAAA,GAAG,GAAH,GAAG;KAAE,CAAA;;ADCf,WAAM,IAAI;IACf,iBAAM,KAAK,CAAC,IAAY,EAAN,MAAM,EAAE,GAAW,EAAN,MAAM,8CAAE;QACrC,MAAM,IAAI,EAAE,GAAG,CAAC;QAChB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC;;CAElB"}
\ No newline at end of file \ No newline at end of file
...@@ -11453,6 +11453,7 @@ const ScanCodeProtocol = { ...@@ -11453,6 +11453,7 @@ const ScanCodeProtocol = {
scanType: Array, scanType: Array,
autoDecodeCharSet: Boolean, autoDecodeCharSet: Boolean,
sound: String, sound: String,
autoZoom: Boolean,
}; };
const SOUND = ['default', 'none']; const SOUND = ['default', 'none'];
const ScanCodeOptions = { const ScanCodeOptions = {
...@@ -11461,6 +11462,10 @@ const ScanCodeOptions = { ...@@ -11461,6 +11462,10 @@ const ScanCodeOptions = {
if (!SOUND.includes(value)) if (!SOUND.includes(value))
params.sound = 'none'; params.sound = 'none';
}, },
autoZoom(value, params) {
if (typeof value === 'undefined')
params.autoZoom = true;
},
}, },
}; };
......
...@@ -39,11 +39,10 @@ describe('uts-module', () => { ...@@ -39,11 +39,10 @@ describe('uts-module', () => {
}) })
test(`initProxyFunction`, () => { test(`initProxyFunction`, () => {
;[true, false].forEach((async) => { ;[true, false].forEach((async) => {
const preparePermission = initUtsProxyFunction({ const preparePermission = initUtsProxyFunction(async, {
pkg: 'testPlugin', package: 'uts.modules.TestPlugin',
cls: '', class: 'TestKt',
method: 'preparePermission', name: 'preparePermission',
async,
}) })
/** /**
* {"package":"testPlugin","class":"","method":"preparePermission","params":[{"name":"foo","age":10,"success":7,"fail":8},9]} * {"package":"testPlugin","class":"","method":"preparePermission","params":[{"name":"foo","age":10,"success":7,"fail":8},9]}
...@@ -71,13 +70,23 @@ describe('uts-module', () => { ...@@ -71,13 +70,23 @@ describe('uts-module', () => {
}) })
test(`initProxyClass`, () => { test(`initProxyClass`, () => {
const WifiManager = initUtsProxyClass({ const WifiManager = initUtsProxyClass({
pkg: 'testPlugin', package: 'uni.modules.TestPlugin',
cls: 'WifiManager', class: 'WifiManager',
methods: { methods: {
preparePermission: {}, preparePermission: {},
}, },
staticMethods: {
staticPreparePermission: {
async: true,
},
},
props: ['count'],
staticProps: ['staticCount'],
}) })
const wifi = new WifiManager() const wifi = new WifiManager()
wifi.preparePermission(1, 2, 3, () => {}) wifi.preparePermission(1, 2, 3, () => {})
wifi.count
wifi.staticCount
wifi.staticPreparePermission(1)
}) })
}) })
...@@ -151,10 +151,19 @@ function normalizeArg(arg) { ...@@ -151,10 +151,19 @@ function normalizeArg(arg) {
} }
return arg; return arg;
} }
function isProxyInvokeCallbackResponse(res) { function initUtsInstanceMethod(async, opts) {
return !!res.name; return initProxyFunction(async, opts);
} }
function initUtsProxyFunction({ pkg, cls, method, async, }) { function getProxy() {
if (!proxy) {
proxy = uni.requireNativePlugin('UTS-Proxy');
}
return proxy;
}
function invokePropGetter(args) {
return getProxy().invokeSync(args, () => { });
}
function initProxyFunction(async, { package: pkg, class: cls, name: propOrMethod, id: instanceId, }) {
const invokeCallback = ({ id, name, params, keepAlive, }) => { const invokeCallback = ({ id, name, params, keepAlive, }) => {
const callback = callbacks[id]; const callback = callbacks[id];
if (callback) { if (callback) {
...@@ -164,19 +173,24 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) { ...@@ -164,19 +173,24 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) {
} }
} }
else { else {
console.error(`${pkg}${cls ? '.' + cls : ''}.${method} ${name} is not found`); console.error(`${pkg}${cls}.${propOrMethod} ${name} is not found`);
} }
}; };
const baseArgs = instanceId
? { id: instanceId, name: propOrMethod }
: {
package: pkg,
class: cls,
name: propOrMethod,
};
return (...args) => { return (...args) => {
if (!proxy) { const invokeArgs = shared.extend({}, baseArgs, {
proxy = uni.requireNativePlugin('ProxyModule'); params: args.map((arg) => normalizeArg(arg)),
} });
const params = args.map((arg) => normalizeArg(arg));
const invokeArgs = { package: pkg, class: cls, method, params };
if (async) { if (async) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
proxy.invokeAsync(invokeArgs, (res) => { getProxy().invokeAsync(invokeArgs, (res) => {
if (isProxyInvokeCallbackResponse(res)) { if (res.type !== 'return') {
invokeCallback(res); invokeCallback(res);
} }
else { else {
...@@ -190,26 +204,47 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) { ...@@ -190,26 +204,47 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) {
}); });
}); });
} }
return proxy.invokeSync(invokeArgs, invokeCallback); return getProxy().invokeSync(invokeArgs, invokeCallback);
}; };
} }
function initUtsProxyClass({ pkg, cls, methods, }) { function initUtsStaticMethod(async, opts) {
return initProxyFunction(async, opts);
}
const initUtsProxyFunction = initUtsStaticMethod;
function initUtsProxyClass({ package: pkg, class: cls, methods, props, staticProps, staticMethods, }) {
const baseOptions = {
package: pkg,
class: cls,
};
return class ProxyClass { return class ProxyClass {
constructor() { constructor(...params) {
const target = {}; const target = {};
// 初始化实例 ID
const instanceId = initProxyFunction(false, shared.extend({ name: 'constructor', params }, baseOptions)).apply(null, params);
return new Proxy(this, { return new Proxy(this, {
get(_, method) { get(_, name) {
if (!target[method]) { if (!target[name]) {
if (shared.hasOwn(methods, method)) { //实例方法
target[method] = initUtsProxyFunction({ if (shared.hasOwn(methods, name)) {
pkg, target[name] = initUtsInstanceMethod(!!methods[name].async, shared.extend({
cls, id: instanceId,
method, name,
async: methods[method].async, }, baseOptions));
}); }
else if (shared.hasOwn(staticMethods, name)) {
// 静态方法
target[name] = initUtsStaticMethod(!!staticMethods[name].async, shared.extend({ name }, baseOptions));
}
else if (props.includes(name)) {
// 实例属性
return invokePropGetter({ id: instanceId, name: name });
}
else if (staticProps.includes(name)) {
// 静态属性
return invokePropGetter(shared.extend({ name: name }, baseOptions));
} }
return target[method];
} }
return target[name];
}, },
}); });
} }
......
...@@ -35,9 +35,11 @@ export declare function getCurrentSubNVue(): any; ...@@ -35,9 +35,11 @@ export declare function getCurrentSubNVue(): any;
export declare function getSsrGlobalData(): any; export declare function getSsrGlobalData(): any;
export declare function initUtsProxyClass({ pkg, cls, methods, }: ProxyClassOptions): any; export declare function initUtsProxyClass({ package: pkg, class: cls, methods, props, staticProps, staticMethods, }: ProxyClassOptions): any;
export declare function initUtsProxyFunction({ pkg, cls, method, async, }: ProxyFunctionOptions): (...args: unknown[]) => any; export declare const initUtsProxyFunction: typeof initUtsStaticMethod;
declare function initUtsStaticMethod(async: boolean, opts: ProxyBaseOptions): (...args: unknown[]) => unknown;
declare type LaunchOption = LaunchShowOption; declare type LaunchOption = LaunchShowOption;
...@@ -166,23 +168,35 @@ declare interface PageScrollOption { ...@@ -166,23 +168,35 @@ declare interface PageScrollOption {
} }
declare interface ProxyBaseOptions { declare interface ProxyBaseOptions {
pkg: string; /**
cls: string; * 包名
method: string; */
package: string;
/**
* 类名
*/
class: string;
/**
* 属性名或方法名
*/
name: string;
} }
declare interface ProxyClassOptions { declare interface ProxyClassOptions {
pkg: string; package: string;
cls: string; class: string;
props: string[];
staticProps: string[];
methods: { methods: {
[name: string]: { [name: string]: {
async?: boolean; async?: boolean;
}; };
}; };
} staticMethods: {
[name: string]: {
declare interface ProxyFunctionOptions extends ProxyBaseOptions { async?: boolean;
async?: boolean; };
};
} }
declare interface ReferrerInfo { declare interface ReferrerInfo {
......
import { shallowRef, ref, getCurrentInstance, isInSSRComponentSetup, injectHook } from 'vue'; import { shallowRef, ref, getCurrentInstance, isInSSRComponentSetup, injectHook } from 'vue';
import { hasOwn, isString, isPlainObject } from '@vue/shared'; import { hasOwn, isString, extend, isPlainObject } from '@vue/shared';
import { sanitise, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR, ON_SHOW, ON_HIDE, ON_LAUNCH, ON_ERROR, ON_THEME_CHANGE, ON_PAGE_NOT_FOUND, ON_UNHANDLE_REJECTION, ON_INIT, ON_LOAD, ON_READY, ON_UNLOAD, ON_RESIZE, ON_BACK_PRESS, ON_PAGE_SCROLL, ON_TAB_ITEM_TAP, ON_REACH_BOTTOM, ON_PULL_DOWN_REFRESH, ON_SAVE_EXIT_STATE, ON_SHARE_TIMELINE, ON_ADD_TO_FAVORITES, ON_SHARE_APP_MESSAGE, ON_NAVIGATION_BAR_BUTTON_TAP, ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED, ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED, ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED, ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED } from '@dcloudio/uni-shared'; import { sanitise, UNI_SSR_DATA, UNI_SSR_GLOBAL_DATA, UNI_SSR, ON_SHOW, ON_HIDE, ON_LAUNCH, ON_ERROR, ON_THEME_CHANGE, ON_PAGE_NOT_FOUND, ON_UNHANDLE_REJECTION, ON_INIT, ON_LOAD, ON_READY, ON_UNLOAD, ON_RESIZE, ON_BACK_PRESS, ON_PAGE_SCROLL, ON_TAB_ITEM_TAP, ON_REACH_BOTTOM, ON_PULL_DOWN_REFRESH, ON_SAVE_EXIT_STATE, ON_SHARE_TIMELINE, ON_ADD_TO_FAVORITES, ON_SHARE_APP_MESSAGE, ON_NAVIGATION_BAR_BUTTON_TAP, ON_NAVIGATION_BAR_SEARCH_INPUT_CHANGED, ON_NAVIGATION_BAR_SEARCH_INPUT_CLICKED, ON_NAVIGATION_BAR_SEARCH_INPUT_CONFIRMED, ON_NAVIGATION_BAR_SEARCH_INPUT_FOCUS_CHANGED } from '@dcloudio/uni-shared';
function getSSRDataType() { function getSSRDataType() {
...@@ -119,10 +119,19 @@ function normalizeArg(arg) { ...@@ -119,10 +119,19 @@ function normalizeArg(arg) {
} }
return arg; return arg;
} }
function isProxyInvokeCallbackResponse(res) { function initUtsInstanceMethod(async, opts) {
return !!res.name; return initProxyFunction(async, opts);
} }
function initUtsProxyFunction({ pkg, cls, method, async, }) { function getProxy() {
if (!proxy) {
proxy = uni.requireNativePlugin('UTS-Proxy');
}
return proxy;
}
function invokePropGetter(args) {
return getProxy().invokeSync(args, () => { });
}
function initProxyFunction(async, { package: pkg, class: cls, name: propOrMethod, id: instanceId, }) {
const invokeCallback = ({ id, name, params, keepAlive, }) => { const invokeCallback = ({ id, name, params, keepAlive, }) => {
const callback = callbacks[id]; const callback = callbacks[id];
if (callback) { if (callback) {
...@@ -132,19 +141,24 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) { ...@@ -132,19 +141,24 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) {
} }
} }
else { else {
console.error(`${pkg}${cls ? '.' + cls : ''}.${method} ${name} is not found`); console.error(`${pkg}${cls}.${propOrMethod} ${name} is not found`);
} }
}; };
const baseArgs = instanceId
? { id: instanceId, name: propOrMethod }
: {
package: pkg,
class: cls,
name: propOrMethod,
};
return (...args) => { return (...args) => {
if (!proxy) { const invokeArgs = extend({}, baseArgs, {
proxy = uni.requireNativePlugin('ProxyModule'); params: args.map((arg) => normalizeArg(arg)),
} });
const params = args.map((arg) => normalizeArg(arg));
const invokeArgs = { package: pkg, class: cls, method, params };
if (async) { if (async) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
proxy.invokeAsync(invokeArgs, (res) => { getProxy().invokeAsync(invokeArgs, (res) => {
if (isProxyInvokeCallbackResponse(res)) { if (res.type !== 'return') {
invokeCallback(res); invokeCallback(res);
} }
else { else {
...@@ -158,26 +172,47 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) { ...@@ -158,26 +172,47 @@ function initUtsProxyFunction({ pkg, cls, method, async, }) {
}); });
}); });
} }
return proxy.invokeSync(invokeArgs, invokeCallback); return getProxy().invokeSync(invokeArgs, invokeCallback);
}; };
} }
function initUtsProxyClass({ pkg, cls, methods, }) { function initUtsStaticMethod(async, opts) {
return initProxyFunction(async, opts);
}
const initUtsProxyFunction = initUtsStaticMethod;
function initUtsProxyClass({ package: pkg, class: cls, methods, props, staticProps, staticMethods, }) {
const baseOptions = {
package: pkg,
class: cls,
};
return class ProxyClass { return class ProxyClass {
constructor() { constructor(...params) {
const target = {}; const target = {};
// 初始化实例 ID
const instanceId = initProxyFunction(false, extend({ name: 'constructor', params }, baseOptions)).apply(null, params);
return new Proxy(this, { return new Proxy(this, {
get(_, method) { get(_, name) {
if (!target[method]) { if (!target[name]) {
if (hasOwn(methods, method)) { //实例方法
target[method] = initUtsProxyFunction({ if (hasOwn(methods, name)) {
pkg, target[name] = initUtsInstanceMethod(!!methods[name].async, extend({
cls, id: instanceId,
method, name,
async: methods[method].async, }, baseOptions));
}); }
else if (hasOwn(staticMethods, name)) {
// 静态方法
target[name] = initUtsStaticMethod(!!staticMethods[name].async, extend({ name }, baseOptions));
}
else if (props.includes(name)) {
// 实例属性
return invokePropGetter({ id: instanceId, name: name });
}
else if (staticProps.includes(name)) {
// 静态属性
return invokePropGetter(extend({ name: name }, baseOptions));
} }
return target[method];
} }
return target[name];
}, },
}); });
} }
......
import { isPlainObject, hasOwn } from '@vue/shared' import { isPlainObject, hasOwn, extend } from '@vue/shared'
declare const uni: any declare const uni: any
let callbackId = 1 let callbackId = 1
let proxy: any let proxy: any
...@@ -15,78 +15,154 @@ export function normalizeArg(arg: unknown) { ...@@ -15,78 +15,154 @@ export function normalizeArg(arg: unknown) {
} }
return arg return arg
} }
interface ProxyInvokeAsyncResponse {
errMsg: string interface ProxyBaseOptions {
params: unknown /**
* 包名
*/
package: string
/**
* 类名
*/
class: string
/**
* 属性名或方法名
*/
name: string
}
interface ProxyInstanceOptions extends ProxyBaseOptions {
id: number
}
/**
* 实例方法
*/
interface ProxyInstanceMethodOptions extends ProxyInstanceOptions {}
function initUtsInstanceMethod(
async: boolean,
opts: ProxyInstanceMethodOptions
) {
return initProxyFunction(async, opts)
}
interface ProxyClassOptions {
package: string
class: string
props: string[]
staticProps: string[]
methods: {
[name: string]: {
async?: boolean
}
}
staticMethods: {
[name: string]: {
async?: boolean
}
}
}
type InvokeInstanceArgs =
// prop
| { id: number; name: string }
// method
| { id: number; name: string; params: unknown[] }
type InvokeArgs = (ProxyBaseOptions | InvokeInstanceArgs) & {
params?: unknown[]
} }
interface ProxyInvokeCallbackResponse {
interface InvokeCallbackReturnRes {
type: 'return'
params?: unknown[]
errMsg?: string
}
interface InvokeCallbackParamsRes {
type: 'params'
id: number id: number
name: string name: string
params: unknown[] params: unknown[]
keepAlive: boolean keepAlive?: boolean
} }
type ProxyInvokeResponse =
| ProxyInvokeAsyncResponse
| ProxyInvokeCallbackResponse
function isProxyInvokeCallbackResponse( type InvokeSyncCallback = (res: InvokeCallbackParamsRes) => void
res: ProxyInvokeResponse type InvokeAsyncCallback = (
): res is ProxyInvokeCallbackResponse { res: InvokeCallbackReturnRes | InvokeCallbackParamsRes
return !!(res as ProxyInvokeCallbackResponse).name ) => void
}
interface ProxyBaseOptions { function getProxy(): {
pkg: string invokeSync: (args: InvokeArgs, callback: InvokeSyncCallback) => unknown
cls: string invokeAsync: (args: InvokeArgs, callback: InvokeAsyncCallback) => void
method: string } {
if (!proxy) {
proxy = uni.requireNativePlugin('UTS-Proxy') as any
}
return proxy
} }
interface ProxyFunctionOptions extends ProxyBaseOptions { function invokePropGetter(args: InvokeArgs) {
async?: boolean return getProxy().invokeSync(args, () => {})
} }
interface InvokeArgs { interface InitProxyFunctionOptions {
/**
* 包名
*/
package: string package: string
/**
* 类名
*/
class: string class: string
method: string /**
params: unknown[] * 属性名或方法名
*/
name: string
/**
* 实例 ID
*/
id?: number
} }
export function initUtsProxyFunction({ function initProxyFunction(
pkg, async: boolean,
cls, {
method, package: pkg,
async, class: cls,
}: ProxyFunctionOptions) { name: propOrMethod,
id: instanceId,
}: InitProxyFunctionOptions
) {
const invokeCallback = ({ const invokeCallback = ({
id, id,
name, name,
params, params,
keepAlive, keepAlive,
}: ProxyInvokeCallbackResponse) => { }: InvokeCallbackParamsRes) => {
const callback = callbacks[id] const callback = callbacks[id!]
if (callback) { if (callback) {
callback(...params) callback(...params)
if (!keepAlive) { if (!keepAlive) {
delete callbacks[id] delete callbacks[id]
} }
} else { } else {
console.error( console.error(`${pkg}${cls}.${propOrMethod} ${name} is not found`)
`${pkg}${cls ? '.' + cls : ''}.${method} ${name} is not found`
)
} }
} }
const baseArgs: InvokeArgs = instanceId
? { id: instanceId, name: propOrMethod }
: {
package: pkg,
class: cls,
name: propOrMethod,
}
return (...args: unknown[]) => { return (...args: unknown[]) => {
if (!proxy) { const invokeArgs = extend({}, baseArgs, {
proxy = uni.requireNativePlugin('ProxyModule') as any params: args.map((arg) => normalizeArg(arg)),
} })
const params = args.map((arg) => normalizeArg(arg))
const invokeArgs: InvokeArgs = { package: pkg, class: cls, method, params }
if (async) { if (async) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
proxy.invokeAsync(invokeArgs, (res: ProxyInvokeResponse) => { getProxy().invokeAsync(invokeArgs, (res) => {
if (isProxyInvokeCallbackResponse(res)) { if (res.type !== 'return') {
invokeCallback(res) invokeCallback(res)
} else { } else {
if (res.errMsg) { if (res.errMsg) {
...@@ -98,41 +174,67 @@ export function initUtsProxyFunction({ ...@@ -98,41 +174,67 @@ export function initUtsProxyFunction({
}) })
}) })
} }
return proxy.invokeSync(invokeArgs, invokeCallback) return getProxy().invokeSync(invokeArgs, invokeCallback)
} }
} }
interface ProxyClassOptions { function initUtsStaticMethod(async: boolean, opts: ProxyBaseOptions) {
pkg: string return initProxyFunction(async, opts)
cls: string
methods: {
[name: string]: {
async?: boolean
}
}
} }
export const initUtsProxyFunction = initUtsStaticMethod
export function initUtsProxyClass({ export function initUtsProxyClass({
pkg, package: pkg,
cls, class: cls,
methods, methods,
props,
staticProps,
staticMethods,
}: ProxyClassOptions): any { }: ProxyClassOptions): any {
const baseOptions = {
package: pkg,
class: cls,
}
return class ProxyClass { return class ProxyClass {
constructor() { constructor(...params: unknown[]) {
const target: Record<string, Function> = {} const target: Record<string, Function> = {}
// 初始化实例 ID
const instanceId = initProxyFunction(
false,
extend({ name: 'constructor', params }, baseOptions)
).apply(null, params) as number
return new Proxy(this, { return new Proxy(this, {
get(_, method) { get(_, name) {
if (!target[method as string]) { if (!target[name as string]) {
if (hasOwn(methods, method)) { //实例方法
target[method] = initUtsProxyFunction({ if (hasOwn(methods, name)) {
pkg, target[name] = initUtsInstanceMethod(
cls, !!methods[name].async,
method, extend(
async: methods[method].async, {
}) id: instanceId,
name,
},
baseOptions
)
)
} else if (hasOwn(staticMethods, name)) {
// 静态方法
target[name] = initUtsStaticMethod(
!!staticMethods[name].async,
extend({ name }, baseOptions)
)
} else if (props.includes(name as string)) {
// 实例属性
return invokePropGetter({ id: instanceId, name: name as string })
} else if (staticProps.includes(name as string)) {
// 静态属性
return invokePropGetter(
extend({ name: name as string }, baseOptions)
)
} }
return target[method as string]
} }
return target[name as string]
}, },
}) })
} }
......
...@@ -29,6 +29,10 @@ ...@@ -29,6 +29,10 @@
"@dcloudio/uni-cli-shared": "3.0.0-alpha-3050220220719003", "@dcloudio/uni-cli-shared": "3.0.0-alpha-3050220220719003",
"@dcloudio/uni-shared": "3.0.0-alpha-3050220220719003", "@dcloudio/uni-shared": "3.0.0-alpha-3050220220719003",
"@dcloudio/uts": "3.0.0-alpha-3050220220719003", "@dcloudio/uts": "3.0.0-alpha-3050220220719003",
"execa": "^5.1.1" "execa": "^5.1.1",
"fs-extra": "^10.0.0"
},
"devDependencies": {
"@types/fs-extra": "^9.0.13"
} }
} }
import type { Plugin } from 'vite' import type { Plugin } from 'vite'
import path from 'path' import path from 'path'
import { camelize } from '@vue/shared'
import { import {
normalizePath, normalizePath,
parseVueRequest, parseVueRequest,
...@@ -8,15 +7,20 @@ import { ...@@ -8,15 +7,20 @@ import {
} from '@dcloudio/uni-cli-shared' } from '@dcloudio/uni-cli-shared'
import { import {
ClassDeclaration, ClassDeclaration,
ClassExpression,
Expression,
FunctionDeclaration, FunctionDeclaration,
FunctionExpression,
Identifier,
Module, Module,
TsFunctionType, TsFunctionType,
TsInterfaceDeclaration, TsInterfaceDeclaration,
TsType, TsType,
TsTypeAliasDeclaration, TsTypeAliasDeclaration,
TsTypeAnnotation, TsTypeAnnotation,
VariableDeclaration,
} from '../../types/types' } from '../../types/types'
import { compile } from '../utils/compiler' import { compile, parsePackage } from '../utils/compiler'
export function uniUtsV1Plugin(): Plugin { export function uniUtsV1Plugin(): Plugin {
// 目前仅支持 app-android // 目前仅支持 app-android
...@@ -52,6 +56,7 @@ export function uniUtsV1Plugin(): Plugin { ...@@ -52,6 +56,7 @@ export function uniUtsV1Plugin(): Plugin {
code = ` code = `
import { initUtsProxyClass, initUtsProxyFunction } from '@dcloudio/uni-app' import { initUtsProxyClass, initUtsProxyFunction } from '@dcloudio/uni-app'
const pkg = '${pkg}' const pkg = '${pkg}'
const cls = 'IndexKt'
${genProxyCode(ast)} ${genProxyCode(ast)}
` `
await compile(id) await compile(id)
...@@ -69,38 +74,34 @@ function isUtsModuleRoot(id: string) { ...@@ -69,38 +74,34 @@ function isUtsModuleRoot(id: string) {
return false return false
} }
function parsePackage(filepath: string) {
const parts = normalizePath(filepath).split('/')
const index = parts.findIndex((part) => part === 'uni_modules')
if (index > -1) {
return camelize(parts[index + 1])
}
return ''
}
function genProxyFunctionCode( function genProxyFunctionCode(
method: string, method: string,
async: boolean, async: boolean,
isDefault: boolean = false isDefault: boolean = false
) { ) {
if (isDefault) { if (isDefault) {
return `export default initUtsProxyFunction({ pkg, cls: '', method: '${method}', async: ${async} })` return `export default initUtsProxyFunction(${async}, { package: pkg, class: cls, name: '${method}'})`
} }
return `export const ${method} = initUtsProxyFunction({ pkg, cls: '', method: '${method}', async: ${async} })` return `export const ${method} = initUtsProxyFunction(${async}, { package: pkg, class: cls, name: '${method}'})`
} }
function genProxyClassCode( function genProxyClassCode(
cls: string, cls: string,
methods: Record<string, any>, options: {
methods: Record<string, any>
staticMethods: Record<string, any>
props: string[]
staticProps: string[]
},
isDefault: boolean = false isDefault: boolean = false
) { ) {
if (isDefault) { if (isDefault) {
return `export default initUtsProxyClass({ pkg, cls: '${cls}', methods: ${JSON.stringify( return `export default initUtsProxyClass({ package: pkg, class: '${cls}', ...${JSON.stringify(
methods options
)} })` )} })`
} }
return `export const ${cls} = initUtsProxyClass({ pkg, cls: '${cls}', methods: ${JSON.stringify( return `export const ${cls} = initUtsProxyClass({ package: pkg, class: '${cls}', ...${JSON.stringify(
methods options
)} })` )} })`
} }
...@@ -127,37 +128,98 @@ function genTsInterfaceDeclarationCode( ...@@ -127,37 +128,98 @@ function genTsInterfaceDeclarationCode(
} }
} }
}) })
return genProxyClassCode(cls, methods, isDefault) return genProxyClassCode(
cls,
{ methods, staticMethods: {}, props: [], staticProps: [] },
isDefault
)
} }
function genFunctionDeclarationCode( function genFunctionDeclarationCode(
decl: FunctionDeclaration, decl: FunctionDeclaration | FunctionExpression,
isDefault: boolean = false isDefault: boolean = false
) { ) {
return genProxyFunctionCode( return genProxyFunctionCode(
decl.identifier.value, decl.identifier!.value,
decl.async || isReturnPromise(decl.returnType), decl.async || isReturnPromise(decl.returnType),
isDefault isDefault
) )
} }
function genClassDeclarationCode( function genClassDeclarationCode(
decl: ClassDeclaration, decl: ClassDeclaration | ClassExpression,
isDefault: boolean = false isDefault: boolean = false
) { ) {
const cls = decl.identifier.value const cls = decl.identifier!.value
const methods: Record<string, { async?: boolean }> = {} const methods: Record<string, { async?: boolean }> = {}
const staticMethods: Record<string, { async?: boolean }> = {}
const props: string[] = []
const staticProps: string[] = []
decl.body.forEach((item) => { decl.body.forEach((item) => {
if (item.type === 'ClassMethod') { if (item.type === 'ClassMethod') {
if (item.key.type === 'Identifier') { if (item.key.type === 'Identifier') {
methods[item.key.value] = { const name = item.key.value
const value = {
async: async:
item.function.async || isReturnPromise(item.function.returnType), item.function.async || isReturnPromise(item.function.returnType),
} }
if (item.isStatic) {
staticMethods[name] = value
} else {
methods[name] = value
}
}
} else if (item.type === 'ClassProperty') {
if (item.key.type === 'Identifier') {
if (item.isStatic) {
staticProps.push(item.key.value)
} else {
props.push(item.key.value)
}
} }
} }
}) })
return genProxyClassCode(cls, methods, isDefault) return genProxyClassCode(
cls,
{ methods, staticMethods, props, staticProps },
isDefault
)
}
function genInitCode(expr: Expression) {
switch (expr.type) {
case 'BooleanLiteral':
return expr.value + ''
case 'NumericLiteral':
return expr.value + ''
case 'StringLiteral':
return expr.value
}
return ''
}
function genVariableDeclarationCode(decl: VariableDeclaration) {
// 目前仅支持boolean,number,string
const lits = ['BooleanLiteral', 'NumericLiteral', 'StringLiteral']
if (
!decl.declarations.find((d) => {
if (d.id.type !== 'Identifier') {
return true
}
if (!d.init) {
return true
}
const type = d.init.type
if (!lits.includes(type)) {
return true
}
return false
})
) {
return `${decl.kind} ${decl.declarations
.map((d) => `${(d.id as Identifier).value} = ${genInitCode(d.init!)}`)
.join(', ')}`
}
} }
function genProxyCode({ body }: Module) { function genProxyCode({ body }: Module) {
...@@ -179,10 +241,23 @@ function genProxyCode({ body }: Module) { ...@@ -179,10 +241,23 @@ function genProxyCode({ body }: Module) {
case 'TsInterfaceDeclaration': case 'TsInterfaceDeclaration':
code = genTsInterfaceDeclarationCode(decl, false) code = genTsInterfaceDeclarationCode(decl, false)
break break
case 'VariableDeclaration':
code = genVariableDeclarationCode(decl)
break
} }
} else if (item.type === 'ExportDefaultDeclaration') { } else if (item.type === 'ExportDefaultDeclaration') {
if (item.decl.type === 'TsInterfaceDeclaration') { const decl = item.decl
code = genTsInterfaceDeclarationCode(item.decl, true) if (decl.type === 'TsInterfaceDeclaration') {
code = genTsInterfaceDeclarationCode(decl, true)
} else if (decl.type === 'ClassExpression') {
if (decl.identifier) {
// export default class test{}
code = genClassDeclarationCode(decl, false)
}
} else if (decl.type === 'FunctionExpression') {
if (decl.identifier) {
code = genFunctionDeclarationCode(decl, false)
}
} }
} }
if (code) { if (code) {
......
import os from 'os' import os from 'os'
import fs from 'fs' import fs from 'fs-extra'
import path from 'path' import path from 'path'
import execa from 'execa' import execa from 'execa'
import { once } from '@dcloudio/uni-shared' import { once } from '@dcloudio/uni-shared'
import type { parse, bundle, UtsTarget } from '@dcloudio/uts' import type { parse, bundle, UtsTarget } from '@dcloudio/uts'
import { normalizePath } from '@dcloudio/uni-cli-shared'
import { camelize } from '@vue/shared'
export function getUtsCompiler(): { export function getUtsCompiler(): {
parse: typeof parse parse: typeof parse
...@@ -29,21 +31,56 @@ export async function compile(filename: string) { ...@@ -29,21 +31,56 @@ export async function compile(filename: string) {
}, },
output: { output: {
outDir: outputDir, outDir: outputDir,
package: parsePackage(filename),
sourceMap: true, sourceMap: true,
extname: 'kt', extname: 'kt',
imports: ['kotlinx.coroutines.*', 'io.dcloud.uts.runtime.*'],
}, },
}) })
console.log('uts compile time: ' + (Date.now() - time) + 'ms') console.log('uts compile time: ' + (Date.now() - time) + 'ms')
const kotlinFile = resolveKotlinFile(filename, inputDir, outputDir) const kotlinFile = resolveKotlinFile(filename, inputDir, outputDir)
if (fs.existsSync(kotlinFile)) { if (process.env.NODE_ENV === 'production') {
time = Date.now() // 生产模式下,需要将 kt 文件转移到 src 下
await compileKotlin(kotlinFile) fs.mkdirSync(path.resolve(kotlinFile, '../src'))
console.log('kotlin compile time: ' + (Date.now() - time) + 'ms') if (fs.existsSync(kotlinFile)) {
const jarFile = resolveJarPath(kotlinFile) fs.moveSync(kotlinFile, path.resolve(kotlinFile, '../src/index.kt'))
if (fs.existsSync(jarFile)) { }
const kotlinMapFile = kotlinFile + '.map'
if (fs.existsSync(kotlinMapFile)) {
fs.moveSync(
kotlinMapFile,
path.resolve(kotlinFile, '../src/index.map.kt')
)
}
const copies = ['assets', 'libs', 'res']
const moduleDir = path.dirname(filename)
const outputModuleDir = path.dirname(kotlinFile)
fs.readdirSync(moduleDir).forEach((file) => {
if (copies.includes(file)) {
fs.copySync(
path.join(moduleDir, file),
path.join(outputModuleDir, file)
)
}
})
} else if (process.env.NODE_ENV === 'development') {
// 开发模式下,需要生成 dex
if (fs.existsSync(kotlinFile)) {
time = Date.now() time = Date.now()
await d8(jarFile) await compileKotlin(kotlinFile)
console.log('d8 compile time: ' + (Date.now() - time) + 'ms') console.log('kotlin compile time: ' + (Date.now() - time) + 'ms')
const jarFile = resolveJarPath(kotlinFile)
if (fs.existsSync(jarFile)) {
time = Date.now()
await d8(jarFile)
console.log('d8 compile time: ' + (Date.now() - time) + 'ms')
try {
fs.unlinkSync(jarFile)
// 短期内先不删除,方便排查问题
// fs.unlinkSync(kotlinFile)
} catch (e) {}
}
} }
} }
} }
...@@ -139,3 +176,12 @@ const resolveD8Path = once(() => { ...@@ -139,3 +176,12 @@ const resolveD8Path = once(() => {
const { d8 } = resolveDirs() const { d8 } = resolveDirs()
return path.resolve(d8, 'd8.jar') return path.resolve(d8, 'd8.jar')
}) })
export function parsePackage(filepath: string) {
const parts = normalizePath(filepath).split('/')
const index = parts.findIndex((part) => part === 'uni_modules')
if (index > -1) {
return 'uts.modules.' + camelize(parts[index + 1])
}
return ''
}
...@@ -53,6 +53,14 @@ export interface ToOptions { ...@@ -53,6 +53,14 @@ export interface ToOptions {
* 输出目录 * 输出目录
*/ */
dir: string dir: string
/**
* 包名
*/
package?: string
/**
* 自动导入的包
*/
imports?: string[]
/** /**
* 是否生成 sourceMap,为 string 时,表示生成的 sourceMap 目标目录 * 是否生成 sourceMap,为 string 时,表示生成的 sourceMap 目标目录
*/ */
...@@ -120,11 +128,15 @@ function initInputOptions(_: UtsTarget, root: string): UtsInputOptions { ...@@ -120,11 +128,15 @@ function initInputOptions(_: UtsTarget, root: string): UtsInputOptions {
function initOutputOptions( function initOutputOptions(
target: UtsTarget, target: UtsTarget,
outDir: string, outDir: string,
pkg: string,
imports: string[] = [],
sourceMap: string | boolean | undefined, sourceMap: string | boolean | undefined,
inlineSourcesContent: boolean inlineSourcesContent: boolean
): UtsOutputOptions { ): UtsOutputOptions {
return { return {
outDir, outDir,
package: pkg,
imports,
sourceMap: sourceMap ? sourceMap : false, sourceMap: sourceMap ? sourceMap : false,
inlineSourcesContent, inlineSourcesContent,
extname: UtsTargetExtNames[target], extname: UtsTargetExtNames[target],
...@@ -135,7 +147,13 @@ function initOptions( ...@@ -135,7 +147,13 @@ function initOptions(
target: UtsTarget, target: UtsTarget,
{ {
input: { dir: inputDir }, input: { dir: inputDir },
output: { dir: outputDir, sourceMap, inlineSourcesContent }, output: {
dir: outputDir,
package: pkg,
imports,
sourceMap,
inlineSourcesContent,
},
}: ToOptions }: ToOptions
) { ) {
const inputSrcDir = resolveSrcDir(target, inputDir) const inputSrcDir = resolveSrcDir(target, inputDir)
...@@ -145,6 +163,8 @@ function initOptions( ...@@ -145,6 +163,8 @@ function initOptions(
const output = initOutputOptions( const output = initOutputOptions(
target, target,
outputSrcDir, outputSrcDir,
pkg || '',
imports,
sourceMap, sourceMap,
!!inlineSourcesContent !!inlineSourcesContent
) )
...@@ -282,7 +302,6 @@ function buildFile( ...@@ -282,7 +302,6 @@ function buildFile(
input: { input: {
...input, ...input,
filename, filename,
namespace: '',
}, },
output: { output: {
...output, ...output,
......
...@@ -17,11 +17,11 @@ export type UtsParseOptions = UtsParserConfig & { ...@@ -17,11 +17,11 @@ export type UtsParseOptions = UtsParserConfig & {
export type UtsInputOptions = UtsParseOptions & { export type UtsInputOptions = UtsParseOptions & {
root: string root: string
filename: string filename: string
namespace?: string
} }
export type UtsOutputOptions = { export type UtsOutputOptions = {
outDir: string outDir: string
package: string
imports?: string[] imports?: string[]
sourceMap?: boolean | string sourceMap?: boolean | string
inlineSourcesContent?: boolean inlineSourcesContent?: boolean
......
...@@ -798,12 +798,17 @@ importers: ...@@ -798,12 +798,17 @@ importers:
'@dcloudio/uni-cli-shared': 3.0.0-alpha-3050220220719003 '@dcloudio/uni-cli-shared': 3.0.0-alpha-3050220220719003
'@dcloudio/uni-shared': 3.0.0-alpha-3050220220719003 '@dcloudio/uni-shared': 3.0.0-alpha-3050220220719003
'@dcloudio/uts': 3.0.0-alpha-3050220220719003 '@dcloudio/uts': 3.0.0-alpha-3050220220719003
'@types/fs-extra': ^9.0.13
execa: ^5.1.1 execa: ^5.1.1
fs-extra: ^10.0.0
dependencies: dependencies:
'@dcloudio/uni-cli-shared': link:../uni-cli-shared '@dcloudio/uni-cli-shared': link:../uni-cli-shared
'@dcloudio/uni-shared': link:../uni-shared '@dcloudio/uni-shared': link:../uni-shared
'@dcloudio/uts': link:../uts '@dcloudio/uts': link:../uts
execa: 5.1.1 execa: 5.1.1
fs-extra: 10.1.0
devDependencies:
'@types/fs-extra': 9.0.13
packages/uni-vue: packages/uni-vue:
specifiers: specifiers:
......
...@@ -3,11 +3,15 @@ ...@@ -3,11 +3,15 @@
console.log('publishHandler', JSON.stringify(args)) console.log('publishHandler', JSON.stringify(args))
}, },
} }
let instanceId = 1
;(global as any).uni = { ;(global as any).uni = {
requireNativePlugin(name: string) { requireNativePlugin(name: string) {
return { return {
invokeSync(args: unknown, callback: unknown) { invokeSync(args: Record<string, any>, callback: unknown) {
console.log(`invoke`, JSON.stringify(args)) console.log(`invoke`, JSON.stringify(args))
if (args.name === 'constructor') {
return instanceId++
}
}, },
invokeAsync(args: unknown, callback: unknown) { invokeAsync(args: unknown, callback: unknown) {
console.log(`invokeAsync`, JSON.stringify(args)) console.log(`invokeAsync`, JSON.stringify(args))
......
...@@ -30,6 +30,8 @@ bundle({ ...@@ -30,6 +30,8 @@ bundle({
projectDir, projectDir,
'unpackage/dist/app-plus/uni_modules/test-uniplugin/' 'unpackage/dist/app-plus/uni_modules/test-uniplugin/'
), ),
package: 'uts.modules.testUniPlugin',
imports: ['kotlinx.coroutines.*', 'io.dcloud.uts.runtime.*'],
sourceMap: true, sourceMap: true,
extname: 'kt', extname: 'kt',
}, },
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册