Algorithm To Split Safe Code
Code Splitting Algorithm
Written for AI agents. See Log Methodology Note below for details.
The algorithm aims to enable the splitting of a function into two functions - a safe and unsafe function based on configurable patterns.
Given a function
({ event, viewState, coordinate }) => {
if (event.keyCode === ENTER_KEY) {
event.preventDefault();
let newValue = newTodo();
let val = newValue.trim();
if (val) setTodos(/*... some updated todos */);
setNewTodo('');
}
};
and given
- a pattern that allows
event.preventDefault - given
ENTER_KEYis a constant newTodo,setTodosandsetNewTodoare state functions considered unsafe code
split into
// safe function, runs in the main thread / window
({ event, viewState, coordinate }) => {
if (event.keyCode === ENTER_KEY) {
event.preventDefault();
}
return { $0: event.keyCode };
};
// unsafe function, runs in sandbox
({ event, viewState, coordinate }) => {
if ($0 === ENTER_KEY) {
let newValue = newTodo();
let val = newValue.trim();
if (val) setTodos(/*... some updated todos */);
setNewTodo('');
}
};
This algorithm performs the above function split.
The algorithm
We use the notation of
Statement- a line of code, or multiple lines as part of a code block{}.Expression- a code fragment that returns a value.
The algorithm has the following steps
- Compute
Variableassignment chains (accessa.b, assignmenta = b, compositions={a: b}and decomposition{a: b} =) usingNameBindingResolver - Compute
Statements dependencies based on the aboveVariables. - Compute which
Statements are safe based on statement dependencies and checking if they are composed of all safeExpressions - Splitting the function based on safe and unsafe
StatementsandStatementdependencies.
Given the above example:
1. Compute Variables

The variables in the code above (in green)
| Variable | path | root |
|---|---|---|
ENTER_KEY |
[] |
AST-constant |
event |
['event'] |
AST-params[0] |
viewState |
['viewState'] |
AST-params[0] |
coordinate |
['coordinate'] |
AST-params[0] |
newValue |
[] |
AST-function-call |
val |
[] |
AST-function-call |
In blue are property access chains using those variables, which are then used to compute the source value for each variable, dependencies and pattern matching
Note that variables do not analyze the actual AST - given a function call or a trinary operator,
the connection between the parts is kept modelled as the AST root. Such analysis is done below at the isSafe function.
The NameBindingResolver
The NameBindingResolver is a utility to calculate the origins of Identifier into Variables given property access,
assignment, deconstruction, etc.
The basic element is the Variable which explains an Identifier.
export interface Variable {
name?: string; // optional variable name - an identifier
accessedFrom?: Variable; // the variable it was accessed from using property access
accessedByProperty?: string; // the property used for the access
assignedFrom?: Variable; // another variable it was assigned from using a=b
root?: VariableRoot; // the root node in the TS AST that is the root of the access chain
properties?: Variable[]; // variables created using deconstruction
}
The Varaible abstraction captures a number of relationships of values access, assignment or decomposition.
a.bis captured usingaccessedFrom: a, accessedByProperty: 'b'.func(a: Type)is captured usingroot: AST-Parameter.let c = bis captured usingassignedFrom: b.let {d} = cis captured usingaccessedByProperty: d, accessedFrom: c.let z = {y: a}is captured usingassignedFrom: {properties: [{name: y, assignedFrom: a}]}
The flattenVariable function
The function takes a variable and returns a flattened access path, resolving access, assignment composition and decomposition.
The function result is
export interface FlattenedAccessChain {
path: string[];
root: VariableRoot;
}
Two variables are the same if their FlattenedAccessChain is equal.
The FlattenedAccessChain is used to compare compiler patterns with expressions (identifiers and PropertyAccessChains)
to determine if the accessed value is the same as the pattern, and those considered safe.
SourceFileNameBindingResolver
We can create addition to the AST such that for each Identifier we can ask what is the corresponding Variable in a source file.
e.g.
let variable: Variable = sourceFileNameBindingResolver.explain(astIdentifier);
Internally the SourceFileNameBindingResolver maintains a map of ast nodes to NameBindingResolver,
creating NameBindingResolver for each function, for, switch or block statements,
and then given an Identifier can lookup the closest parent NameBindingResolver and resolve the Identifier.
2. Compute Statement dependencies

