Skip to content

Commit ab7d63d

Browse files
authored
feat(runtime): check circular dependency (#1448)
* feat(runtime): check import * feat: add check circular dependency
1 parent d0cbbba commit ab7d63d

File tree

5 files changed

+38
-12
lines changed

5 files changed

+38
-12
lines changed

runtimes/nodejs/src/support/function-engine/cache.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ export class FunctionCache {
3333
* @param module
3434
* @returns
3535
*/
36-
static requireCloudFunction(moduleName: string): any {
36+
static requireCloudFunction(moduleName: string, fromModules?: string[]): any {
3737
const func = FunctionCache.cache.get(moduleName)
3838
assert(
3939
func,
4040
`require cloud function failed: function ${moduleName} not found`,
4141
)
42-
const funcRequire = new FunctionRequire(this.requireFunc)
43-
const module = funcRequire.load(func.name, func.source.compiled)
42+
const funcRequire = new FunctionRequire(this.requireFunc, fromModules)
43+
const module = funcRequire.load(func.name, func.source.compiled, fromModules)
4444
return module
4545
}
4646

@@ -87,12 +87,20 @@ export class FunctionCache {
8787
* @param module the module id. ex. `path`, `lodash`
8888
* @returns
8989
*/
90-
static requireFunc: RequireFuncType = (module: string): any => {
90+
static requireFunc: RequireFuncType = (module: string, fromModules?: string[]): any => {
9191
if (module === '@/cloud-sdk') {
9292
return require('@lafjs/cloud')
9393
}
9494
if (module.startsWith('@/')) {
95-
return FunctionCache.requireCloudFunction(module.replace('@/', ''))
95+
const cloudModule = module.replace('@/', '')
96+
97+
// check circular dependency
98+
const index = fromModules?.indexOf(cloudModule)
99+
if (index !== -1) {
100+
throw new Error(`Circular dependency detected: ${fromModules.slice(index).join(' -> ')} -> ${cloudModule}`)
101+
}
102+
103+
return FunctionCache.requireCloudFunction(cloudModule, fromModules)
96104
}
97105
return require(module) as any
98106
}

runtimes/nodejs/src/support/function-engine/engine.ts

+4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ export class FunctionEngine {
6666
*/
6767
wrap(code: string): string {
6868
const wrapped = `
69+
const require = (module) => {
70+
fromModules.push(__filename)
71+
return requireFunc(module, fromModules)
72+
}
6973
${code};
7074
const __main__ = exports.main || exports.default
7175
if(!__main__) { throw new Error('FunctionExecError: main function not found') }

runtimes/nodejs/src/support/function-engine/require.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ const defaultRequireFunction: RequireFuncType = (module): any => {
77

88
export class FunctionRequire {
99
requireFunc: RequireFuncType
10+
fromModules: string[]
1011

11-
constructor(requireFunc?: RequireFuncType) {
12+
constructor(requireFunc?: RequireFuncType, fromModules?: string[]) {
1213
this.requireFunc = requireFunc ?? defaultRequireFunction
14+
this.fromModules = fromModules ?? []
1315
}
1416

1517
/**
@@ -18,13 +20,13 @@ export class FunctionRequire {
1820
* @param code
1921
* @returns
2022
*/
21-
load(name: string, code: string): any {
23+
load(name: string, code: string, fromModules: string[]): any {
2224
const context = {
2325
__function_name: name,
2426
requestId: '',
2527
}
2628

27-
const sandbox = FunctionVm.buildSandbox(context, this.requireFunc)
29+
const sandbox = FunctionVm.buildSandbox(context, this.requireFunc, fromModules)
2830
const wrapped = this.warp(code)
2931
const script = FunctionVm.createVM(wrapped, {})
3032
return script.runInNewContext(sandbox, {})
@@ -37,6 +39,10 @@ export class FunctionRequire {
3739
*/
3840
warp(code: string): string {
3941
return `
42+
const require = (module) => {
43+
fromModules.push(__filename)
44+
return requireFunc(module, fromModules)
45+
}
4046
const exports = {};
4147
${code}
4248
exports;

runtimes/nodejs/src/support/function-engine/types.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Request, Response } from 'express'
44
import { ObjectId } from 'mongodb'
55
import WebSocket = require('ws')
66

7-
export type RequireFuncType = (module: string) => any
7+
export type RequireFuncType = (module: string, fromModules?: string[]) => any
88

99
/**
1010
* vm run context (global)
@@ -14,7 +14,7 @@ export interface RuntimeContext {
1414
module: { exports: Object }
1515
exports: Object
1616
console: FunctionConsole
17-
require: RequireFuncType
17+
requireFunc: RequireFuncType
1818
Buffer: typeof Buffer
1919
setTimeout: typeof setTimeout
2020
clearTimeout: typeof clearTimeout
@@ -26,7 +26,8 @@ export interface RuntimeContext {
2626
process: {
2727
env: { [key: string]: string }
2828
}
29-
global: RuntimeContext
29+
global: RuntimeContext,
30+
fromModules: string[]
3031
}
3132

3233
/**

runtimes/nodejs/src/support/function-engine/vm.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,25 @@ export class FunctionVm {
3434
static buildSandbox(
3535
functionContext: FunctionContext,
3636
requireFunc: any,
37+
fromModules?: string[],
3738
): RuntimeContext {
3839
const fconsole = new FunctionConsole(functionContext)
3940

4041
const _module = {
4142
exports: {},
4243
}
44+
45+
if (!fromModules) {
46+
fromModules = []
47+
}
48+
4349
const sandbox = {
4450
__context__: functionContext,
4551
__filename: functionContext.__function_name,
4652
module: _module,
4753
exports: _module.exports,
4854
console: fconsole,
49-
require: requireFunc,
55+
requireFunc: requireFunc,
5056
Buffer: Buffer,
5157
setImmediate: setImmediate,
5258
clearImmediate: clearImmediate,
@@ -60,6 +66,7 @@ export class FunctionVm {
6066
URL: URL,
6167
fetch: globalThis.fetch,
6268
global: null,
69+
fromModules: [...fromModules],
6370
}
6471

6572
sandbox.global = sandbox

0 commit comments

Comments
 (0)