Skip to content

Commit

Permalink
fix(): collect storyboard functions perf
Browse files Browse the repository at this point in the history
  • Loading branch information
weareoutman committed Jul 25, 2024
1 parent 7b793f9 commit 2e9c821
Show file tree
Hide file tree
Showing 8 changed files with 75 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ module.exports = [
},
{
path: "packages/brick-kit/dist/index.esm.js",
limit: "130 KB",
limit: "131 KB",
},
{
path: "packages/brick-types/dist/index.esm.js",
Expand Down
7 changes: 7 additions & 0 deletions declarations/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ interface Window {

/** Use Apache SkyWalking Client-side JavaScript exception and tracing library **/
USE_SKYWALKING_ANALYSIS?: boolean;

STORYBOARD_FUNCTIONS_PERF?: {
name: string;
source: string;
/** 函数执行耗时列表,单位毫秒 */
durations: number[];
}[];
}

declare const __webpack_public_path__: string;
Expand Down
2 changes: 1 addition & 1 deletion etc/brick-kit.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ export abstract class ModalElement extends UpdatingElement {
// Warning: (ae-internal-missing-underscore) The name "PartialMicroApp" should be prefixed with an underscore because the declaration is marked as @internal
//
// @internal (undocumented)
export type PartialMicroApp = Pick<MicroApp, "id" | "isBuildPush">;
export type PartialMicroApp = Pick<MicroApp, "id" | "isBuildPush" | "currentVersion" | "config">;

// Warning: (ae-forgotten-export) The symbol "TransformOptions" needs to be exported by the entry point index.d.ts
// Warning: (ae-internal-missing-underscore) The name "preprocessTransformProperties" should be prefixed with an underscore because the declaration is marked as @internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ describe("StoryboardFunctions", () => {
],
{
id: "my-app",
config: {
settings: {
perfStoryboardFunctions: true,
},
},
}
);
expect(fn.sayHello({ en: "world", zh: "世界" })).toBe(
Expand Down Expand Up @@ -113,6 +118,8 @@ describe("StoryboardFunctions", () => {
expect(fn.checkPermissions("my:action-b")).toBe(false);
expect(fn.getUniqueId()).not.toBe("42");
expect(fn.getUniqueId("test-")).not.toBe("test-42");

expect(window.STORYBOARD_FUNCTIONS_PERF?.length).toBe(6);
});

it("should register no functions", () => {
Expand Down
42 changes: 37 additions & 5 deletions packages/brick-kit/src/core/StoryboardFunctionRegistryFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,14 @@ export interface FunctionCoverageSettings {
}

/** @internal */
export type PartialMicroApp = Pick<MicroApp, "id" | "isBuildPush">;
export type PartialMicroApp = Pick<
MicroApp,
"id" | "isBuildPush" | "currentVersion" | "config"
>;

interface AppConfigSettings {
perfStoryboardFunctions?: boolean;
}

/** @internal */
export function StoryboardFunctionRegistryFactory({
Expand All @@ -77,13 +84,20 @@ export function StoryboardFunctionRegistryFactory({
}) as ReadonlyStoryboardFunctions;

let currentApp: PartialMicroApp;
let needPerf = false;

function registerStoryboardFunctions(
functions: StoryboardFunction[],
app?: PartialMicroApp
): void {
if (app) {
currentApp = app;
needPerf = (app?.config?.settings as AppConfigSettings)
?.perfStoryboardFunctions;
if (needPerf) {
// Clear perf data when initialize functions.
window.STORYBOARD_FUNCTIONS_PERF = [];
}
}
registeredFunctions.clear();
if (Array.isArray(functions)) {
Expand Down Expand Up @@ -130,10 +144,19 @@ export function StoryboardFunctionRegistryFactory({
}),
!!collectCoverage
),
hooks: collector && {
beforeEvaluate: collector.beforeEvaluate,
beforeCall: collector.beforeCall,
beforeBranch: collector.beforeBranch,
hooks: {
perfCall: needPerf
? (duration) => {
perf(name, fn.source, duration);
}
: undefined,
...(collector
? {
beforeEvaluate: collector.beforeEvaluate,
beforeCall: collector.beforeCall,
beforeBranch: collector.beforeBranch,
}
: null),
},
}) as SimpleFunction;
fn.processed = true;
Expand All @@ -154,3 +177,12 @@ export function StoryboardFunctionRegistryFactory({
},
};
}

function perf(name: string, source: string, duration: number): void {
const list = (window.STORYBOARD_FUNCTIONS_PERF ??= []);
let data = list.find((item) => item.name === name);
if (!data) {
list.push((data = { name, durations: [], source }));
}
data.durations.push(Math.round(duration * 1000));
}
2 changes: 1 addition & 1 deletion packages/brick-utils/src/scanAppInStoryboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function scanAppGetMenuInStoryboard(storyboard: Storyboard): string[] {
matchExpressionString: matchAppGetMenu,
}
);
// // `APP` is not available in storyboard functions
// `APP` is not available in storyboard functions
return Array.from(collection);
}

Expand Down
4 changes: 4 additions & 0 deletions packages/cook/src/cook.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,14 @@ describe("evaluate", () => {
const beforeEvaluate = jest.fn();
const beforeCall = jest.fn();
const beforeBranch = jest.fn();
const perfCall = jest.fn();
const func = cook(funcAst, source, {
globalVariables,
hooks: {
beforeEvaluate,
beforeCall,
beforeBranch,
perfCall,
},
}) as SimpleFunction;

Expand Down Expand Up @@ -350,6 +352,7 @@ describe("evaluate", () => {
}),
"if"
);
expect(perfCall).toBeCalledTimes(1);

jest.clearAllMocks();
func(0);
Expand Down Expand Up @@ -386,6 +389,7 @@ describe("evaluate", () => {
}),
"else"
);
expect(perfCall).toBeCalledTimes(1);
});

