jslike 1.4.5 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -114,6 +114,20 @@ export function createGlobalEnvironment(env) {
114
114
  env.define('SyntaxError', SyntaxError);
115
115
  env.define('RangeError', RangeError);
116
116
 
117
+ // JSX Runtime Support
118
+ env.define('createElement', (type, props, ...children) => ({
119
+ $$typeof: Symbol.for('react.element'),
120
+ type,
121
+ props: {
122
+ ...props,
123
+ children: children.length === 0 ? undefined : children.length === 1 ? children[0] : children
124
+ },
125
+ key: props?.key ?? null,
126
+ ref: props?.ref ?? null
127
+ }));
128
+
129
+ env.define('Fragment', Symbol.for('react.fragment'));
130
+
117
131
  // Global console functions (shortcuts for console.log/warn/error)
118
132
  env.define('log', (...args) => {
119
133
  console.log(...args);
@@ -0,0 +1,212 @@
1
+ /**
2
+ * ExecutionController - Controls and monitors interpreter execution
3
+ *
4
+ * Provides pause/resume/abort capabilities and execution status introspection.
5
+ * Works with async evaluation only - sync evaluation can only abort.
6
+ */
7
+ export class ExecutionController {
8
+ constructor() {
9
+ this.state = 'idle'; // 'idle' | 'running' | 'paused' | 'aborted' | 'completed'
10
+ this.pauseRequested = false;
11
+ this.abortRequested = false;
12
+
13
+ // Debug info
14
+ this.stepCount = 0;
15
+ this.currentNode = null;
16
+ this.callStack = [];
17
+ this.currentEnv = null;
18
+
19
+ this._resolveResume = null;
20
+ }
21
+
22
+ // --- Control methods (called by user) ---
23
+
24
+ /**
25
+ * Request pause at next checkpoint. Only works during async evaluation.
26
+ */
27
+ pause() {
28
+ if (this.state === 'running') {
29
+ this.pauseRequested = true;
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Resume execution after pause.
35
+ */
36
+ resume() {
37
+ if (this.state === 'paused' && this._resolveResume) {
38
+ this.pauseRequested = false;
39
+ this._resolveResume();
40
+ this._resolveResume = null;
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Abort execution. Works for both sync and async evaluation.
46
+ * If paused, will resume and then abort.
47
+ */
48
+ abort() {
49
+ this.abortRequested = true;
50
+ // Also resume if paused to allow abort to take effect
51
+ if (this._resolveResume) {
52
+ this._resolveResume();
53
+ this._resolveResume = null;
54
+ }
55
+ }
56
+
57
+ // --- Status (called by user) ---
58
+
59
+ /**
60
+ * Get current execution status with full debug information.
61
+ * @returns {Object} Status object with state, stepCount, currentNode, callStack, and variables
62
+ */
63
+ getStatus() {
64
+ return {
65
+ state: this.state,
66
+ stepCount: this.stepCount,
67
+ currentNode: this.currentNode?.type || null,
68
+ callStack: [...this.callStack],
69
+ variables: this._getEnvironmentVariables()
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Check if abort has been requested.
75
+ * Compatible with AbortSignal interface.
76
+ */
77
+ get aborted() {
78
+ return this.abortRequested;
79
+ }
80
+
81
+ // --- Internal methods (called by interpreter) ---
82
+
83
+ /**
84
+ * Mark execution as started. Called by execute().
85
+ * @internal
86
+ */
87
+ _start() {
88
+ this.state = 'running';
89
+ this.stepCount = 0;
90
+ this.currentNode = null;
91
+ this.callStack = [];
92
+ this.pauseRequested = false;
93
+ // Note: don't reset abortRequested - allow pre-abort
94
+ }
95
+
96
+ /**
97
+ * Mark execution as completed. Called by execute().
98
+ * @internal
99
+ */
100
+ _complete() {
101
+ this.state = 'completed';
102
+ }
103
+
104
+ /**
105
+ * Push a function call onto the call stack.
106
+ * @internal
107
+ */
108
+ _pushCall(name) {
109
+ this.callStack.push(name);
110
+ }
111
+
112
+ /**
113
+ * Pop a function call from the call stack.
114
+ * @internal
115
+ */
116
+ _popCall() {
117
+ this.callStack.pop();
118
+ }
119
+
120
+ /**
121
+ * Set the current environment for variable introspection.
122
+ * @internal
123
+ */
124
+ _setEnv(env) {
125
+ this.currentEnv = env;
126
+ }
127
+
128
+ /**
129
+ * Async checkpoint - yields if paused, throws if aborted.
130
+ * Called at coarse granularity points (loops, function calls).
131
+ * @internal
132
+ */
133
+ async _checkpoint(node) {
134
+ this.stepCount++;
135
+ this.currentNode = node;
136
+
137
+ if (this.abortRequested) {
138
+ this.state = 'aborted';
139
+ const error = new Error('The operation was aborted');
140
+ error.name = 'AbortError';
141
+ throw error;
142
+ }
143
+
144
+ if (this.pauseRequested && this.state === 'running') {
145
+ this.state = 'paused';
146
+ await new Promise(resolve => { this._resolveResume = resolve; });
147
+ this.state = 'running';
148
+
149
+ // Check abort after resume (user may have called abort while paused)
150
+ if (this.abortRequested) {
151
+ this.state = 'aborted';
152
+ const error = new Error('The operation was aborted');
153
+ error.name = 'AbortError';
154
+ throw error;
155
+ }
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Sync abort check - only throws if aborted, cannot pause.
161
+ * Used in sync evaluate() path.
162
+ * @internal
163
+ */
164
+ _checkAbortSync() {
165
+ if (this.abortRequested) {
166
+ this.state = 'aborted';
167
+ const error = new Error('The operation was aborted');
168
+ error.name = 'AbortError';
169
+ throw error;
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Get all variables from current environment chain.
175
+ * @internal
176
+ */
177
+ _getEnvironmentVariables() {
178
+ if (!this.currentEnv) return {};
179
+ const vars = {};
180
+ let env = this.currentEnv;
181
+ while (env) {
182
+ if (env.vars) {
183
+ for (const [key, value] of env.vars) {
184
+ if (!(key in vars)) {
185
+ vars[key] = this._serializeValue(value);
186
+ }
187
+ }
188
+ }
189
+ env = env.parent;
190
+ }
191
+ return vars;
192
+ }
193
+
194
+ /**
195
+ * Serialize a value for status reporting.
196
+ * @internal
197
+ */
198
+ _serializeValue(value) {
199
+ if (value === undefined) return { type: 'undefined' };
200
+ if (value === null) return { type: 'null' };
201
+ if (typeof value === 'function' || (value && value.__isFunction)) {
202
+ return { type: 'function', name: value.name || 'anonymous' };
203
+ }
204
+ if (Array.isArray(value)) {
205
+ return { type: 'array', length: value.length };
206
+ }
207
+ if (typeof value === 'object') {
208
+ return { type: 'object', preview: Object.keys(value).slice(0, 5) };
209
+ }
210
+ return { type: typeof value, value };
211
+ }
212
+ }