本文共 3763 字,大约阅读时间需要 12 分钟。
紧接上回,Cocos2d-JS通过JSBinding从C++API到JSAPI,完成了H5的跨平台加速,这一回,我们一起来见证一下JSPatch的跨平台实现,为JS语言增加消息转发机制,无需修改js脚本,让下面这段代码可以正确地运行起来:
var controller = UIViewController.alloc().init();
而不是让app翻译成:
UIViewController.__c('alloc')().__c('init')();
JSPatch依赖JavaScriptCore作为运行环境,在iOS7.0之后越来越受到终端开发者的欢迎,我们简单地分析JSPatch的技术框架和原理,来引出SpiderMonkey在Patch上的lazy加载机制:
使用反射机制
`Class class = NSClassFromString("UIViewController"); id viewController = [[class alloc] init]; SEL selector = NSSelectorFromString("viewDidLoad"); [viewController performSelector:selector];
`
使用runtime机制动态注册类,并添加函数`Class superCls = NSClassFromString(superClassName); cls = objc_allocateClassPair(superCls, className.UTF8String, 0); objc_registerClassPair(cls); class_addMethod(cls, @selector(ORIGforwardInvocation:), originalForwardImp, "v@:@");
`
替换掉掉原有的实现,增加JS拦截`IMP originalImp = class_respondsToSelector(cls, selector) ? class_getMethodImplementation(cls, selector) : NULL;IMP msgForwardIMP = _objc_msgForward;class_replaceMethod(cls, selector, msgForwardIMP, typeDescription);
`
通过require增加全局变量`var _require = function(clsName) { if (!global[clsName]) { global[clsName] = { __isCls: 1, __clsName: clsName; }; } return global[clsName]}
`
修改JS脚本,通过正则替换JS的表达式:
UIView.alloc().init();替换后UIView.__c('alloc')().__c('init')();
问题的核心是,JS能否拥有API的转发机制,通过运行时动态加载和调用,打开JSClass,查看描述信息:
查看JSResolveOp方法的说明:
`解决在一个Obj对象上的懒加载属性,这些属性按照需求映射在Native对象;JS会首先在Obj上查找该属性,如果没有找到,则调用该方法解析该属性;如果解析成功,JS引擎将再次调用Obj.ID,如果没有找到,会提交给它的父类解析;JSNewResolveOp提供了更便宜的方式来解决延迟属性;`bool AJContext::Resolve(JSContext *cx, HandleObject handle, HandleId handId, MutableHandleObject retval) { char* name = JS_EncodeStringToUTF8(cx, RootedString(cx, JSID_TO_STRING(handId))); Class clazz = objc_getClass(name); if (clazz) { AJContext* nactx = (AJContext*)JS_GetContextPrivate(cx); RootedObject proxy(cx, JS_NewObject(cx, &AJProxy::Clazz, RootedObject(cx, nactx->prototypeProxy()), NullPtr())); JS_SetPrivate(proxy, new AJProxy(false, clazz)); JS_DefinePropertyById(cx, handle, handId, proxy, 0); retval.set(handle.get()); } return true;}
class AJProxy {public: void* prt; bool isObject; char* classNamepublic: GetterAndSetterMethods....}
bool AJProxy::NewResolve(JSContext *cx, JS::HandleObject handle, JS::HandleId handId, JS::MutableHandleObject retval) { const char* name = JS_EncodeStringToUTF8(cx, RootedString(cx, JSID_TO_STRING(handId))); fprintf(stderr, "%s\n", name); AJProxy* proxy = (AJProxy*)JS_GetPrivate(handle); if (proxy->isObject()) { //...处理对象方法 } else { // ...处理类方法 id clazz = (id)proxy->target(); id object = objc_msgSend(clazz, sel_registerName(name)); AJContext* nactx = (AJContext*)JS_GetContextPrivate(cx); RootedObject proxy(cx, JS_NewObject(cx, &AJProxy::Clazz, RootedObject(cx, nactx->prototypeProxy()), NullPtr())); JS_SetPrivate(proxy, new AJProxy(true, object)); JS_DefineFunction(cx, handle, name, NewFunction, 0, 0); retval.set(handle.get()); } return true;}
需要补充的地方也很多,比如继承、类型转换等,作者很喜欢SpiderMonkey、C++,这种兼容iOS和Android的事情,还需要很多很多的工作。
这里我们主要讲得时SpiderMonkey,通过这个小巧的JsVM,我们可以重新改写很多JS的不擅长的地方,毕竟js的开发者要比lua、wax、swift多很多。相信不需要多久,就会有人写出跨平台的JSPatch,使用JS开发的同学会越来越多。转载地址:http://okbbl.baihongyu.com/