blob: 401d614efd45afafd3f822ae806ac873a557eb20 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* @fileOverview
* instance controls from native
*
* - init bundle
*
* corresponded with the API of instance manager (framework.js)
*/
import Vm from '../../vm/index'
import { removeWeexPrefix } from '../../util/index'
import {
defineFn,
bootstrap,
register
} from '../bundle/index'
import { updateActions } from './misc'
/**
* Init an app by run code witgh data
* @param {object} app
* @param {string} code
* @param {object} data
*/
export function init (app, code, data, services) {
console.debug('[JS Framework] Intialize an instance with:\n', data)
let result
// prepare app env methods
const bundleDefine = (...args) => defineFn(app, ...args)
const bundleBootstrap = (name, config, _data) => {
result = bootstrap(app, name, config, _data || data)
updateActions(app)
app.doc.listener.createFinish()
console.debug(`[JS Framework] After intialized an instance(${app.id})`)
}
const bundleVm = Vm
/* istanbul ignore next */
const bundleRegister = (...args) => register(app, ...args)
/* istanbul ignore next */
const bundleRender = (name, _data) => {
result = bootstrap(app, name, {}, _data)
}
/* istanbul ignore next */
const bundleRequire = name => _data => {
result = bootstrap(app, name, {}, _data)
}
const bundleDocument = app.doc
/* istanbul ignore next */
const bundleRequireModule = name => app.requireModule(removeWeexPrefix(name))
const weexGlobalObject = {
config: app.options,
define: bundleDefine,
bootstrap: bundleBootstrap,
requireModule: bundleRequireModule,
document: bundleDocument,
Vm: bundleVm
}
Object.freeze(weexGlobalObject)
// prepare code
let functionBody
/* istanbul ignore if */
if (typeof code === 'function') {
// `function () {...}` -> `{...}`
// not very strict
functionBody = code.toString().substr(12)
}
/* istanbul ignore next */
else if (code) {
functionBody = code.toString()
}
// wrap IFFE and use strict mode
functionBody = `(function(global){\n\n"use strict";\n\n ${functionBody} \n\n})(Object.create(this))`
// run code and get result
const { WXEnvironment } = global
const timerAPIs = {}
/* istanbul ignore if */
if (WXEnvironment && WXEnvironment.platform !== 'Web') {
// timer APIs polyfill in native
const timer = app.requireModule('timer')
Object.assign(timerAPIs, {
setTimeout: (...args) => {
const handler = function () {
args[0](...args.slice(2))
}
timer.setTimeout(handler, args[1])
return app.doc.taskCenter.callbackManager.lastCallbackId.toString()
},
setInterval: (...args) => {
const handler = function () {
args[0](...args.slice(2))
}
timer.setInterval(handler, args[1])
return app.doc.taskCenter.callbackManager.lastCallbackId.toString()
},
clearTimeout: (n) => {
timer.clearTimeout(n)
},
clearInterval: (n) => {
timer.clearInterval(n)
}
})
}
// run code and get result
const globalObjects = Object.assign({
define: bundleDefine,
require: bundleRequire,
bootstrap: bundleBootstrap,
register: bundleRegister,
render: bundleRender,
__weex_define__: bundleDefine, // alias for define
__weex_bootstrap__: bundleBootstrap, // alias for bootstrap
__weex_document__: bundleDocument,
__weex_require__: bundleRequireModule,
__weex_viewmodel__: bundleVm,
weex: weexGlobalObject
}, timerAPIs, services)
if (!callFunctionNative(globalObjects, functionBody)) {
// If failed to compile functionBody on native side,
// fallback to callFunction.
callFunction(globalObjects, functionBody)
}
return result
}
/**
* Call a new function body with some global objects.
* @param {object} globalObjects
* @param {string} code
* @return {any}
*/
function callFunction (globalObjects, body) {
const globalKeys = []
const globalValues = []
for (const key in globalObjects) {
globalKeys.push(key)
globalValues.push(globalObjects[key])
}
globalKeys.push(body)
const result = new Function(...globalKeys)
return result(...globalValues)
}
/**
* Call a new function generated on the V8 native side.
* @param {object} globalObjects
* @param {string} body
* @return {boolean} return true if no error occurred.
*/
function callFunctionNative (globalObjects, body) {
if (typeof compileAndRunBundle !== 'function') {
return false
}
let fn = void 0
let isNativeCompileOk = false
let script = '(function ('
const globalKeys = []
const globalValues = []
for (const key in globalObjects) {
globalKeys.push(key)
globalValues.push(globalObjects[key])
}
for (let i = 0; i < globalKeys.length - 1; ++i) {
script += globalKeys[i]
script += ','
}
script += globalKeys[globalKeys.length - 1]
script += ') {'
script += body
script += '} )'
try {
const weex = globalObjects.weex || {}
const config = weex.config || {}
fn = compileAndRunBundle(script,
config.bundleUrl,
config.bundleDigest,
config.codeCachePath)
if (fn && typeof fn === 'function') {
fn(...globalValues)
isNativeCompileOk = true
}
}
catch (e) {
console.error(e)
}
return isNativeCompileOk
}