Skip to content

Commit

Permalink
feat(eval): support config.serializer
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jun 3, 2021
1 parent 8976f25 commit 58f8281
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 25 deletions.
2 changes: 1 addition & 1 deletion packages/plugin-eval/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ const defaultConfig: EvalConfig = {
scriptLoader: 'default',
channelFields: ['id'],
userFields: ['id', 'authority'],
dataKeys: ['inspect', 'moduleLoaders', 'setupFiles', 'loaderConfig'],
dataKeys: ['inspect', 'moduleLoaders', 'setupFiles', 'loaderConfig', 'serializer'],
}

const logger = new Logger('eval')
Expand Down
21 changes: 11 additions & 10 deletions packages/plugin-eval/src/worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { parentPort, workerData } from 'worker_threads'
import { InspectOptions, formatWithOptions } from 'util'
import { findSourceMap } from 'module'
import { resolve, dirname, sep } from 'path'
import { deserialize, serialize } from 'v8'

/* eslint-disable import/first */

Expand All @@ -20,7 +19,7 @@ export const config: WorkerData = {
},
}

import prepare, { synthetize, readSerialized, safeWriteFile, LoaderConfig } from './loader'
import prepare, { synthetize, LoaderConfig, system } from './loader'
import { expose, wrap } from '../transfer'
import { Sandbox } from './sandbox'
import { MainHandle } from '..'
Expand All @@ -29,6 +28,7 @@ export * from './loader'

export interface WorkerConfig {
root?: string
serializer?: 'yaml' | 'v8'
moduleLoaders?: Record<string, string>
inspect?: InspectOptions
cacheFile?: string
Expand Down Expand Up @@ -155,10 +155,10 @@ export class WorkerHandle {
await scope.user?._update()
await scope.channel?._update()
try {
const buffer = serialize(scope.storage)
await safeWriteFile(storagePath, backupStorage = buffer)
const buffer = system.serialize(scope.storage)
await system.write(storagePath, backupStorage = buffer)
} catch (error) {
storage = deserialize(backupStorage)
storage = system.deserialize(backupStorage)
throw error
}
}
Expand Down Expand Up @@ -221,12 +221,13 @@ export function mapDirectory(identifier: string, filename: string) {
Object.values(config.setupFiles).map(require)

async function start() {
const data = await Promise.all([readSerialized(storagePath), prepare()])
storage = data[0][0]
backupStorage = data[0][1]
if (!storage) {
const [data] = await Promise.all([system.read(storagePath), prepare()])
if (data) {
storage = data[0]
backupStorage = data[1]
} else {
storage = {}
backupStorage = serialize({})
backupStorage = system.serialize({})
}

response.commands = Object.keys(commandMap)
Expand Down
51 changes: 37 additions & 14 deletions packages/plugin-eval/src/worker/loader.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { config, context, internal } from '.'
import { resolve, posix, dirname, extname } from 'path'
import { promises as fs } from 'fs'
import { deserialize, serialize, cachedDataVersionTag } from 'v8'
import { Logger } from 'koishi-utils'
import * as yaml from 'js-yaml'
import * as v8 from 'v8'

const logger = new Logger('eval:loader')

Expand Down Expand Up @@ -100,21 +101,34 @@ interface FileCache {
}

const CACHE_TAG = 1
const V8_TAG = cachedDataVersionTag()
const V8_TAG = v8.cachedDataVersionTag()
const files: Record<string, FileCache> = {}
const cachedFiles: Record<string, FileCache> = {}

export async function readSerialized(filename: string): Promise<[any?, Buffer?]> {
try {
const buffer = await fs.readFile(filename)
return [deserialize(buffer), buffer]
} catch {
return []
export const system = new class System {
serialize: (value: any) => Buffer
deserialize: (data: Buffer) => any

async read(filename: string) {
try {
const buffer = await fs.readFile(filename)
return [this.deserialize(buffer), buffer] as const
} catch {}
}
}

// errors should be catched because we should not expose file paths to users
async write(filename: string, data: Buffer) {
try {
await fs.mkdir(dirname(filename), { recursive: true })
await fs.writeFile(filename, data)
} catch (error) {
logger.warn(error)
}
}
}()

// errors should be catched because we should not expose file paths to users
export async function safeWriteFile(filename: string, data: any) {
export async function safeWriteFile(filename: string, data: Buffer) {
try {
await fs.mkdir(dirname(filename), { recursive: true })
await fs.writeFile(filename, data)
Expand All @@ -124,18 +138,27 @@ export async function safeWriteFile(filename: string, data: any) {
}

export default async function prepare() {
if (config.serializer === 'yaml') {
system.serialize = value => Buffer.from(yaml.dump(value))
system.deserialize = data => yaml.load(data.toString())
} else {
system.serialize = v8.serialize
system.deserialize = v8.deserialize
}

if (!config.root) return
for (const ext in config.moduleLoaders || {}) {
extnames.add(ext)
}
const cachePath = resolve(config.root, config.cacheFile || '.koishi/cache')
await readSerialized(cachePath).then(([data]) => {
if (data && data.tag === CACHE_TAG && data.v8tag === V8_TAG) {
Object.assign(cachedFiles, data.files)
await system.read(cachePath).then((data) => {
if (!data) return
if (data[0].tag === CACHE_TAG && data[0].v8tag === V8_TAG) {
Object.assign(cachedFiles, data[0].files)
}
})
await Promise.all(config.addonNames.map(evaluate))
safeWriteFile(cachePath, serialize({ tag: CACHE_TAG, v8tag: V8_TAG, files }))
safeWriteFile(cachePath, system.serialize({ tag: CACHE_TAG, v8tag: V8_TAG, files }))
for (const key in synthetics) {
exposeGlobal(key, synthetics[key].namespace)
}
Expand Down

0 comments on commit 58f8281

Please sign in to comment.