wasm-idle 0.2.5 → 0.3.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.
- package/dist/clang/app.d.ts +44 -0
- package/dist/clang/app.js +229 -2
- package/dist/clang/index.d.ts +40 -9
- package/dist/clang/index.js +591 -15
- package/dist/clang/memory/memFs.d.ts +3 -1
- package/dist/clang/memory/memFs.js +12 -1
- package/dist/clang/memory/memory.d.ts +8 -4
- package/dist/clang/memory/memory.js +14 -0
- package/dist/clang/memory/tar.d.ts +1 -1
- package/dist/clang/memory/tar.js +1 -1
- package/dist/clang/wasm.d.ts +1 -1
- package/dist/clang/wasm.js +13 -4
- package/dist/clangd/config.d.ts +18 -0
- package/dist/clangd/config.js +14 -0
- package/dist/clangd/jsonStream.d.ts +9 -0
- package/dist/clangd/jsonStream.js +48 -0
- package/dist/clangd/session.d.ts +14 -0
- package/dist/clangd/session.js +94 -0
- package/dist/clangd/worker.d.ts +1 -0
- package/dist/clangd/worker.js +130 -0
- package/dist/debug/editor/index.d.ts +1 -0
- package/dist/debug/editor/index.js +1 -0
- package/dist/debug/editor/monaco.d.ts +18 -0
- package/dist/debug/editor/monaco.js +105 -0
- package/dist/debug/expression.d.ts +2 -0
- package/dist/debug/expression.js +182 -0
- package/dist/debug/index.d.ts +5 -0
- package/dist/debug/index.js +4 -0
- package/dist/debug/language/cpp.d.ts +2 -0
- package/dist/debug/language/cpp.js +7 -0
- package/dist/debug/language/index.d.ts +2 -0
- package/dist/debug/language/index.js +1 -0
- package/dist/debug/language/types.d.ts +6 -0
- package/dist/debug/language/types.js +1 -0
- package/dist/debug/locals.d.ts +2 -0
- package/dist/debug/locals.js +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/playground/clang.d.ts +8 -2
- package/dist/playground/clang.js +25 -4
- package/dist/playground/options.d.ts +38 -0
- package/dist/playground/options.js +1 -0
- package/dist/playground/python.d.ts +10 -2
- package/dist/playground/python.js +6 -3
- package/dist/playground/sandbox.d.ts +15 -2
- package/dist/playground/worker/clang.js +21 -4
- package/dist/playground/worker/python.js +196 -3
- package/dist/terminal/Terminal.svelte +35 -11
- package/dist/terminal/Terminal.svelte.d.ts +12 -9
- package/dist/terminal/index.d.ts +2 -0
- package/dist/terminal/theme.js +2 -2
- package/dist/terminal/types.d.ts +13 -0
- package/dist/terminal/types.js +1 -0
- package/package.json +42 -33
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export class MonacoDebugView {
|
|
2
|
+
Monaco;
|
|
3
|
+
editor;
|
|
4
|
+
breakpointDecorations;
|
|
5
|
+
inlineValueDecorations;
|
|
6
|
+
pausedLineWidget = null;
|
|
7
|
+
pausedLineWidgetNode = null;
|
|
8
|
+
mouseHandler = null;
|
|
9
|
+
breakpoints = [];
|
|
10
|
+
onBreakpointsChange;
|
|
11
|
+
constructor(MonacoModule, editor, onBreakpointsChange) {
|
|
12
|
+
this.Monaco = MonacoModule;
|
|
13
|
+
this.editor = editor;
|
|
14
|
+
this.onBreakpointsChange = onBreakpointsChange;
|
|
15
|
+
this.breakpointDecorations = editor.createDecorationsCollection();
|
|
16
|
+
this.inlineValueDecorations = editor.createDecorationsCollection();
|
|
17
|
+
this.mouseHandler = editor.onMouseDown((event) => {
|
|
18
|
+
if (event.target.type !== this.Monaco.editor.MouseTargetType.GUTTER_GLYPH_MARGIN &&
|
|
19
|
+
event.target.type !== this.Monaco.editor.MouseTargetType.GUTTER_LINE_DECORATIONS) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const line = event.target.position?.lineNumber;
|
|
23
|
+
if (!line)
|
|
24
|
+
return;
|
|
25
|
+
const next = this.breakpoints.includes(line)
|
|
26
|
+
? this.breakpoints.filter((value) => value !== line)
|
|
27
|
+
: [...this.breakpoints, line].sort((a, b) => a - b);
|
|
28
|
+
this.onBreakpointsChange?.(next);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
setBreakpoints(lines) {
|
|
32
|
+
this.breakpoints = lines;
|
|
33
|
+
this.breakpointDecorations.set(lines.map((line) => ({
|
|
34
|
+
range: new this.Monaco.Range(line, 1, line, 1),
|
|
35
|
+
options: {
|
|
36
|
+
isWholeLine: false,
|
|
37
|
+
glyphMarginClassName: 'debug-breakpoint-glyph',
|
|
38
|
+
glyphMarginHoverMessage: { value: 'Breakpoint' }
|
|
39
|
+
}
|
|
40
|
+
})));
|
|
41
|
+
}
|
|
42
|
+
setPauseState(pausedLine, locals, adapter) {
|
|
43
|
+
const lineMaxColumn = pausedLine ? this.editor.getModel()?.getLineMaxColumn(pausedLine) || 1 : 1;
|
|
44
|
+
if (pausedLine) {
|
|
45
|
+
const fontInfo = this.editor.getOption(this.Monaco.editor.EditorOption.fontInfo);
|
|
46
|
+
if (!this.pausedLineWidgetNode) {
|
|
47
|
+
this.pausedLineWidgetNode = document.createElement('div');
|
|
48
|
+
this.pausedLineWidgetNode.className = 'debug-paused-line-widget';
|
|
49
|
+
}
|
|
50
|
+
this.pausedLineWidgetNode.style.height = `${fontInfo.lineHeight}px`;
|
|
51
|
+
this.pausedLineWidgetNode.style.width = `${this.editor.getLayoutInfo().contentWidth}px`;
|
|
52
|
+
if (!this.pausedLineWidget) {
|
|
53
|
+
this.pausedLineWidget = {
|
|
54
|
+
getId: () => 'wasm-idle-debug-current-line',
|
|
55
|
+
getDomNode: () => this.pausedLineWidgetNode,
|
|
56
|
+
getPosition: () => ({
|
|
57
|
+
position: { lineNumber: pausedLine, column: 1 },
|
|
58
|
+
preference: [this.Monaco.editor.ContentWidgetPositionPreference.EXACT]
|
|
59
|
+
})
|
|
60
|
+
};
|
|
61
|
+
this.editor.addContentWidget(this.pausedLineWidget);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
this.editor.layoutContentWidget(this.pausedLineWidget);
|
|
65
|
+
}
|
|
66
|
+
this.editor.revealLineInCenterIfOutsideViewport(pausedLine);
|
|
67
|
+
}
|
|
68
|
+
else if (this.pausedLineWidget) {
|
|
69
|
+
this.editor.removeContentWidget(this.pausedLineWidget);
|
|
70
|
+
this.pausedLineWidget = null;
|
|
71
|
+
this.pausedLineWidgetNode = null;
|
|
72
|
+
}
|
|
73
|
+
const inlineLocals = pausedLine && adapter
|
|
74
|
+
? adapter.selectInlineLocals(this.editor.getModel()?.getLineContent(pausedLine) || '', locals)
|
|
75
|
+
: [];
|
|
76
|
+
this.inlineValueDecorations.set(pausedLine && inlineLocals.length
|
|
77
|
+
? [
|
|
78
|
+
{
|
|
79
|
+
range: new this.Monaco.Range(pausedLine, lineMaxColumn, pausedLine, lineMaxColumn),
|
|
80
|
+
options: {
|
|
81
|
+
showIfCollapsed: true,
|
|
82
|
+
after: {
|
|
83
|
+
content: ` ${inlineLocals
|
|
84
|
+
.map((variable) => `${variable.name} = ${variable.value}`)
|
|
85
|
+
.join(', ')}`,
|
|
86
|
+
inlineClassName: 'debug-inline-values',
|
|
87
|
+
inlineClassNameAffectsLetterSpacing: true
|
|
88
|
+
},
|
|
89
|
+
zIndex: 20
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
: []);
|
|
94
|
+
}
|
|
95
|
+
dispose() {
|
|
96
|
+
this.breakpointDecorations.clear();
|
|
97
|
+
this.inlineValueDecorations.clear();
|
|
98
|
+
if (this.pausedLineWidget)
|
|
99
|
+
this.editor.removeContentWidget(this.pausedLineWidget);
|
|
100
|
+
this.pausedLineWidget = null;
|
|
101
|
+
this.pausedLineWidgetNode = null;
|
|
102
|
+
this.mouseHandler?.dispose();
|
|
103
|
+
this.mouseHandler = null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
const operators = ['&&', '||', '==', '!=', '<=', '>=', '+', '-', '*', '/', '%', '<', '>', '!'];
|
|
2
|
+
export function evaluateDebugExpression(expression, variables) {
|
|
3
|
+
const source = expression.trim();
|
|
4
|
+
if (!source)
|
|
5
|
+
throw new Error('empty expression');
|
|
6
|
+
const values = new Map();
|
|
7
|
+
for (const variable of variables)
|
|
8
|
+
values.set(variable.name, variable.value);
|
|
9
|
+
const tokens = [];
|
|
10
|
+
for (let index = 0; index < source.length;) {
|
|
11
|
+
const character = source[index];
|
|
12
|
+
if (!character)
|
|
13
|
+
break;
|
|
14
|
+
if (/\s/.test(character)) {
|
|
15
|
+
index += 1;
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (character === '(' || character === ')') {
|
|
19
|
+
tokens.push({ type: 'paren', value: character });
|
|
20
|
+
index += 1;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const operator = operators.find((candidate) => source.startsWith(candidate, index));
|
|
24
|
+
if (operator) {
|
|
25
|
+
tokens.push({ type: 'operator', value: operator });
|
|
26
|
+
index += operator.length;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const number = source.slice(index).match(/^\d+(?:\.\d+)?/);
|
|
30
|
+
if (number?.[0]) {
|
|
31
|
+
tokens.push({ type: 'number', value: number[0] });
|
|
32
|
+
index += number[0].length;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const identifier = source.slice(index).match(/^[A-Za-z_]\w*/);
|
|
36
|
+
if (identifier?.[0]) {
|
|
37
|
+
if (identifier[0] === 'true' || identifier[0] === 'false') {
|
|
38
|
+
tokens.push({ type: 'boolean', value: identifier[0] === 'true' });
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
tokens.push({ type: 'identifier', value: identifier[0] });
|
|
42
|
+
}
|
|
43
|
+
index += identifier[0].length;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
throw new Error(`unsupported token near "${source.slice(index)}"`);
|
|
47
|
+
}
|
|
48
|
+
let cursor = 0;
|
|
49
|
+
const parsePrimary = () => {
|
|
50
|
+
const token = tokens[cursor];
|
|
51
|
+
if (!token)
|
|
52
|
+
throw new Error('unexpected end of expression');
|
|
53
|
+
if (token.type === 'number') {
|
|
54
|
+
cursor += 1;
|
|
55
|
+
return Number(token.value);
|
|
56
|
+
}
|
|
57
|
+
if (token.type === 'boolean') {
|
|
58
|
+
cursor += 1;
|
|
59
|
+
return token.value;
|
|
60
|
+
}
|
|
61
|
+
if (token.type === 'identifier') {
|
|
62
|
+
cursor += 1;
|
|
63
|
+
const value = values.get(token.value);
|
|
64
|
+
if (value == null || value === '?')
|
|
65
|
+
throw new Error('unavailable');
|
|
66
|
+
if (value === 'true' || value === 'false')
|
|
67
|
+
return value === 'true';
|
|
68
|
+
const parsed = Number(value);
|
|
69
|
+
if (!Number.isNaN(parsed))
|
|
70
|
+
return parsed;
|
|
71
|
+
throw new Error(`unsupported value for ${token.value}`);
|
|
72
|
+
}
|
|
73
|
+
if (token.type === 'paren' && token.value === '(') {
|
|
74
|
+
cursor += 1;
|
|
75
|
+
const result = parseOr();
|
|
76
|
+
const closing = tokens[cursor];
|
|
77
|
+
if (!closing || closing.type !== 'paren' || closing.value !== ')')
|
|
78
|
+
throw new Error('missing closing parenthesis');
|
|
79
|
+
cursor += 1;
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
throw new Error('expected value');
|
|
83
|
+
};
|
|
84
|
+
const parseUnary = () => {
|
|
85
|
+
const token = tokens[cursor];
|
|
86
|
+
if (token?.type === 'operator' && token.value === '!') {
|
|
87
|
+
cursor += 1;
|
|
88
|
+
return !Boolean(parseUnary());
|
|
89
|
+
}
|
|
90
|
+
if (token?.type === 'operator' && token.value === '-') {
|
|
91
|
+
cursor += 1;
|
|
92
|
+
return -Number(parseUnary());
|
|
93
|
+
}
|
|
94
|
+
if (token?.type === 'operator' && token.value === '+') {
|
|
95
|
+
cursor += 1;
|
|
96
|
+
return Number(parseUnary());
|
|
97
|
+
}
|
|
98
|
+
return parsePrimary();
|
|
99
|
+
};
|
|
100
|
+
const parseMul = () => {
|
|
101
|
+
let left = parseUnary();
|
|
102
|
+
while (true) {
|
|
103
|
+
const operator = tokens[cursor];
|
|
104
|
+
if (operator?.type !== 'operator' || !['*', '/', '%'].includes(operator.value))
|
|
105
|
+
return left;
|
|
106
|
+
cursor += 1;
|
|
107
|
+
const right = parseUnary();
|
|
108
|
+
if (operator.value === '*')
|
|
109
|
+
left = Number(left) * Number(right);
|
|
110
|
+
if (operator.value === '/')
|
|
111
|
+
left = Number(left) / Number(right);
|
|
112
|
+
if (operator.value === '%')
|
|
113
|
+
left = Number(left) % Number(right);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const parseAdd = () => {
|
|
117
|
+
let left = parseMul();
|
|
118
|
+
while (true) {
|
|
119
|
+
const operator = tokens[cursor];
|
|
120
|
+
if (operator?.type !== 'operator' || !['+', '-'].includes(operator.value))
|
|
121
|
+
return left;
|
|
122
|
+
cursor += 1;
|
|
123
|
+
const right = parseMul();
|
|
124
|
+
if (operator.value === '+')
|
|
125
|
+
left = Number(left) + Number(right);
|
|
126
|
+
if (operator.value === '-')
|
|
127
|
+
left = Number(left) - Number(right);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const parseCompare = () => {
|
|
131
|
+
let left = parseAdd();
|
|
132
|
+
while (true) {
|
|
133
|
+
const operator = tokens[cursor];
|
|
134
|
+
if (operator?.type !== 'operator' || !['<', '<=', '>', '>='].includes(operator.value))
|
|
135
|
+
return left;
|
|
136
|
+
cursor += 1;
|
|
137
|
+
const right = parseAdd();
|
|
138
|
+
if (operator.value === '<')
|
|
139
|
+
left = Number(left) < Number(right);
|
|
140
|
+
if (operator.value === '<=')
|
|
141
|
+
left = Number(left) <= Number(right);
|
|
142
|
+
if (operator.value === '>')
|
|
143
|
+
left = Number(left) > Number(right);
|
|
144
|
+
if (operator.value === '>=')
|
|
145
|
+
left = Number(left) >= Number(right);
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
const parseEquality = () => {
|
|
149
|
+
let left = parseCompare();
|
|
150
|
+
while (true) {
|
|
151
|
+
const operator = tokens[cursor];
|
|
152
|
+
if (operator?.type !== 'operator' || !['==', '!='].includes(operator.value))
|
|
153
|
+
return left;
|
|
154
|
+
cursor += 1;
|
|
155
|
+
const right = parseCompare();
|
|
156
|
+
if (operator.value === '==')
|
|
157
|
+
left = left === right;
|
|
158
|
+
if (operator.value === '!=')
|
|
159
|
+
left = left !== right;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
const parseAnd = () => {
|
|
163
|
+
let left = parseEquality();
|
|
164
|
+
while (tokens[cursor]?.type === 'operator' && tokens[cursor]?.value === '&&') {
|
|
165
|
+
cursor += 1;
|
|
166
|
+
left = Boolean(left) && Boolean(parseEquality());
|
|
167
|
+
}
|
|
168
|
+
return left;
|
|
169
|
+
};
|
|
170
|
+
const parseOr = () => {
|
|
171
|
+
let left = parseAnd();
|
|
172
|
+
while (tokens[cursor]?.type === 'operator' && tokens[cursor]?.value === '||') {
|
|
173
|
+
cursor += 1;
|
|
174
|
+
left = Boolean(left) || Boolean(parseAnd());
|
|
175
|
+
}
|
|
176
|
+
return left;
|
|
177
|
+
};
|
|
178
|
+
const result = parseOr();
|
|
179
|
+
if (cursor !== tokens.length)
|
|
180
|
+
throw new Error('unexpected trailing tokens');
|
|
181
|
+
return `${result}`;
|
|
182
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { cppDebugLanguageAdapter } from './cpp';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { DebugVariable } from '../../playground/options';
|
|
2
|
+
export interface DebugLanguageAdapter {
|
|
3
|
+
id: string;
|
|
4
|
+
evaluateExpression: (expression: string, locals: DebugVariable[]) => string;
|
|
5
|
+
selectInlineLocals: (lineText: string, locals: DebugVariable[]) => DebugVariable[];
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function selectInlineDebugLocals(lineText, locals) {
|
|
2
|
+
const sanitized = lineText
|
|
3
|
+
.replace(/\/\/.*$/g, ' ')
|
|
4
|
+
.replace(/"(?:\\.|[^"\\])*"/g, ' ')
|
|
5
|
+
.replace(/'(?:\\.|[^'\\])*'/g, ' ');
|
|
6
|
+
const identifiers = new Set(sanitized.match(/[A-Za-z_]\w*/g) || []);
|
|
7
|
+
return locals.filter((variable) => identifiers.has(variable.name));
|
|
8
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import Terminal from './terminal';
|
|
2
|
+
export { cppDebugLanguageAdapter, evaluateDebugExpression, MonacoDebugView, selectInlineDebugLocals } from './debug';
|
|
2
3
|
import playground from './playground';
|
|
3
4
|
export default Terminal;
|
|
4
5
|
export { playground };
|
|
6
|
+
export type { DebugCommand, DebugFrame, DebugSessionEvent, DebugVariable, SandboxExecutionOptions, TerminalControl } from './terminal';
|
|
7
|
+
export type { DebugLanguageAdapter } from './debug';
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import type { DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
|
|
1
2
|
import type { Sandbox } from './sandbox';
|
|
2
3
|
import type { Writable } from 'svelte/store';
|
|
3
4
|
declare class Clang implements Sandbox {
|
|
4
5
|
ts: number;
|
|
5
6
|
output: any;
|
|
7
|
+
ondebug?: (event: DebugSessionEvent) => void;
|
|
6
8
|
worker?: Worker;
|
|
7
9
|
buffer: SharedArrayBuffer;
|
|
10
|
+
debugBuffer: SharedArrayBuffer;
|
|
8
11
|
interruptBuffer: SharedArrayBuffer;
|
|
9
12
|
internalBuffer: string[];
|
|
10
13
|
begin: number;
|
|
@@ -12,11 +15,14 @@ declare class Clang implements Sandbox {
|
|
|
12
15
|
uid: number;
|
|
13
16
|
log: boolean;
|
|
14
17
|
exit: boolean;
|
|
15
|
-
load(path: string, code?: string, log?: boolean, args?:
|
|
18
|
+
load(path: string, code?: string, log?: boolean, args?: string[], _options?: SandboxExecutionOptions): Promise<void>;
|
|
16
19
|
write(input: string): void;
|
|
17
20
|
_write(input: string): void;
|
|
18
21
|
eof(): void;
|
|
19
|
-
run(code: string, prepare: boolean, log?: boolean, prog?: Writable<number
|
|
22
|
+
run(code: string, prepare: boolean, log?: boolean, prog?: Writable<number> | {
|
|
23
|
+
set?: (value: number) => void;
|
|
24
|
+
}, args?: string[], options?: SandboxExecutionOptions): Promise<boolean | string>;
|
|
25
|
+
debugCommand(command: DebugCommand): void;
|
|
20
26
|
terminate(): void;
|
|
21
27
|
clear(): Promise<void>;
|
|
22
28
|
}
|
package/dist/playground/clang.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
class Clang {
|
|
2
2
|
ts = Date.now();
|
|
3
3
|
output = null;
|
|
4
|
+
ondebug;
|
|
4
5
|
worker = null;
|
|
5
6
|
buffer = new SharedArrayBuffer(1024);
|
|
7
|
+
debugBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 4);
|
|
6
8
|
interruptBuffer = new SharedArrayBuffer(1);
|
|
7
9
|
internalBuffer = [];
|
|
8
10
|
begin = 0;
|
|
@@ -10,7 +12,7 @@ class Clang {
|
|
|
10
12
|
uid = 0;
|
|
11
13
|
log = true;
|
|
12
14
|
exit = true;
|
|
13
|
-
load(path, code = '', log = true, args = []) {
|
|
15
|
+
load(path, code = '', log = true, args = [], _options = {}) {
|
|
14
16
|
return new Promise(async (resolve) => {
|
|
15
17
|
this.log = log;
|
|
16
18
|
this.internalBuffer = [];
|
|
@@ -39,7 +41,7 @@ class Clang {
|
|
|
39
41
|
buffer.set(new Int32Array(data.buffer.slice(data.byteOffset), 0));
|
|
40
42
|
}
|
|
41
43
|
eof() { }
|
|
42
|
-
run(code, prepare, log = this.log, prog, args = []) {
|
|
44
|
+
run(code, prepare, log = this.log, prog, args = [], options = {}) {
|
|
43
45
|
this.exit = false;
|
|
44
46
|
return new Promise(async (resolve, reject) => {
|
|
45
47
|
if (!this.worker)
|
|
@@ -50,14 +52,17 @@ class Clang {
|
|
|
50
52
|
return reject('Worker not loaded');
|
|
51
53
|
if (_uid !== this.uid)
|
|
52
54
|
return (this.worker.onmessage = null);
|
|
53
|
-
const { id, output, results, log, error, buffer, progress } = event.data;
|
|
55
|
+
const { id, output, results, log, error, buffer, progress, debugEvent } = event.data;
|
|
54
56
|
if (buffer && this.internalBuffer.length)
|
|
55
57
|
this._write(this.internalBuffer.splice(0, 1)[0]);
|
|
56
58
|
if (output)
|
|
57
59
|
this.output(output);
|
|
60
|
+
if (debugEvent)
|
|
61
|
+
this.ondebug?.(debugEvent);
|
|
58
62
|
if (results) {
|
|
59
63
|
this.elapse = Date.now() - this.begin;
|
|
60
64
|
this.exit = true;
|
|
65
|
+
this.ondebug?.({ type: 'stop' });
|
|
61
66
|
resolve(results);
|
|
62
67
|
}
|
|
63
68
|
if (log)
|
|
@@ -76,15 +81,29 @@ class Clang {
|
|
|
76
81
|
code,
|
|
77
82
|
prepare,
|
|
78
83
|
buffer: this.buffer,
|
|
84
|
+
debugBuffer: this.debugBuffer,
|
|
79
85
|
interrupt: this.interruptBuffer,
|
|
80
86
|
context: {},
|
|
81
87
|
log,
|
|
82
|
-
args
|
|
88
|
+
args,
|
|
89
|
+
debug: !!options.debug,
|
|
90
|
+
breakpoints: [...(options.breakpoints || [])],
|
|
91
|
+
pauseOnEntry: !!options.pauseOnEntry
|
|
83
92
|
});
|
|
84
93
|
});
|
|
85
94
|
}
|
|
95
|
+
debugCommand(command) {
|
|
96
|
+
const control = new Int32Array(this.debugBuffer);
|
|
97
|
+
Atomics.store(control, 1, command === 'stepInto' ? 2 : command === 'nextLine' ? 3 : command === 'stepOut' ? 4 : 1);
|
|
98
|
+
Atomics.add(control, 0, 1);
|
|
99
|
+
Atomics.notify(control, 0);
|
|
100
|
+
this.ondebug?.({ type: 'resume', command });
|
|
101
|
+
}
|
|
86
102
|
terminate() {
|
|
87
103
|
new Uint8Array(this.interruptBuffer)[0] = 2;
|
|
104
|
+
const control = new Int32Array(this.debugBuffer);
|
|
105
|
+
Atomics.add(control, 0, 1);
|
|
106
|
+
Atomics.notify(control, 0);
|
|
88
107
|
}
|
|
89
108
|
async clear() {
|
|
90
109
|
this.terminate();
|
|
@@ -93,6 +112,8 @@ class Clang {
|
|
|
93
112
|
this.worker.onmessage = null;
|
|
94
113
|
const buffer = new Int32Array(this.buffer);
|
|
95
114
|
buffer.fill(0);
|
|
115
|
+
const debugBuffer = new Int32Array(this.debugBuffer);
|
|
116
|
+
debugBuffer.fill(0);
|
|
96
117
|
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
97
118
|
if (!this.exit) {
|
|
98
119
|
this.worker?.terminate?.();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type DebugCommand = 'continue' | 'stepInto' | 'nextLine' | 'stepOut';
|
|
2
|
+
export type DebugPauseReason = 'breakpoint' | 'entry' | 'step' | 'nextLine' | 'stepOut';
|
|
3
|
+
export interface DebugVariable {
|
|
4
|
+
name: string;
|
|
5
|
+
value: string;
|
|
6
|
+
}
|
|
7
|
+
export type DebugVariableKind = 'number' | 'bool' | 'array' | 'text';
|
|
8
|
+
export type DebugArrayElementKind = 'int' | 'float' | 'double' | 'bool' | 'char';
|
|
9
|
+
export interface DebugVariableMetadata {
|
|
10
|
+
slot: number;
|
|
11
|
+
name: string;
|
|
12
|
+
kind: DebugVariableKind;
|
|
13
|
+
fromLine: number;
|
|
14
|
+
toLine: number;
|
|
15
|
+
elementKind?: DebugArrayElementKind;
|
|
16
|
+
length?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface DebugFrame {
|
|
19
|
+
functionName: string;
|
|
20
|
+
line: number;
|
|
21
|
+
}
|
|
22
|
+
export type DebugSessionEvent = {
|
|
23
|
+
type: 'pause';
|
|
24
|
+
line: number;
|
|
25
|
+
reason: DebugPauseReason;
|
|
26
|
+
locals: DebugVariable[];
|
|
27
|
+
callStack: DebugFrame[];
|
|
28
|
+
} | {
|
|
29
|
+
type: 'resume';
|
|
30
|
+
command: DebugCommand;
|
|
31
|
+
} | {
|
|
32
|
+
type: 'stop';
|
|
33
|
+
};
|
|
34
|
+
export interface SandboxExecutionOptions {
|
|
35
|
+
debug?: boolean;
|
|
36
|
+
breakpoints?: number[];
|
|
37
|
+
pauseOnEntry?: boolean;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { Sandbox } from './sandbox';
|
|
2
|
+
import type { SandboxExecutionOptions } from './options';
|
|
2
3
|
declare class Python implements Sandbox {
|
|
3
4
|
ts: number;
|
|
4
5
|
output: any;
|
|
6
|
+
image?: (data: {
|
|
7
|
+
mime: string;
|
|
8
|
+
b64: string;
|
|
9
|
+
ts?: number;
|
|
10
|
+
}) => void;
|
|
5
11
|
worker: Worker;
|
|
6
12
|
buffer: SharedArrayBuffer;
|
|
7
13
|
interruptBuffer: SharedArrayBuffer;
|
|
@@ -9,11 +15,13 @@ declare class Python implements Sandbox {
|
|
|
9
15
|
begin: number;
|
|
10
16
|
elapse: number;
|
|
11
17
|
uid: number;
|
|
12
|
-
load(path: string, code?: string, log?: boolean): Promise<void>;
|
|
18
|
+
load(path: string, code?: string, log?: boolean, _args?: string[], _options?: SandboxExecutionOptions): Promise<void>;
|
|
13
19
|
write(input: string): void;
|
|
14
20
|
_write(input: string): void;
|
|
15
21
|
eof(): void;
|
|
16
|
-
run(code: string, prepare: boolean
|
|
22
|
+
run(code: string, prepare: boolean, _log?: boolean, _prog?: {
|
|
23
|
+
set?: (value: number) => void;
|
|
24
|
+
} | import('svelte/store').Writable<number>, _args?: string[], _options?: SandboxExecutionOptions): Promise<boolean | string>;
|
|
17
25
|
terminate(): void;
|
|
18
26
|
clear(): Promise<void>;
|
|
19
27
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
class Python {
|
|
2
2
|
ts = Date.now();
|
|
3
3
|
output = null;
|
|
4
|
+
image;
|
|
4
5
|
worker = null;
|
|
5
6
|
buffer = new SharedArrayBuffer(1024);
|
|
6
7
|
interruptBuffer = new SharedArrayBuffer(1);
|
|
@@ -8,7 +9,7 @@ class Python {
|
|
|
8
9
|
begin = 0;
|
|
9
10
|
elapse = 0;
|
|
10
11
|
uid = 0;
|
|
11
|
-
load(path, code = '', log = true) {
|
|
12
|
+
load(path, code = '', log = true, _args = [], _options = {}) {
|
|
12
13
|
return new Promise(async (resolve) => {
|
|
13
14
|
this.internalBuffer = [];
|
|
14
15
|
if (!this.worker) {
|
|
@@ -36,15 +37,17 @@ class Python {
|
|
|
36
37
|
buffer.set(new Int32Array(data.buffer.slice(data.byteOffset), 0));
|
|
37
38
|
}
|
|
38
39
|
eof() { }
|
|
39
|
-
run(code, prepare) {
|
|
40
|
+
run(code, prepare, _log = true, _prog, _args = [], _options = {}) {
|
|
40
41
|
return new Promise(async (resolve, reject) => {
|
|
41
42
|
const interrupt = new Uint8Array(this.interruptBuffer), _uid = ++this.uid;
|
|
42
43
|
const handler = (event) => {
|
|
43
44
|
if (_uid !== this.uid)
|
|
44
45
|
return (this.worker.onmessage = null);
|
|
45
|
-
const { id, output, results, log, error, buffer } = event.data;
|
|
46
|
+
const { id, output, results, log, error, buffer, type, data: payload } = event.data;
|
|
46
47
|
if (buffer && this.internalBuffer.length)
|
|
47
48
|
this._write(this.internalBuffer.splice(0, 1)[0]);
|
|
49
|
+
if (type === 'img' && payload)
|
|
50
|
+
this.image?.(payload);
|
|
48
51
|
if (output)
|
|
49
52
|
this.output(output);
|
|
50
53
|
if (results) {
|
|
@@ -1,12 +1,25 @@
|
|
|
1
|
+
import type { DebugCommand, DebugSessionEvent, SandboxExecutionOptions } from './options';
|
|
2
|
+
import type { Writable } from 'svelte/store';
|
|
3
|
+
type ProgressLike = Writable<number> | {
|
|
4
|
+
set?: (value: number) => void;
|
|
5
|
+
};
|
|
1
6
|
export interface Sandbox {
|
|
2
7
|
constructor: any;
|
|
3
8
|
eof: () => void;
|
|
4
|
-
load: (path: string, code?: string, log?: boolean) => Promise<void>;
|
|
5
|
-
run: (code: string, prepare: boolean, log?: boolean) => Promise<string>;
|
|
9
|
+
load: (path: string, code?: string, log?: boolean, args?: string[], options?: SandboxExecutionOptions) => Promise<void>;
|
|
10
|
+
run: (code: string, prepare: boolean, log?: boolean, prog?: ProgressLike, args?: string[], options?: SandboxExecutionOptions) => Promise<boolean | string>;
|
|
6
11
|
terminate: () => void;
|
|
7
12
|
clear: () => Promise<void>;
|
|
8
13
|
kill?: () => void;
|
|
9
14
|
write?: (data: string) => void;
|
|
10
15
|
output?: (data: string) => void;
|
|
16
|
+
ondebug?: (event: DebugSessionEvent) => void;
|
|
17
|
+
debugCommand?: (command: DebugCommand) => void;
|
|
18
|
+
image?: (data: {
|
|
19
|
+
mime: string;
|
|
20
|
+
b64: string;
|
|
21
|
+
ts?: number;
|
|
22
|
+
}) => void;
|
|
11
23
|
elapse?: number;
|
|
12
24
|
}
|
|
25
|
+
export {};
|