Compare commits

...

5 Commits

Author SHA1 Message Date
b97e4bf163
improve: UserValue API 2024-12-28 13:20:04 +02:00
9ce0504948
fix: debugger concurrency issues 2024-12-28 13:19:53 +02:00
4c53689d9c
feat: some behavioral fixes 2024-12-28 13:19:35 +02:00
6c8c329992
feat: improve transpiler infrastructure 2024-12-28 13:19:02 +02:00
74f08b7483
small stdlib improvements
Some checks failed
tagged-release / Tagged Release (push) Failing after 7m8s
2024-12-28 13:17:45 +02:00
24 changed files with 329 additions and 87 deletions

View File

@ -31,7 +31,7 @@ task compileEnv(type: NpmTask) {
} }
task compileTypescript(type: NpmTask) { task compileTypescript(type: NpmTask) {
inputs.files('rollup.config.js'); inputs.files('rollup.config.js');
inputs.dir('src/lib/ts'); inputs.dir('src/lib/transpiler');
outputs.files("build/js/ts.js"); outputs.files("build/js/ts.js");
// nom nom tasty ram // nom nom tasty ram
environment.put("NODE_OPTIONS", "--max-old-space-size=4096"); environment.put("NODE_OPTIONS", "--max-old-space-size=4096");

View File

@ -1,10 +1,17 @@
{ {
"scripts": { "scripts": {
"build-env": "rollup -c --environment INPUT:src/lib/libs/_entry.ts,OUTPUT:build/js/index.js,POLYFILLS:src/lib/libs/polyfills", "build-env": "rollup -c --environment INPUT:src/lib/libs/_entry.ts,OUTPUT:build/js/index.js,POLYFILLS:src/lib/libs/polyfills",
"build-ts": "rollup -c --environment INPUT:src/lib/ts/_entry.ts,OUTPUT:build/js/ts.js" "build-ts": "rollup -c --environment INPUT:src/lib/transpiler/_entry.ts,OUTPUT:build/js/ts.js"
}, },
"dependencies": { "dependencies": {
"@babel/core": "^7.26.0",
"@babel/runtime": "^7.26.0", "@babel/runtime": "^7.26.0",
"@babel/standalone": "^7.26.4",
"@rollup/plugin-json": "^6.1.0",
"@types/babel__preset-env": "^7.9.7",
"@types/babel__standalone": "^7.1.9",
"@types/coffeescript": "^2.5.7",
"coffeescript": "^2.7.0",
"typescript": "^5.7.2" "typescript": "^5.7.2"
}, },
"devDependencies": { "devDependencies": {

View File

@ -4,6 +4,7 @@ const typescript = require("@rollup/plugin-typescript");
const babel = require("@rollup/plugin-babel"); const babel = require("@rollup/plugin-babel");
const commonjs = require("@rollup/plugin-commonjs"); const commonjs = require("@rollup/plugin-commonjs");
const nodeResolve = require("@rollup/plugin-node-resolve"); const nodeResolve = require("@rollup/plugin-node-resolve");
const json = require("@rollup/plugin-json");
const { resolve } = require("path"); const { resolve } = require("path");
const shouldMinify = () => false; const shouldMinify = () => false;
@ -21,6 +22,7 @@ const construct = (input, output) => defineConfig({
}, },
commonjs(), commonjs(),
nodeResolve(), nodeResolve(),
json(),
babel({ babel({
extensions: [], extensions: [],
exclude: ["node_modules/**"], exclude: ["node_modules/**"],
@ -107,6 +109,14 @@ const construct = (input, output) => defineConfig({
path: "null", path: "null",
os: "null", os: "null",
inspector: "null", inspector: "null",
tty: "null",
util: "null",
assert: "null",
url: "null",
"@babel/preset-typescript/package.json": "null",
module: "null",
process: "null",
v8: "null",
}, },
// plugins: [babel.getBabelOutputPlugin({ // plugins: [babel.getBabelOutputPlugin({
// allowAllFormats: true, // allowAllFormats: true,

View File

@ -13,6 +13,7 @@ import { Date } from "./date.ts";
import { Math as _Math } from "./math.ts"; import { Math as _Math } from "./math.ts";
import { Set, WeakSet } from "./set.ts"; import { Set, WeakSet } from "./set.ts";
import { JSON } from "./json.ts"; import { JSON } from "./json.ts";
import { console } from "./console.ts";
import { encodeURI, encodeURIComponent } from "./url.ts"; import { encodeURI, encodeURIComponent } from "./url.ts";
import { Promise } from "./promise.ts"; import { Promise } from "./promise.ts";
@ -27,6 +28,8 @@ function fixup<T extends Function>(clazz: T) {
return clazz; return clazz;
} }
object.setPrototype(target, Object.prototype);
object.defineField(target, "undefined", { e: false, c: false, w: false, v: void 0 }); object.defineField(target, "undefined", { e: false, c: false, w: false, v: void 0 });
target.Symbol = fixup(Symbol); target.Symbol = fixup(Symbol);
@ -52,6 +55,8 @@ target.Date = fixup(Date);
target.Promise = fixup(Promise); target.Promise = fixup(Promise);
target.Math = object.setPrototype(_Math, Object.prototype); target.Math = object.setPrototype(_Math, Object.prototype);
target.JSON = object.setPrototype(JSON, Object.prototype); target.JSON = object.setPrototype(JSON, Object.prototype);
target.console = object.setPrototype(console, Object.prototype);
target.TYPED_ARRAY_SUPPORT = false;
target.parseInt = Number.parseInt; target.parseInt = Number.parseInt;
target.parseFloat = Number.parseFloat; target.parseFloat = Number.parseFloat;

View File

@ -157,6 +157,16 @@ export const Array = (() => {
return res; return res;
} }
public reduce(this: any[], cb: Function, initial: any) {
let i = 0;
if (arguments.length <= 1) initial = this[i++];
for (; i < this.length; i++) {
initial = cb(initial, this[i], i, this);
}
return initial;
}
public some(this: any[], cb: Function, self?: any) { public some(this: any[], cb: Function, self?: any) {
for (let i = 0; i < this.length; i++) { for (let i = 0; i < this.length; i++) {
if (i in this && func.invoke(cb, self, [this[i], i, this])) return true; if (i in this && func.invoke(cb, self, [this[i], i, this])) return true;
@ -197,6 +207,13 @@ export const Array = (() => {
return -1; return -1;
} }
public includes(this: any[], val: any) {
for (let i = 0; i < this.length; i++) {
if (i in this && this[i] === val) return i;
}
return false;
}
public sort(this: any[], cb?: Function) { public sort(this: any[], cb?: Function) {
cb ||= (a: any, b: any) => { cb ||= (a: any, b: any) => {
@ -207,6 +224,18 @@ export const Array = (() => {
return object.sort(this, cb); return object.sort(this, cb);
} }
public reverse(this: any[]) {
const mid = this.length >> 1;
const end = this.length - 1;
for (let i = 0; i < mid; i++) {
const tmp = this[i];
this[i] = this[end - i];
this[end - i] = tmp;
}
return this;
}
public [symbols.iterator](this: any[]) { public [symbols.iterator](this: any[]) {
let i = 0; let i = 0;

11
src/lib/libs/console.ts Normal file
View File

@ -0,0 +1,11 @@
import { func, json, object } from "./primordials";
export const console = {};
function method(name: string, func: Function) {
object.defineField(console, name, { c: true, e: false, w: true, v: func });
}
method("log", function log() {
func.invoke(print, null, arguments as any);
});

View File

@ -55,3 +55,6 @@ method("ceil", function floor(val: number) {
method("pow", function pow(a: number, b: number) { method("pow", function pow(a: number, b: number) {
return number.pow(a, b); return number.pow(a, b);
}); });
method("log", function log(val: number) {
return number.log(val);
});

View File

@ -13,9 +13,10 @@ export const Object = (() => {
else if (this === null) return "[object Null]"; else if (this === null) return "[object Null]";
else if (typeof this === "object") { else if (typeof this === "object") {
if (symbols.toStringTag in this) return "[object " + (this as any)[symbols.toStringTag] + "]"; if (symbols.toStringTag in this) return "[object " + (this as any)[symbols.toStringTag] + "]";
else if (object.isArray(this)) return "[object Array]";
else return "[object Object]"; else return "[object Object]";
} }
else if (typeof this === "number" || this instanceof Object) return "[object Object]"; else if (typeof this === "number" || this instanceof Number) return "[object Number]";
else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]"; else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]";
else if (typeof this === "string" || this instanceof String) return "[object String]"; else if (typeof this === "string" || this instanceof String) return "[object String]";
else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]"; else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]";
@ -46,6 +47,12 @@ export const Object = (() => {
public static getOwnPropertyDescriptor(obj: object, key: any) { public static getOwnPropertyDescriptor(obj: object, key: any) {
return object.getOwnMember(obj, key); return object.getOwnMember(obj, key);
} }
public static getOwnPropertyNames(obj: object): string[] {
return object.getOwnMembers(obj, false);
}
public static getOwnPropertySymbols(obj: object): symbol[] {
return object.getOwnSymbolMembers(obj, false);
}
public static defineProperty(obj: object, key: string | symbol, desc: PropertyDescriptor) { public static defineProperty(obj: object, key: string | symbol, desc: PropertyDescriptor) {
if (obj === null || typeof obj !== "function" && typeof obj !== "object") { if (obj === null || typeof obj !== "function" && typeof obj !== "object") {
@ -84,8 +91,8 @@ export const Object = (() => {
return obj; return obj;
} }
public static defineProperties(obj: object, desc: PropertyDescriptorMap) { public static defineProperties(obj: object, desc: PropertyDescriptorMap) {
const keys = object.getOwnMembers(desc, false) as ((keyof typeof obj) & string)[]; const keys = object.getOwnMembers(desc, true) as ((keyof typeof obj) & string)[];
const symbols = object.getOwnSymbolMembers(desc, false) as ((keyof typeof obj) & symbol)[]; const symbols = object.getOwnSymbolMembers(desc, true) as ((keyof typeof obj) & symbol)[];
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
Object.defineProperty(obj, keys[i], desc[keys[i]]); Object.defineProperty(obj, keys[i], desc[keys[i]]);
@ -119,7 +126,6 @@ export const Object = (() => {
return target; return target;
} }
public static setPrototypeOf(obj: object, proto: object | null) { public static setPrototypeOf(obj: object, proto: object | null) {
object.setPrototype(obj, proto!); object.setPrototype(obj, proto!);
} }
@ -129,8 +135,8 @@ export const Object = (() => {
public static keys(obj: any) { public static keys(obj: any) {
const res: any[] = []; const res: any[] = [];
const keys = object.getOwnMembers(obj, false); const keys = object.getOwnMembers(obj, true);
const symbols = object.getOwnSymbolMembers(obj, false); const symbols = object.getOwnSymbolMembers(obj, true);
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
res[res.length] = keys[i]; res[res.length] = keys[i];
@ -143,8 +149,8 @@ export const Object = (() => {
} }
public static values(obj: any) { public static values(obj: any) {
const res: any[] = []; const res: any[] = [];
const keys = object.getOwnMembers(obj, false); const keys = object.getOwnMembers(obj, true);
const symbols = object.getOwnSymbolMembers(obj, false); const symbols = object.getOwnSymbolMembers(obj, true);
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
res[res.length] = obj[keys[i]]; res[res.length] = obj[keys[i]];
@ -157,8 +163,8 @@ export const Object = (() => {
} }
public static entries(obj: any) { public static entries(obj: any) {
const res: [any, any][] = []; const res: [any, any][] = [];
const keys = object.getOwnMembers(obj, false); const keys = object.getOwnMembers(obj, true);
const symbols = object.getOwnSymbolMembers(obj, false); const symbols = object.getOwnSymbolMembers(obj, true);
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
res[res.length] = [keys[i], obj[keys[i]]]; res[res.length] = [keys[i], obj[keys[i]]];
@ -172,12 +178,15 @@ export const Object = (() => {
public static preventExtensions(obj: object) { public static preventExtensions(obj: object) {
object.preventExt(obj); object.preventExt(obj);
return obj;
} }
public static seal(obj: object) { public static seal(obj: object) {
object.seal(obj); object.seal(obj);
return obj;
} }
public static freeze(obj: object) { public static freeze(obj: object) {
object.freeze(obj); object.freeze(obj);
return obj;
} }
} }

View File

@ -5,19 +5,28 @@ function _defineProperties(target, arr) {
for (var i = 0; i < arr.length; i++) { for (var i = 0; i < arr.length; i++) {
var desc = arr[i]; var desc = arr[i];
var res; var res;
var w, e, c;
c = desc.configurable;
if (c == null) c = true;
e = desc.enumerable;
if (e == null) e = false;
if ("value" in desc) { if ("value" in desc) {
res = object.defineField(target, desc.key, { w: desc.writable || true, e: desc.enumerable || true, c: desc.configurable || true, v: desc.value }); w = desc.writable;
if (w == null) w = true;
if (desc.writable == null)
res = object.defineField(target, desc.key, { w: !!w, e: !!e, c: !!c, v: desc.value });
} }
else { else {
res = object.defineProperty(target, desc.key, { e: desc.enumerable || true, c: desc.configurable || true, g: desc.get, s: desc.set }); res = object.defineProperty(target, desc.key, { e: !!e, c: !!c, g: desc.get, s: desc.set });
} }
if (!res) throw "Couldn't set property"; if (!res) throw "Couldn't set property";
} }
} }
/* __#PURE__ */
export default function _createClass(clazz, instance, nonInstance) { export default function _createClass(clazz, instance, nonInstance) {
_defineProperties(clazz.prototype, instance); _defineProperties(clazz.prototype, instance);
_defineProperties(clazz, nonInstance); _defineProperties(clazz, nonInstance);

View File

@ -5,13 +5,17 @@ export interface SymbolPrimordials {
getSymbolDescription(symbol: symbol): string; getSymbolDescription(symbol: symbol): string;
} }
export interface NumberPrimordials { export interface NumberPrimordials {
NaN: number;
Infinity: number;
PI: number;
E: number;
parseInt(raw: string | number, radix?: number): number; parseInt(raw: string | number, radix?: number): number;
parseFloat(raw: string | number): number; parseFloat(raw: string | number): number;
isNaN(num: number): boolean; isNaN(num: number): boolean;
NaN: number;
Infinity: number;
pow(a: number, b: number): number; pow(a: number, b: number): number;
log(val: number): number;
} }
export interface StringPrimordials { export interface StringPrimordials {
stringBuild(parts: string[]): string; stringBuild(parts: string[]): string;
@ -32,12 +36,13 @@ export interface ObjectPrimordials {
getOwnSymbolMembers(obj: object, onlyEnumerable: boolean): symbol[]; getOwnSymbolMembers(obj: object, onlyEnumerable: boolean): symbol[];
getPrototype(obj: object): object | undefined; getPrototype(obj: object): object | undefined;
setPrototype(obj: object, proto?: object): object; setPrototype(obj: object, proto?: object): object;
isArray(obj: any[]): boolean;
preventExt(obj: object): void; preventExt(obj: object): void;
seal(obj: object): void; seal(obj: object): void;
freeze(obj: object): void; freeze(obj: object): void;
isArray(obj: any): obj is any[];
subarray(arr: any[], start: number, end: number): any[];
memcpy(src: any[], dst: any[], srcI: number, dstI: number, n: number): void; memcpy(src: any[], dst: any[], srcI: number, dstI: number, n: number): void;
sort(arr: any[], cb: Function): any[]; sort(arr: any[], cb: Function): any[];
} }

View File

@ -72,6 +72,13 @@ export const String = (() => {
return string.substring(self, 0, endI); return string.substring(self, 0, endI);
} }
public trimLeft() {
return func.invoke(String.prototype.trimStart, this, []);
}
public trimRight() {
return func.invoke(String.prototype.trimEnd, this, []);
}
public charAt(i: number) { public charAt(i: number) {
const self = unwrapThis(this, "string", String, "String.prototype.charAt"); const self = unwrapThis(this, "string", String, "String.prototype.charAt");
return self[i]; return self[i];
@ -133,6 +140,16 @@ export const String = (() => {
return applyReplaces(self, matches, replacer, false); return applyReplaces(self, matches, replacer, false);
} }
public repeat(n: number) {
const self = unwrapThis(this, "string", String, "String.prototype.replaceAll");
const res: string[] = [];
for (let i = 0; i < n; i++) {
res[i] = self;
}
return string.stringBuild(res);
}
public slice(start = 0, end?: number) { public slice(start = 0, end?: number) {
const self = unwrapThis(this, "string", String, "String.prototype.slice"); const self = unwrapThis(this, "string", String, "String.prototype.slice");
@ -143,32 +160,63 @@ export const String = (() => {
if (end <= start) return ""; if (end <= start) return "";
return string.substring(self, start, end); return string.substring(self, start, end);
} }
public substring(this: string, start = 0, end = this.length) { public substring(start = 0, end?: number) {
const self = unwrapThis(this, "string", String, "String.prototype.substring"); const self = unwrapThis(this, "string", String, "String.prototype.substring");
start = limitI(start, this.length); if (end === undefined) end = self.length;
end = limitI(end, this.length); start = limitI(start, self.length);
end = limitI(end, self.length);
if (end <= start) return ""; if (end <= start) return "";
return string.substring(self, start, end); return string.substring(self, start, end);
} }
public substr(this: string, start = 0, count = this.length - start) { public substr(start = 0, count?: number) {
const self = unwrapThis(this, "string", String, "String.prototype.substr"); const self = unwrapThis(this, "string", String, "String.prototype.substr");
start = limitI(start, this.length); count = self.length - start;
count = limitI(count, this.length - start); start = limitI(start, self.length);
count = limitI(count, self.length - start);
if (count <= 0) return ""; if (count <= 0) return "";
return string.substring(self, start, count + start); return string.substring(self, start, count + start);
} }
public concat() {
const self = unwrapThis(this, "string", String, "String.prototype.concat");
const parts = [self];
for (let i = 0; i < arguments.length; i++) {
parts[i + 1] = (String as any)(arguments[i]);
}
public toLowerCase(this: string) { return string.stringBuild(parts);
}
public toLowerCase() {
const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase"); const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase");
return string.lower(self); return string.lower(self);
} }
public toUpperCase(this: string) { public toUpperCase() {
const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase"); const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase");
return string.upper(self); return string.upper(self);
} }
public match(regex: RegExp) {
const self = unwrapThis(this, "string", String, "String.prototype.match");
if (!(regex instanceof RegExp)) throw new TypeError("Regexp expected for String.prototype.match");
if (regex.global) {
let matches: string[] | null = null;
while (true) {
const match = regex.exec(self);
if (match == null) break;
matches ||= [];
matches[matches.length] = match[0];
}
return matches;
}
else return regex.exec(self);
}
public [symbols.iterator]() { public [symbols.iterator]() {
var i = 0; var i = 0;
var arr: string | undefined = unwrapThis(this, "string", String, "String.prototype[Symbol.iterator]"); var arr: string | undefined = unwrapThis(this, "string", String, "String.prototype[Symbol.iterator]");

View File

@ -0,0 +1,4 @@
import coffeescript from "./coffeescript.ts";
import babel from "./babel.ts";
register(v => coffeescript(babel(v)));

View File

@ -0,0 +1,25 @@
import { SourceMap } from "./map.ts";
import { transform } from "@babel/standalone";
// import presetEnv from "@babel/preset-env";
export default function babel(next: Compiler): Compiler {
print("Loaded babel!");
return (filename, code, prevMap) => {
const res = transform(code, {
filename,
sourceMaps: true,
});
const map = SourceMap.parse({
file: "babel-internal://" + filename,
mappings: res.map!.mappings,
sources: [filename],
});
const compiled = next("babel-internal://" + filename, res.code!, SourceMap.chain(map, prevMap));
registerSource(filename, code);
return compiled;
};
}

View File

@ -0,0 +1,28 @@
import { compile } from "coffeescript";
import { SourceMap } from "./map.ts";
export default function coffee(next: Compiler): Compiler {
print("Loaded coffeescript!");
return (filename, code, prevMap) => {
const {
js: result,
v3SourceMap: rawMap,
} = compile(code, {
filename,
sourceMap: true,
bare: true,
});
const map = SourceMap.parse({
file: "coffee-internal://" + filename,
mappings: JSON.parse(rawMap).mappings,
sources: [filename],
});
const compiled = next("coffee-internal://" + filename, result, SourceMap.chain(map, prevMap));
registerSource(filename, code);
return compiled;
};
}

View File

@ -123,13 +123,16 @@ export class VLQSourceMap {
public static parseVLQ(compiled: string, filenames: string[], raw: string): VLQSourceMap { public static parseVLQ(compiled: string, filenames: string[], raw: string): VLQSourceMap {
const mapping = decodeVLQ(raw); const mapping = decodeVLQ(raw);
const res = new Map<string, [start: number, dst: Location][][]>(); const file: [start: number, dst: Location][][] = [];
const res = new Map<string, [start: number, dst: Location][][]>([[compiled, file]]);
let originalRow = 0; let originalRow = 0;
let originalCol = 0; let originalCol = 0;
let originalFile = 0; let originalFile = 0;
const lastCols = new Set<number>();
for (let compiledRow = 0; compiledRow < mapping.length; compiledRow++) { for (let compiledRow = 0; compiledRow < mapping.length; compiledRow++) {
const line = file[compiledRow] ??= [];
let compiledCol = 0; let compiledCol = 0;
for (const rawSeg of mapping[compiledRow]) { for (const rawSeg of mapping[compiledRow]) {
@ -138,12 +141,13 @@ export class VLQSourceMap {
originalRow += rawSeg.length > 2 ? rawSeg[2] : 0; originalRow += rawSeg.length > 2 ? rawSeg[2] : 0;
originalCol += rawSeg.length > 3 ? rawSeg[3] : 0; originalCol += rawSeg.length > 3 ? rawSeg[3] : 0;
let file = res.get(compiled); if (!lastCols.has(compiledCol)) {
if (file == null) res.set(compiled, file = []); line[line.length] = [compiledCol, [filenames[originalFile], originalRow, originalCol]];
}
const line = file[compiledRow] ??= []; lastCols.add(compiledCol);
line[line.length] = [compiledCol, [filenames[originalFile], originalRow, originalCol]];
} }
line.sort((a, b) => a[0] - b[0]);
} }
return new VLQSourceMap(res); return new VLQSourceMap(res);

View File

@ -0,0 +1,10 @@
import { type SourceMap } from "./map.ts";
declare global {
type CompilerFactory = (next: Compiler) => Compiler;
type Compiler = (filename: string, src: string, mapper: SourceMap) => Function;
function print(...args: any[]): void;
function register(factory: CompilerFactory): void;
function registerSource(filename: string, src: string): void;
}

View File

@ -17,7 +17,7 @@ function resource(name: string) {
else return resources[name] = getResource(name); else return resources[name] = getResource(name);
} }
register(next => { export default function typescript(next: Compiler): Compiler {
const files: Record<string, IScriptSnapshot> = {}; const files: Record<string, IScriptSnapshot> = {};
const versions: Record<string, number> = {}; const versions: Record<string, number> = {};
let declI = 0; let declI = 0;
@ -34,6 +34,7 @@ register(next => {
forceConsistentCasingInFileNames: true, forceConsistentCasingInFileNames: true,
declaration: true, declaration: true,
sourceMap: true, sourceMap: true,
downlevelIteration: true,
}; };
let service: LanguageService; let service: LanguageService;
@ -114,7 +115,7 @@ register(next => {
const result = outputs["/src.js"]; const result = outputs["/src.js"];
const declaration = outputs["/src.d.ts"]; const declaration = outputs["/src.d.ts"];
const compiled = next("ts-internal://" + filename, result, SourceMap.chain(prevMap, map)); const compiled = next("ts-internal://" + filename, result, SourceMap.chain(map, prevMap));
registerSource(filename, code); registerSource(filename, code);
return function (this: any) { return function (this: any) {
@ -123,4 +124,4 @@ register(next => {
return res; return res;
}; };
}; };
}); }

View File

@ -30,9 +30,22 @@ public class VariableDeclareNode extends Node {
} }
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
for (var entry : values) { for (var entry : values) {
var index = target.scope.get(entry.var.name, false);
if (entry.value != null) { if (entry.value != null) {
entry.value.compile(target, true); entry.value.compile(target, true);
target.add(VariableNode.toSet(target, loc(), entry.var.name, false, true)).setLocation(loc()); }
if (index == null) {
if (entry.value == null) {
target.add(Instruction.globDef(entry.var.name));
}
else {
target.add(Instruction.globSet(entry.var.name, false, true));
}
}
else if (entry.value != null) {
target.add(index.index().toSet(false));
} }
} }

View File

@ -32,6 +32,7 @@ public class RegexNode extends Node {
var inBrackets = false; var inBrackets = false;
loop: while (true) { loop: while (true) {
if (i + n >= src.size()) break;
switch (src.at(i + n)) { switch (src.at(i + n)) {
case '[': case '[':
inBrackets = true; inBrackets = true;

View File

@ -159,7 +159,9 @@ public class DebugServer {
} }
public void awaitConnection() throws InterruptedException { public void awaitConnection() throws InterruptedException {
connNotifier.wait(); synchronized (connNotifier) {
connNotifier.wait();
}
} }
public void run(InetSocketAddress address) { public void run(InetSocketAddress address) {

View File

@ -1032,24 +1032,29 @@ public class SimpleDebugger implements Debugger {
} }
mappings.put(body, map); mappings.put(body, map);
} }
private boolean instructionLock;
@Override public boolean onInstruction(Environment env, Frame cf, Instruction instruction, Value returnVal, EngineException error, boolean caught) { @Override public boolean onInstruction(Environment env, Frame cf, Instruction instruction, Value returnVal, EngineException error, boolean caught) {
if (!enabled) return false; if (!enabled) return false;
if (instructionLock) return false;
instructionLock = true;
boolean isBreakpointable; try {
Location loc; boolean isBreakpointable;
DebugFrame frame; Location loc;
BreakpointType bptType; DebugFrame frame;
BreakpointType bptType;
synchronized (this) {
frame = getFrame(cf); frame = getFrame(cf);
var map = DebugContext.get(env).getMap(frame.frame.function); var map = DebugContext.get(env).getMap(frame.frame.function);
frame.updateLoc(map.toLocation(frame.frame.codePtr)); frame.updateLoc(map.toLocation(frame.frame.codePtr));
loc = frame.location; loc = frame.location;
bptType = map.getBreakpoint(frame.frame.codePtr); bptType = map.getBreakpoint(frame.frame.codePtr);
isBreakpointable = loc != null && (bptType.shouldStepIn()); isBreakpointable = loc != null && (bptType.shouldStepIn());
if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) { if (error != null && (execptionType == CatchType.ALL || execptionType == CatchType.UNCAUGHT && !caught)) {
pauseException(env, error); pauseException(env, error);
} }
@ -1077,49 +1082,55 @@ public class SimpleDebugger implements Debugger {
instruction.params.length == 1 && instruction.params.length == 1 &&
instruction.get(0).equals("debug") instruction.get(0).equals("debug")
) pauseDebug(env, null); ) pauseDebug(env, null);
}
while (enabled) {
synchronized (this) { synchronized (this) {
switch (state) { }
case PAUSED_EXCEPTION:
case PAUSED_NORMAL: break; while (enabled) {
synchronized (this) {
case STEPPING_OUT: switch (state) {
case RESUMED: return false; case PAUSED_EXCEPTION:
case PAUSED_NORMAL: break;
case STEPPING_IN:
case STEPPING_OVER: case STEPPING_OUT:
if (stepOutFrame.frame == frame.frame) { case RESUMED: return false;
if (returnVal != null || error != null) {
state = State.STEPPING_OUT; case STEPPING_IN:
continue; case STEPPING_OVER:
} if (stepOutFrame.frame == frame.frame) {
else if (stepOutPtr != frame.frame.codePtr) { if (returnVal != null || error != null) {
state = State.STEPPING_OUT;
if (state == State.STEPPING_IN && bptType.shouldStepIn()) { continue;
pauseDebug(env, null);
break;
} }
else if (state == State.STEPPING_OVER && bptType.shouldStepOver()) { else if (stepOutPtr != frame.frame.codePtr) {
pauseDebug(env, null);
break; if (state == State.STEPPING_IN && bptType.shouldStepIn()) {
pauseDebug(env, null);
break;
}
else if (state == State.STEPPING_OVER && bptType.shouldStepOver()) {
pauseDebug(env, null);
break;
}
} }
} }
} return false;
return false; }
} }
try {
synchronized (updateNotifier) {
updateNotifier.wait();
}
}
catch (InterruptedException e) { Thread.currentThread().interrupt(); }
} }
try { return false;
synchronized (updateNotifier) { }
updateNotifier.wait(); finally {
} instructionLock = false;
}
catch (InterruptedException e) { Thread.currentThread().interrupt(); }
} }
return false;
} }
@Override public synchronized void onFramePush(Environment env, Frame frame) { @Override public synchronized void onFramePush(Environment env, Frame frame) {
var prevFrame = currFrame; var prevFrame = currFrame;

View File

@ -32,16 +32,17 @@ public class Arguments {
public Value self() { public Value self() {
return get(-1); return get(-1);
} }
@SuppressWarnings("unchecked")
public <T> T self(Class<T> clazz) { public <T> T self(Class<T> clazz) {
if (self instanceof UserValue user && clazz.isInstance(user.value)) return (T)user.value; return UserValue.unwrap(clazz, self);
else return null;
} }
public Value get(int i) { public Value get(int i) {
if (i >= args.length || i < -1) return Value.UNDEFINED; if (i >= args.length || i < -1) return Value.UNDEFINED;
else if (i == -1) return self; else if (i == -1) return self;
else return args[i]; else return args[i];
} }
public <T> T get(Class<T> clazz, int i) {
return UserValue.unwrap(clazz, get(i));
}
public Value getOrDefault(int i, Value def) { public Value getOrDefault(int i, Value def) {
if (i < -1 || i >= args.length) return def; if (i < -1 || i >= args.length) return def;
else return get(i); else return get(i);

View File

@ -115,7 +115,7 @@ public abstract class FunctionValue extends ObjectValue {
this.length = length; this.length = length;
this.name = name; this.name = name;
prototype.defineOwnField(null, "constructor", this); prototype.defineOwnField(null, "constructor", this, true, false, true);
} }
} }

View File

@ -77,4 +77,10 @@ public final class UserValue<T> extends Value {
public static <T> UserValue<T> of(T value, ObjectValue prototype) { public static <T> UserValue<T> of(T value, ObjectValue prototype) {
return new UserValue<T>(value, prototype); return new UserValue<T>(value, prototype);
} }
@SuppressWarnings("unchecked")
public static <T> T unwrap(Class<T> clazz, Value val) {
if (val instanceof UserValue user && clazz.isInstance(user.value)) return (T)user.value;
else return null;
}
} }