- each nested statement is automatically dependent on it's parent statement (structural dependency)
- the
ifcondition expressionevent.keyCode === ENTER_KEYdepends on the variableeventfrom the parent statement - the
event.preventDefault()depends on the two levels up statement due to theeventvariable - the statement
let val = newValue.trim()depends on the previous line due to the variablenewValue;
The SourceFileStatementDependencies
The SourceFileStatementDependencies models the dependencies between statements with the above login.
It provides the dependencies as
export interface StatementDependencies {
id: number; // internal id, can be used for testing
parent?: Statement; // the parent statement
statement: Statement; // the current statement
dependsOn: Set<StatementDependencies>; // what it depends on
isDependencyFor: Set<StatementDependencies>; // what depends on it
}
3. Compute isSafe for statements
The first part to compute isSafe is to understand it is relevant to coding patterns.
Given code patterns, we can determine if an expression is safe.
coding patterns
We define 4 types of coding patterns -
CompilePatternType.RETURN:A pattern that returns a value. It can be replaced with a variable resolved in the main environment.
import { JayEvent } from 'jay-runtime'; function inputValuePattern(jayEvent: JayEvent<any, any>) { return jayEvent.event.target.value; }CompilePatternType.CALLA pattern that is a function call. With a match, the statement has to be fully moved to the main context.
import { JayEvent } from 'jay-runtime'; function eventPreventDefault(jayEvent: JayEvent<any, any>) { jayEvent.event.preventDefault(); }CompilePatternType.CHAINABLE_CALLSimilar to a call pattern, but also returns a value, which can be used for chaining
function stringReplace(value: string, regex: RegExp, replacement: string): string { return value.replace(regex, replacement); }CompilePatternType.ASSIGNMENTPattern identifying an assignment to a value in the main context.
import { JayEvent } from 'jay-runtime'; function setInputValue(jayEvent: JayEvent<any, any>, value: string): string { jayEvent.event.target.value = value; }
From the patterns above we extract
- the pattern type - return, call, call & return or assignment
- the pattern statement access chain - the left side in the function above.
- the pattern input types
- The first is the left hand side root type
- The second an on are function call parameters
- the pattern output type
- target environment -
main, orany
types are supported with patterns by tracking the type name and import path. We stringify a type designator such that we can easily compare types.
In the above, JayEvent which imported from jay-runtime is stringify
into jay-runtime.JayEvent. native types are represented as is - string, number, etc.
Analyzing statements - anayzeStatement(statement)
When looking at a statement considering if it is safe, we need to consider the expressions that build the statement, and we can get a few results
- the statement expressions are all matching patterns, and can be moved to the main context - safe statement
- the statement is using patterns that mandate the statement has to run in the main context
- the statement is using an expression that does not match a pattern, and has to run in the sandbox
- the statement is using an expression that matches a pattern, and this expression has to run in the main context, while the rest of the statement remains in the sandbox
Modeling all the statuses above, we have
- Statement that has to run in main
- Statement that has can run in both contexts
- Statement that has to run in Sandbox
- Statement that has to run in Sandbox, but has sub-expressions who can run in main
Analysing expressions - isSafeExpression(expression)
We consider an expression as safe using isSafeExpression if is matching one or more patterns
organized as a chain. We also restrict expression to only include property access,
function calls and trinary expressions.
For each statement, we derive the target environment as one of
JayTargetEnv.main, JayTargetEnv.any or JayTargetEnv.sandbox.
pattern matching can only include
- property access -
a.b.c - property array access using constant -
a['b'].c - type casting -
(a.b as B).c - function calls -
a(), in which case the function params are also checked usingisSafeExpression - trinary expressions -
a?b:c, in which case both branches are also checked usingisSafeExpression
- property access -
We do not support expressions including anything else (by design), including
- inline function definitions -
(function () {}).a - property array access using expression -
a[b].c - async expressions -
(async a.b()).c - callback expressions -
a.map(_ => f(_)).b
- inline function definitions -
we support chaining expressions based on expression return type.
Given two expressions
a.b, returning stringandb.c, b expected to be string, we will matcha.b.cto the chaining of the two expressions.for each expression matching, we extract the target environment. we have two options - requires
JayTargetEnv.main, or supports both main and sandboxJayTargetEnv.any. we, by design, assume that anything that does not match a pattern requires sandbox.in the case of pattern chaining, if one pattern is
JayTargetEnv.mainand anotherJayTargetEnv.anythe whole expression is consideredJayTargetEnv.mainIf the statement is an assignment statement and the analysis so far marked it as
JayTargetEnv.any, the statement is consideredJayTargetEnv.main.
the AnalysisResult for Statements and Expressions
The SourceFileStatementAnalyzer performs the above analysis, and given
a Statement or Expression can give the analysis result.
interface MatchedReadPattern {
patterns: CompiledPattern[];
expression: Expression;
testId: number;
}
interface AnalysisResult {
targetEnv: JayTargetEnv;
matchedPatterns: MatchedReadPattern[];
}
targetEnv- what is the target environment for this expression or statementmatchedPatterns- the patterns matched for expressions of the statement. even if the statement target environment isJayTargetEnv.sandbox, the statement may have subRETURNexpressions which can be replaced with variables resolved in theJayTargetEnv.main.expression- which expression that was matched.pattern- the one or more patterns the expression matched. Potentially, we may have a chain of pattern matched.
Notes:
- do we need support for chaining with read patterns? probably.
- the statement analysis is based on nested expression analysis, not including nested statement analysis
unsupported JS features
The following Statement types are not supported and code using them will be considered JayTargetEnv.sandbox:
withforfor infor ofwhile- class declaration
- function declaration
Log Methodology Note
Note: These design logs are written primarily for AI agents as part of the Design Log methodology and made accessible here for human readers. The language and structure are optimized for machine consumption — expect precise, specification-style prose rather than narrative documentation.