mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 21:14:28 -08:00
WIP
This commit is contained in:
@@ -13,6 +13,17 @@ const join = arr => arr.reduce(
|
|||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const is_error = n =>
|
||||||
|
!n.ok
|
||||||
|
||
|
||||||
|
(
|
||||||
|
n.value instanceof globalThis.run_window.Promise
|
||||||
|
&&
|
||||||
|
n.value.status != null
|
||||||
|
&&
|
||||||
|
!n.value.status.ok
|
||||||
|
)
|
||||||
|
|
||||||
export class CallTree {
|
export class CallTree {
|
||||||
constructor(ui, container) {
|
constructor(ui, container) {
|
||||||
this.ui = ui
|
this.ui = ui
|
||||||
@@ -108,7 +119,7 @@ export class CallTree {
|
|||||||
)
|
)
|
||||||
: el('span',
|
: el('span',
|
||||||
'call_header '
|
'call_header '
|
||||||
+ (n.ok ? '' : 'error')
|
+ (is_error(n) ? 'error' : '')
|
||||||
+ (n.fn.__location == null ? ' native' : '')
|
+ (n.fn.__location == null ? ' native' : '')
|
||||||
,
|
,
|
||||||
// TODO show `this` argument
|
// TODO show `this` argument
|
||||||
|
|||||||
@@ -42,10 +42,10 @@ export const stringify_for_header = v => {
|
|||||||
if(v.status == null) {
|
if(v.status == null) {
|
||||||
return `Promise<pending>`
|
return `Promise<pending>`
|
||||||
} else {
|
} else {
|
||||||
if(status.ok) {
|
if(v.status.ok) {
|
||||||
return `Promise<fulfilled: ${stringify_for_header(status.value)}>`
|
return `Promise<fulfilled: ${stringify_for_header(v.status.value)}>`
|
||||||
} else {
|
} else {
|
||||||
return `Promise<fulfilled: ${stringify_for_header(status.error)}>`
|
return `Promise<rejected: ${stringify_for_header(v.status.error)}>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(isError(v)) {
|
} else if(isError(v)) {
|
||||||
|
|||||||
@@ -367,7 +367,7 @@ export const eval_modules = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const set_promise_status = value => {
|
const set_promise_status = value => {
|
||||||
if(value instanceof Promise.Original) {
|
if(value instanceof Promise) {
|
||||||
// record stack for async calls, so expand calls works sync
|
// record stack for async calls, so expand calls works sync
|
||||||
set_record_call()
|
set_record_call()
|
||||||
return value
|
return value
|
||||||
@@ -988,7 +988,7 @@ const do_eval_frame_expr = (node, scope, callsleft) => {
|
|||||||
ok = true
|
ok = true
|
||||||
value = - expr.result.value
|
value = - expr.result.value
|
||||||
} else if(node.operator == 'await') {
|
} else if(node.operator == 'await') {
|
||||||
if(expr.result.value instanceof globalThis.run_window.Promise.Original) {
|
if(expr.result.value instanceof globalThis.run_window.Promise) {
|
||||||
const status = expr.result.value.status
|
const status = expr.result.value.status
|
||||||
if(status == null) {
|
if(status == null) {
|
||||||
// Promise must be already resolved
|
// Promise must be already resolved
|
||||||
|
|||||||
@@ -1,78 +1,37 @@
|
|||||||
export const patch_promise = window => {
|
export const patch_promise = window => {
|
||||||
|
|
||||||
if(window.Promise.Original != null) {
|
if(window.Promise.__patched) {
|
||||||
// already patched
|
// already patched
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
class PromiseRecordChildren extends Promise {
|
const _then = Promise.prototype.then
|
||||||
then(on_resolve, on_reject) {
|
|
||||||
let children = window.get_children()
|
|
||||||
if(children == null) {
|
|
||||||
children = []
|
|
||||||
window.set_children(children)
|
|
||||||
}
|
|
||||||
return super.then(
|
|
||||||
on_resolve == null
|
|
||||||
? null
|
|
||||||
: value => {
|
|
||||||
window.set_children(children)
|
|
||||||
return on_resolve(value)
|
|
||||||
},
|
|
||||||
|
|
||||||
on_reject == null
|
Promise.prototype.then = function then(on_resolve, on_reject) {
|
||||||
? null
|
let children = window.get_children()
|
||||||
: error => {
|
if(children == null) {
|
||||||
window.set_children(children)
|
children = []
|
||||||
return on_reject(error)
|
window.set_children(children)
|
||||||
|
}
|
||||||
|
|
||||||
|
const make_callback = cb => cb == null
|
||||||
|
? null
|
||||||
|
: value => {
|
||||||
|
const current = window.get_children()
|
||||||
|
window.set_children(children)
|
||||||
|
try {
|
||||||
|
return cb(value)
|
||||||
|
} finally {
|
||||||
|
window.set_children(current)
|
||||||
}
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PromiseWithStatus extends window.Promise {
|
|
||||||
constructor(fn) {
|
|
||||||
let status
|
|
||||||
let is_constructor_finished = false
|
|
||||||
const p = new PromiseRecordChildren(
|
|
||||||
(resolve, reject) => {
|
|
||||||
fn(
|
|
||||||
(value) => {
|
|
||||||
if(value instanceof window.Promise.Original) {
|
|
||||||
value
|
|
||||||
.then(v => {
|
|
||||||
p.status = {ok: true, value: v}
|
|
||||||
resolve(v)
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
p.status = {ok: false, error: e}
|
|
||||||
reject(e)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
status = {ok: true, value}
|
|
||||||
if(is_constructor_finished) {
|
|
||||||
p.status = status
|
|
||||||
}
|
|
||||||
resolve(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(error) => {
|
|
||||||
status = {ok: false, error}
|
|
||||||
if(is_constructor_finished) {
|
|
||||||
p.status = status
|
|
||||||
}
|
|
||||||
reject(error)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
is_constructor_finished = true
|
return _then.call(
|
||||||
p.status = status
|
this,
|
||||||
return p
|
make_callback(on_resolve),
|
||||||
}
|
make_callback(on_reject),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
PromiseWithStatus.Original = window.Promise
|
window.Promise.__patched = true
|
||||||
|
|
||||||
window.Promise = PromiseWithStatus
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,4 +84,7 @@ const run = root.children[0]
|
|||||||
|
|
||||||
assert_equal(root_calltree_node(state).ok, true)
|
assert_equal(root_calltree_node(state).ok, true)
|
||||||
|
|
||||||
|
// Assert that run children are tests
|
||||||
|
assert_equal(run.children.length > 100, true)
|
||||||
|
|
||||||
console.timeEnd('run')
|
console.timeEnd('run')
|
||||||
|
|||||||
19
test/test.js
19
test/test.js
@@ -2754,7 +2754,7 @@ const y = x()`
|
|||||||
assert_equal(logs, [2, 1])
|
assert_equal(logs, [2, 1])
|
||||||
}),
|
}),
|
||||||
|
|
||||||
test('async/await logs out of order', async () => {
|
test('async/await logs out of order timeout', async () => {
|
||||||
const i = await test_initial_state_async(`
|
const i = await test_initial_state_async(`
|
||||||
const delay = async time => {
|
const delay = async time => {
|
||||||
await new Promise(res => globalThis.setTimeout(res, time*10))
|
await new Promise(res => globalThis.setTimeout(res, time*10))
|
||||||
@@ -2779,12 +2779,25 @@ const y = x()`
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
test('async/await then bug', async () => {
|
||||||
|
await assert_code_evals_to_async(
|
||||||
|
`
|
||||||
|
const p2 = Promise.resolve(2)
|
||||||
|
const p1 = p2.then(() => 1)
|
||||||
|
const x = () => 1
|
||||||
|
await x()
|
||||||
|
`,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
test('async/await Promise.then creates subcall', async () => {
|
test('async/await Promise.then creates subcall', async () => {
|
||||||
const i = await test_initial_state_async(`
|
const i = await test_initial_state_async(`
|
||||||
const x = () => 1
|
const x = () => 1
|
||||||
await Promise.resolve(1).then(x)
|
await Promise.resolve(1).then(x)
|
||||||
`)
|
`)
|
||||||
const root = root_calltree_node(i)
|
const root = root_calltree_node(i)
|
||||||
|
assert_equal(root.children.at(-1).fn.name, 'then')
|
||||||
assert_equal(root.children.at(-1).children[0].fn.name, 'x')
|
assert_equal(root.children.at(-1).children[0].fn.name, 'x')
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@@ -2794,11 +2807,11 @@ const y = x()`
|
|||||||
await Promise.reject(1).catch(x)
|
await Promise.reject(1).catch(x)
|
||||||
`)
|
`)
|
||||||
const root = root_calltree_node(i)
|
const root = root_calltree_node(i)
|
||||||
|
assert_equal(root.children.at(-1).fn.name, 'catch')
|
||||||
assert_equal(root.children.at(-1).children[0].fn.name, 'x')
|
assert_equal(root.children.at(-1).children[0].fn.name, 'x')
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
test('async/await native Promise.then creates subcall', async () => {
|
||||||
test_only('async/await native Promise.then creates subcall', async () => {
|
|
||||||
const i = await test_initial_state_async(`
|
const i = await test_initial_state_async(`
|
||||||
const x = () => 1
|
const x = () => 1
|
||||||
const async_fn = async () => 1
|
const async_fn = async () => 1
|
||||||
|
|||||||
Reference in New Issue
Block a user