Skip to content

Commit

Permalink
✨ further Promise support
Browse files Browse the repository at this point in the history
  • Loading branch information
FurryR committed Jan 24, 2024
1 parent 2062a38 commit d7d3863
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 168 deletions.
6 changes: 3 additions & 3 deletions doc/definition/builtin.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ declare class Promise {
*/
constructor(
executor: (
resolve: (value: any | Promise<any>) => void,
resolve: (value: any | PromiseLike<any>) => void,
reject: (reason: any) => void
) => void
)
Expand All @@ -86,8 +86,8 @@ declare class Promise {
* @returns New Promise instance.
*/
then(
onFulfilled: (value: any) => any | Promise<any>,
onRejected: (reason: any) => any | Promise<any>
onFulfilled: (value: any) => any | PromiseLike<any>,
onRejected: (reason: any) => any | PromiseLike<any>
): Promise<any>
/**
* Register exception handlers.
Expand Down
76 changes: 45 additions & 31 deletions src/core/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@ import {
asBoolean,
serializeObject,
ensureValue,
deserializeObject
deserializeObject,
isPromise
} from './helper'
function processPromise(self: LppPromise, res: LppPromise): LppReturn {
self.pm = res.pm
return new LppReturn(new LppConstant(null))
}
/**
* Global builtins.
*/
Expand Down Expand Up @@ -83,7 +88,7 @@ export namespace Global {
return new LppReturn(new LppConstant(self.value.length))
}
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand Down Expand Up @@ -122,7 +127,7 @@ export namespace Global {
return new LppReturn(new LppConstant(self.value.length))
}
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand Down Expand Up @@ -161,7 +166,7 @@ export namespace Global {
)
if (args[0] instanceof LppFunction) return new LppReturn(args[0])
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand All @@ -188,7 +193,7 @@ export namespace Global {
default: {
if (!(args[1] instanceof LppArray)) {
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand All @@ -203,7 +208,7 @@ export namespace Global {
return self.apply(selfArg, argArray)
} else {
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand All @@ -226,19 +231,29 @@ export namespace Global {
args[0] instanceof LppFunction
) {
const fn = args[0]
return fn.apply(self, [
new LppFunction((_, args) => {
self.resolve(args[0] ?? new LppConstant(null))
return new LppReturn(new LppConstant(null))
}),
new LppFunction((_, args) => {
self.reject(args[0] ?? new LppConstant(null))
return new LppReturn(new LppConstant(null))
})
])
// TODO: resolve(v: PromiseLike<...>)
const temp = LppPromise.generate((resolve, reject) => {
const res = fn.apply(self, [
new LppFunction((_, args) => {
const v = args[0] ?? new LppConstant(null)
// TODO: detect if v is PromiseLike
resolve(v)
return new LppReturn(new LppConstant(null))
}),
new LppFunction((_, args) => {
reject(args[0] ?? new LppConstant(null))
return new LppReturn(new LppConstant(null))
})
])
return isPromise(res) ? res.then(() => undefined) : undefined
})
if (isPromise(temp)) {
return temp.then(res => processPromise(self, res))
}
return processPromise(self, temp)
} else {
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand All @@ -251,7 +266,6 @@ export namespace Global {
[
'then',
LppFunction.native((self, args) => {
// TODO: handle Promise (described in standard documentation)
if (
self instanceof LppPromise &&
args.length > 0 &&
Expand All @@ -265,7 +279,7 @@ export namespace Global {
)
} else {
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand All @@ -285,7 +299,7 @@ export namespace Global {
return new LppReturn(self.error(args[0]))
} else {
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand All @@ -307,7 +321,7 @@ export namespace Global {
return new LppReturn(new LppArray())
} else {
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand All @@ -322,15 +336,15 @@ export namespace Global {
(self, args) => {
if (self.instanceof(IllegalInvocationError)) {
const res = Error.apply(self, args)
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: Error constructor should be synchronous'
)
if (res instanceof LppException) return res
return new LppReturn(new LppConstant(null))
} else {
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand All @@ -347,15 +361,15 @@ export namespace Global {
(self, args) => {
if (self.instanceof(SyntaxError)) {
const res = Error.apply(self, args)
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: Error constructor should be synchronous'
)
if (res instanceof LppException) return res
return new LppReturn(new LppConstant(null))
} else {
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand All @@ -372,7 +386,7 @@ export namespace Global {
LppFunction.native((self, args) => {
if (self != JSON) {
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand All @@ -385,7 +399,7 @@ export namespace Global {
!(typeof args[0].value === 'string')
) {
const res = SyntaxError.construct([new LppConstant('Invalid JSON')])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: SyntaxError constructor should be synchronous'
)
Expand All @@ -399,7 +413,7 @@ export namespace Global {
} catch (e) {
if (e instanceof globalThis.Error) {
const res = SyntaxError.construct([new LppConstant(e.message)])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: SyntaxError constructor should be synchronous'
)
Expand All @@ -414,7 +428,7 @@ export namespace Global {
LppFunction.native((self, args) => {
if (self != JSON) {
const res = IllegalInvocationError.construct([])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: IllegalInvocationError constructor should be synchronous'
)
Expand All @@ -425,7 +439,7 @@ export namespace Global {
const res = SyntaxError.construct([
new LppConstant('Invalid value')
])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: SyntaxError constructor should be synchronous'
)
Expand All @@ -441,7 +455,7 @@ export namespace Global {
} catch (e) {
if (e instanceof globalThis.Error) {
const res = SyntaxError.construct([new LppConstant(e.message)])
if (res instanceof globalThis.Promise)
if (isPromise(res))
throw new globalThis.Error(
'lpp: SyntaxError constructor should be synchronous'
)
Expand Down
53 changes: 53 additions & 0 deletions src/core/helper.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LppException, LppReturn, LppReturnOrException } from './context'
import {
LppValue,
LppReference,
Expand Down Expand Up @@ -252,3 +253,55 @@ export function serializeObject(value: unknown): LppValue {
}
return serializeInternal(value)
}
export function isPromise(value: unknown): value is PromiseLike<unknown> {
return (
typeof value === 'object' &&
value !== null &&
typeof (value as Record<string | number | symbol, unknown>).then ===
'function'
)
}
export function processThenReturn(
returnValue: LppReturnOrException,
resolve: (v: LppValue) => void,
reject: (reason: unknown) => void
): undefined | PromiseLike<void> {
if (returnValue instanceof LppReturn) {
const value = returnValue.value
if (!(value instanceof LppConstant) || value.value !== null) {
const then = ensureValue(value.get('then'))
if (then instanceof LppFunction) {
const res = then.apply(value, [
new LppFunction((_, args) => {
// resolve
const res = processThenReturn(
new LppReturn(args[0] ?? new LppConstant(null)),
resolve,
reject
)
if (isPromise(res)) {
return res.then(() => new LppReturn(new LppConstant(null)))
}
return new LppReturn(new LppConstant(null))
}),
new LppFunction((_, args) => {
// reject
reject(args[0] ?? new LppConstant(null))
return new LppReturn(new LppConstant(null))
})
])
if (isPromise(res)) {
return res.then(value => {
// PromiseLike should return a PromiseLike. Here we do not care about that.
if (value instanceof LppException) {
reject(value.value)
}
})
}
return undefined
}
}
return void resolve(returnValue.value)
}
return void reject(returnValue.value)
}
26 changes: 5 additions & 21 deletions src/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,13 @@
/**
* Lpp core (standard) implementation.
*/
export { Global, global } from './global'
export {
LppClosure,
LppTraceback,
LppContext,
LppException,
LppFunctionContext,
LppReturn,
LppReturnOrException
} from './context'
export {
LppArray,
LppReference,
LppConstant,
LppError,
LppFunction,
LppObject,
LppPromise,
LppValue
} from './type'
export * from './global'
export * from './context'
export * from './type'
export {
ensureValue,
asBoolean,
serializeObject,
deserializeObject
deserializeObject,
isPromise
} from './helper'
Loading

0 comments on commit d7d3863

Please sign in to comment.