diff --git a/Android/doric/proguard-rules.pro b/Android/doric/proguard-rules.pro index f1b42451..20aedfa0 100644 --- a/Android/doric/proguard-rules.pro +++ b/Android/doric/proguard-rules.pro @@ -19,3 +19,13 @@ # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile +-keep class com.github.penfeizhou.doric.extension.bridge.DoricPlugin +-keep class com.github.penfeizhou.doric.extension.bridge.DoricMethod + +-keep @com.github.penfeizhou.doric.extension.bridge.DoricPlugin class * {*;} + +-keepclasseswithmembers @com.github.penfeizhou.doric.extension.bridge.DoricPlugin class * {*;} + +-keep class * { +@com.github.penfeizhou.doric.extension.bridge.DoricMethod ; +} \ No newline at end of file diff --git a/Android/doric/src/main/java/com/github/penfeizhou/doric/engine/DoricJSEngine.java b/Android/doric/src/main/java/com/github/penfeizhou/doric/engine/DoricJSEngine.java index c6193bed..99547774 100644 --- a/Android/doric/src/main/java/com/github/penfeizhou/doric/engine/DoricJSEngine.java +++ b/Android/doric/src/main/java/com/github/penfeizhou/doric/engine/DoricJSEngine.java @@ -166,7 +166,7 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time } private String packageContextScript(String contextId, String content) { - return String.format(DoricConstant.TEMPLATE_CONTEXT_CREATE, content, contextId, contextId); + return String.format(DoricConstant.TEMPLATE_CONTEXT_CREATE, content, contextId, contextId, contextId); } private String packageModuleScript(String moduleName, String content) { diff --git a/Android/doric/src/main/java/com/github/penfeizhou/doric/utils/DoricConstant.java b/Android/doric/src/main/java/com/github/penfeizhou/doric/utils/DoricConstant.java index ccf85fa4..9e163c44 100644 --- a/Android/doric/src/main/java/com/github/penfeizhou/doric/utils/DoricConstant.java +++ b/Android/doric/src/main/java/com/github/penfeizhou/doric/utils/DoricConstant.java @@ -18,11 +18,12 @@ public class DoricConstant { public static final String INJECT_BRIDGE = "nativeBridge"; public static final String TEMPLATE_CONTEXT_CREATE = "Reflect.apply(" + - "function(doric,context,require,exports){" + "\n" + + "function(doric,context,Entry,require,exports){" + "\n" + "%s" + "\n" + "},doric.jsObtainContext(\"%s\"),[" + "undefined," + "doric.jsObtainContext(\"%s\")," + + "doric.jsObtainEntry(\"%s\")," + "doric.__require__" + ",{}" + "])"; diff --git a/js-framework/demo.ts b/js-framework/demo.ts index de5e956d..79052d48 100644 --- a/js-framework/demo.ts +++ b/js-framework/demo.ts @@ -1,44 +1,18 @@ -import { Text, Alignment, Color, VLayout, Link, Panel, log, logw, loge } from "./index" +import { Text, Alignment, Color, VLayout, Panel, log, logw, loge } from "./index" +import { NativeCall } from "./src/ui/panel"; -const v = new Text -v.width = 20 -v.height = 30 -v.left = 5 -v.top = 5 -v.bgColor = Color.parse('#00ff00') -v.config = { - alignment: Alignment.start -} -// console.log(v.toModel()) -const layout = new VLayout -layout.space = 10 -console.log(layout.viewId) -console.log(layout.toModel()) -// log('console', Object.getOwnPropertyNames(console)) -// setTimeout(() => { -// log('exec setTimeout') -// // context.callNative("modal", "toast", "Hello,Doric!") -// }, 1000) -// const timerId = setInterval(() => { -// log('exec setInterval') -// }, 1000) - -// setTimeout(() => { -// log('exec cancelTimer') -// clearInterval(timerId) -// }, 5000) - -@Link(context) +@Entry export class MyPage extends Panel { build() { - return layout + return new Text } + @NativeCall log() { - // log("Hello.HEGO") - // logw("Hello.HEGO") - // loge("Hello.HEGO") + log("Hello.HEGO") + logw("Hello.HEGO") + loge("Hello.HEGO") context.bridge.demo_testPromise(true).then((r) => { log('resolve', r) }, (e) => { @@ -49,5 +23,8 @@ export class MyPage extends Panel { }, (e) => { log('reject', e) }) + setTimeout(function () { + log('settimeout') + }, 1000) } } \ No newline at end of file diff --git a/js-framework/src/runtime/global.ts b/js-framework/src/runtime/global.ts index 7e8d3529..aeefe61f 100644 --- a/js-framework/src/runtime/global.ts +++ b/js-framework/src/runtime/global.ts @@ -1,6 +1,7 @@ import { Context } from "./sandbox"; declare global { - const context: Context + const context: Context; + function Entry(constructor: { new(...args: any[]): {} }): any } export { } \ No newline at end of file diff --git a/js-framework/src/runtime/sandbox.ts b/js-framework/src/runtime/sandbox.ts index fa2ffabd..e4d7b7f3 100644 --- a/js-framework/src/runtime/sandbox.ts +++ b/js-framework/src/runtime/sandbox.ts @@ -1,16 +1,18 @@ import { uniqueId } from "../util/uniqueId"; import { loge } from "../util/log"; +import "reflect-metadata" /** * ``` TypeScript * // load script in global scope * Reflect.apply( - * function(hego,context,require){ + * function(hego,context,Entry,require){ * //Script content * REG() * },hego.jsObtainContext(id),[ * undefined, * hego.jsObtainContext(id), + * hego.jsObtainEntry(id), * hego.__require__, * ]) * // load module in global scope @@ -31,6 +33,31 @@ declare function nativeRequire(moduleName: string): boolean declare function nativeBridge(contextId: string, namespace: string, method: string, callbackId?: string, args?: any): boolean +declare function nativeSetTimer(timerId: number, interval: number, repeat: boolean): void + +declare function nativeClearTimer(timerId: number): void + +function hookBeforeNativeCall(context?: Context) { + if (context) { + Reflect.defineMetadata('__doric_context__', context, global) + context.hookBeforeNativeCall() + } +} + +function hookAfterNativeCall(context?: Context) { + if (context) { + context.hookAfterNativeCall() + } +} + +function getContext(): Context | undefined { + return Reflect.getMetadata('__doric_context__', global) +} + +function setContext(context?: Context) { + Reflect.defineMetadata('__doric_context__', context, global) +} + export function jsCallResolve(contextId: string, callbackId: string, args?: any) { const context = gContexts.get(contextId) if (context === undefined) { @@ -46,7 +73,9 @@ export function jsCallResolve(contextId: string, callbackId: string, args?: any) for (let i = 2; i < arguments.length; i++) { argumentsList.push(arguments[i]) } + hookBeforeNativeCall(context) Reflect.apply(callback.resolve, context, argumentsList) + hookAfterNativeCall(context) } export function jsCallReject(contextId: string, callbackId: string, args?: any) { @@ -64,7 +93,9 @@ export function jsCallReject(contextId: string, callbackId: string, args?: any) for (let i = 2; i < arguments.length; i++) { argumentsList.push(arguments[i]) } - Reflect.apply(callback.reject, context, argumentsList) + hookBeforeNativeCall(context) + Reflect.apply(callback.reject, context.entity, argumentsList) + hookAfterNativeCall(context) } export class Context { @@ -73,6 +104,18 @@ export class Context { callbacks: Map = new Map bridge: { [index: string]: (args?: any) => Promise } + 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, []) + } + } + constructor(id: string) { this.id = id this.bridge = new Proxy({}, { @@ -110,10 +153,13 @@ const gModules: Map = new Map export function jsObtainContext(id: string) { if (gContexts.has(id)) { - return gContexts.get(id) + const context = gContexts.get(id) + setContext(context) + return context } else { const context: Context = new Context(id) gContexts.set(id, context) + setContext(context) return context } } @@ -153,50 +199,77 @@ export function jsCallEntityMethod(contextId: string, methodName: string, args?: for (let i = 2; i < arguments.length; i++) { argumentsList.push(arguments[i]) } - return Reflect.apply(Reflect.get(context.entity, methodName), context.entity, argumentsList) + hookBeforeNativeCall(context) + const ret = Reflect.apply(Reflect.get(context.entity, methodName), context.entity, argumentsList) + hookAfterNativeCall(context) + return ret } else { loge(`Cannot find method for context id:${contextId},method name is:${methodName}`) } } +export function jsObtainEntry(contextId: string) { + const context = jsObtainContext(contextId) + return (constructor: T) => { + const ret = class extends constructor { + context = context + } + if (context) { + context.register(new ret) + } + return ret + } +} + + const global = Function('return this')() let __timerId__ = 0 -const timerCallbacks: Map void> = new Map -declare function nativeSetTimer(timerId: number, interval: number, repeat: boolean): void -declare function nativeClearTimer(timerId: number): void +const timerInfos: Map void, context?: Context }> = new Map + global.setTimeout = (handler: Function, timeout?: number | undefined, ...args: any[]) => { const id = __timerId__++ - timerCallbacks.set(id, () => { - Reflect.apply(handler, undefined, args) - timerCallbacks.delete(id) + timerInfos.set(id, { + callback: () => { + Reflect.apply(handler, undefined, args) + timerInfos.delete(id) + }, + context: getContext(), }) nativeSetTimer(id, timeout || 0, false) return id } global.setInterval = (handler: Function, timeout?: number | undefined, ...args: any[]) => { const id = __timerId__++ - timerCallbacks.set(id, () => { - Reflect.apply(handler, undefined, args) + timerInfos.set(id, { + callback: () => { + Reflect.apply(handler, undefined, args) + }, + context: getContext(), }) nativeSetTimer(id, timeout || 0, true) return id } global.clearTimeout = (timerId: number) => { - timerCallbacks.delete(timerId) + timerInfos.delete(timerId) nativeClearTimer(timerId) } global.clearInterval = (timerId: number) => { - timerCallbacks.delete(timerId) + timerInfos.delete(timerId) nativeClearTimer(timerId) } export function jsCallbackTimer(timerId: number) { - const timerCallback = timerCallbacks.get(timerId) - if (timerCallback instanceof Function) { - Reflect.apply(timerCallback, undefined, []) + const timerInfo = timerInfos.get(timerId) + if (timerInfo === undefined) { + return + } + if (timerInfo.callback instanceof Function) { + hookBeforeNativeCall(timerInfo.context) + Reflect.apply(timerInfo.callback, timerInfo.context, []) + hookAfterNativeCall(timerInfo.context) } } \ No newline at end of file diff --git a/js-framework/src/ui/panel.ts b/js-framework/src/ui/panel.ts index 8209cf1a..658839b4 100644 --- a/js-framework/src/ui/panel.ts +++ b/js-framework/src/ui/panel.ts @@ -2,26 +2,11 @@ import { } from './../runtime/global'; import { View, Stack, Group } from "./view"; import { loge, log } from '../util/log'; -export function Link(context: any) { - return (constructor: T) => { - const ret = class extends constructor { - context = context - } - context.register(new ret) - return ret - } -} export function NativeCall(target: Panel, propertyKey: string, descriptor: PropertyDescriptor) { const originVal = descriptor.value descriptor.value = function () { - const args = [] - for (let index in arguments) { - args.push(arguments[index]); - } - Reflect.apply(Reflect.get(target, '__hookBeforeNativeCall__'), this, []) - const ret = Reflect.apply(originVal, this, args) - Reflect.apply(Reflect.get(target, '__hookAfterNativeCall__'), this, []) + const ret = Reflect.apply(originVal, this, arguments) return ret } return descriptor @@ -103,11 +88,11 @@ export abstract class Panel { }, this.__rootView__) } - private __hookBeforeNativeCall__() { + private hookBeforeNativeCall() { log('__hookBeforeNativeCall__') } - private __hookAfterNativeCall__() { + private hookAfterNativeCall() { log('__hookAfterNativeCall__') if (this.__rootView__.isDirty()) { const model = this.__rootView__.toModel()