it("support RegExp", () => {
Expand Down
23 changes: 17 additions & 6 deletions packages/cook/src/cook.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {
ArrayPattern,
ArrowFunctionExpression,
BlockStatement,
CallExpression,
CatchClause,
DoWhileStatement,
Expand Down Expand Up @@ -84,6 +83,7 @@ export interface CookHooks {
beforeEvaluate?(node: EstreeNode): void;
beforeCall?(node: EstreeNode): void;
beforeBranch?(node: EstreeNode, branch: "if" | "else"): void;
perfCall?(duration: number): void;
}

/** For next-core internal usage only. */
Expand Down Expand Up @@ -1415,9 +1415,10 @@ export function cook(
// https://tc39.es/ecma262/#sec-runtime-semantics-instantiatefunctionobject
function InstantiateFunctionObject(
func: FunctionDeclaration,
scope: EnvironmentRecord
scope: EnvironmentRecord,
isRoot?: boolean
): FunctionObject {
return OrdinaryFunctionCreate(func, scope, true);
return OrdinaryFunctionCreate(func, scope, true, isRoot);
}

// https://tc39.es/ecma262/#sec-runtime-semantics-instantiateordinaryfunctionexpression
Expand Down Expand Up @@ -1454,11 +1455,21 @@ export function cook(
| FunctionExpression
| ArrowFunctionExpression,
scope: EnvironmentRecord,
isConstructor: boolean
isConstructor: boolean,
isRoot?: boolean
): FunctionObject {
const F = function () {
const perf = isRoot && hooks.perfCall;
let start: number;
if (perf) {
start = performance.now();
}
// eslint-disable-next-line prefer-rest-params
return CallFunction(F, arguments);
const result = CallFunction(F, arguments);
if (perf) {
perf(performance.now() - start);
}
return result;
} as FunctionObject;
Object.defineProperties(F, {
[SourceNode]: {
Expand Down Expand Up @@ -1738,7 +1749,7 @@ export function cook(
const [fn] = collectBoundNames(rootAst);
// Create an immutable binding for the root function.
rootEnv.CreateImmutableBinding(fn, true);
const fo = InstantiateFunctionObject(rootAst, rootEnv);
const fo = InstantiateFunctionObject(rootAst, rootEnv, true);
rootEnv.InitializeBinding(fn, fo);
return fo;
}

0 comments on commit 2e9c821

Please sign in to comment.