Strengthen the self-return prevention logic (for recycled nodes and updates)

This commit is contained in:
Pierre-Yves Gerardy 2017-03-26 22:07:49 +02:00
parent aeb1c41bde
commit 7be2ff5feb
2 changed files with 35 additions and 2 deletions

View file

@ -119,12 +119,12 @@ module.exports = function($window) {
if (vnode.attrs != null) initLifecycle(vnode.attrs, vnode, hooks)
initLifecycle(vnode.state, vnode, hooks)
vnode.instance = Vnode.normalize(vnode.state.view(vnode))
if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as argument")
sentinel.$$reentrantLock$$ = null
}
function createComponent(parent, vnode, hooks, ns, nextSibling) {
initComponent(vnode, hooks)
if (vnode.instance != null) {
if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as arguments")
var element = createNode(parent, vnode.instance, hooks, ns, nextSibling)
vnode.dom = vnode.instance.dom
vnode.domSize = vnode.dom != null ? vnode.instance.domSize : 0
@ -317,6 +317,7 @@ module.exports = function($window) {
initComponent(vnode, hooks)
} else {
vnode.instance = Vnode.normalize(vnode.state.view(vnode))
if (vnode.instance === vnode) throw Error("A view cannot return the vnode it received as argument")
if (vnode.attrs != null) updateLifecycle(vnode.attrs, vnode, hooks)
updateLifecycle(vnode.state, vnode, hooks)
}

View file

@ -260,8 +260,9 @@ o.spec("component", function() {
o(root.childNodes.length).equals(0)
})
o("throws a custom error if it returns itself", function() {
o("throws a custom error if it returns itself when created", function() {
// A view that returns its vnode would otherwise trigger an infinite loop
var threw = false
var component = createComponent({
view: function(vnode) {
return vnode
@ -271,10 +272,41 @@ o.spec("component", function() {
render(root, [{tag: component}])
}
catch (e) {
threw = true
o(e instanceof Error).equals(true)
// Call stack exception is a RangeError
o(e instanceof RangeError).equals(false)
}
o(threw).equals(true)
})
o("throws a custom error if it returns itself when updated", function() {
// A view that returns its vnode would otherwise trigger an infinite loop
var threw = false
var init = true
var oninit = o.spy()
var component = createComponent({
oninit: oninit,
view: function(vnode) {
if (init) return init = false
else return vnode
}
})
render(root, [{tag: component}])
o(root.firstChild.nodeType).equals(3)
o(root.firstChild.nodeValue).equals("")
try {
render(root, [{tag: component}])
}
catch (e) {
threw = true
o(e instanceof Error).equals(true)
// Call stack exception is a RangeError
o(e instanceof RangeError).equals(false)
}
o(threw).equals(true)
o(oninit.callCount).equals(1)
})
o("can update when returning fragments", function() {
var component = createComponent({